{"id":8255,"date":"2025-11-07T04:04:09","date_gmt":"2025-11-07T04:04:09","guid":{"rendered":"https:\/\/serisec.com\/index.php\/2025\/11\/07\/32454\/"},"modified":"2025-11-07T04:04:09","modified_gmt":"2025-11-07T04:04:09","slug":"32454","status":"publish","type":"post","link":"https:\/\/serisec.com\/index.php\/2025\/11\/07\/32454\/","title":{"rendered":"Binary Breadcrumbs: Correlating Malware Samples with Honeypot Logs Using PowerShell [Guest Diary], (Wed, Nov 5th)"},"content":{"rendered":"\n<div>Binary Breadcrumbs: Correlating Malware Samples with Honeypot Logs Using PowerShell [Guest Diary], (Wed, Nov 5th)<\/div>\n<p> \t<BR><br \/>\n<BR><\/BR><br \/>\n    <!-- no image --><br \/>\n \t<BR><br \/>\n<BR><\/BR><\/p>\n<div>\n<p>[This is a Guest Diary by David Hammond, an ISC intern as part of the SANS.edu<a href=\"http:\/\/https\/\/www.sans.edu\/cyber-security-programs\/bachelors-degree\/\"> BACS<\/a> program]<\/p>\n<p>My last college credit on my way to earning a bachelor&#8217;s degree was an internship opportunity at the Internet Storm Center. A great opportunity, but one that required the care and feeding of a honeypot. The day it arrived I plugged the freshly imaged honeypot into my home router and happily went about my day. I didn\u2019t think too much about it until the first attack observation was due. You see, I travel often, but my honeypot does not. Furthermore, the administrative side of the honeypot was only accessible through the internal network. I wasn\u2019t about to implement a whole remote solution just to get access while on the road. Instead, I followed some very good advice. I started downloading regular backups of the honeypot logs on a Windows laptop I frequently had with me.<\/p>\n<p>The internship program encouraged us to at least initially review our honeypot logs with command line utilities, such as jq and all its flexibility with filtering. Combined with other standard Unix-like operating system tools, such as wc (word count), less, head, and cut, it was possible to extract exactly what I was looking for. I initially tried using more graphical tools but found I enjoy &#8220;living&#8221; in the command line better. When I first start looking at logs, I was not always sure of what I\u2019m looking for. Command line tools allow me to quickly look for outliers in the data. I can see what sticks out by negating everything that looks the same.\u00a0<\/p>\n<p>So, what\u2019s the trouble? None of these tools were available on my Windows laptop. Admittedly, most of what I mention above are available for Windows, but my ability to install software was restricted on this machine, and I knew that native alternatives existed. At the time I had several directories of JSON logs, and a long list of malware hash values corresponding to an attack I was interested in understanding better. Here\u2019s how a few lines of PowerShell can transform scattered honeypot logs into a clear picture of what really happened.<\/p>\n<p>First, let\u2019s start with the script in two parts. Here\u2019s the PowerShell array containing malware hash values:<\/p>\n<p><span style=\"font-family:Lucida Sans Unicode,Lucida Grande,sans-serif;\">$hashes = @(<br \/>\n&#8220;00deea7003eef2f30f2c84d1497a42c1f375d802ddd17bde455d5fde2a63631f&#8221;,<br \/>\n&#8220;0131d2f87f9bc151fb2701a570585ed4db636440392a357a54b8b29f2ba842da&#8221;,<br \/>\n&#8220;01ba4719c80b6fe911b091a7c05124b64eeece964e09c058ef8f9805daca546b&#8221;,<br \/>\n&#8220;0291de841b47fe19557c2c999ae131cd571eb61782a109b9ef5b4a4944b6e76d&#8221;,<br \/>\n&#8220;02a95dae81a8dd3545ce370f8c0ee300beff428b959bd7cec2b35e8d1cd7024e&#8221;,<br \/>\n&#8220;062ba629c7b2b914b289c8da0573c179fe86f2cb1f70a31f9a1400d563c3042a&#8221;,<br \/>\n&#8220;0be1c3511c67ecb8421d0be951e858bb3169ac598d068bae3bc8451e883946cc&#8221;,<br \/>\n&#8220;0cbd5117413a0cab8b22f567b5ec5ec63c81b2f0f58e8e87146ecf7aace2ec71&#8221;,<br \/>\n&#8220;0d2d316bc4937a2608e8d9a3030a545973e739805c3449b466984b27598fcdec&#8221;,<br \/>\n&#8220;0d58ee0cd46d5908f31ba415f2e006c1bb0215b0ecdc27dd2b3afa74799e17bd&#8221;<br \/>\n)<\/span><\/p>\n<p>The $hashes = @( ) between quoted, comma-separated values, establishes a PowerShell array of strings which represents the hashes we want to search for. Now let\u2019s look at how we put this array to use.<\/p>\n<p><span style=\"font-family:Lucida Sans Unicode,Lucida Grande,sans-serif;\">Get-ChildItem -Path &#8220;C:UsersDaveLogs&#8221; -Filter &#8216;cowrie.json.*&#8217; -Recurse |<br \/>\nForEach-Object {<br \/>\n\u00a0 \u00a0 $jsonContent = Get-Content $_.FullName<br \/>\n\u00a0 \u00a0 write-output $_.FullName<br \/>\n\u00a0 \u00a0 foreach ($hash in $hashes) {<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 $searchResults = $null<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 $searchResults = $jsonContent | Select-String $hash<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 if (![string]::IsNullOrEmpty($searchResults)) {\u00a0<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 write-output $searchResults\u00a0<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 }<br \/>\n\u00a0 \u00a0 }<br \/>\n}<\/span><\/p>\n<p>Let&#8217;s walk through the execution of the script. The first statement, <span style=\"font-family:Courier New,Courier,monospace;\">Get-ChildItem<\/span>, recurses every folder in the specified path (<span style=\"font-family:Courier New,Courier,monospace;\">C:UsersDaveLogs<\/span>) and passes along all filenames that match the filter argument. Each filename is passed through the &#8220;pipe&#8221; (|) directly into the first ForEach-Object statement. You can see what\u2019s passed by observing the output of the <span style=\"font-family:Courier New,Courier,monospace;\">write-output $_.FullName<\/span> line. The <span style=\"font-family:Courier New,Courier,monospace;\">$_<\/span> is a variable designation which represents whatever is passed through the pipe. In this case, we know what kind of data to expect (a filename) so we can access it\u2019s attribute, &#8220;FullName&#8221;. This tells us the specific JSON log file currently being searched.<\/p>\n<p>Now let\u2019s get into the meat of the script. The main body of the script contains two nested For-Loops. The outer loop begins with the first &#8220;<span style=\"font-family:Courier New,Courier,monospace;\">ForEach-Object<\/span>&#8221; block of code. The inner loop is described by the lowercase &#8220;foreach&#8221; block. We already know the name of the JSON log we\u2019ll be searching next, so the next line, <span style=\"font-family:Courier New,Courier,monospace;\">$jsonContent = Get-Content $_.FullName<\/span> sets that up to happen. It takes the content of the first filename passed to <strong>$_<\/strong> though the pipe, reads the contents of that filename, and stores the text in a variable named <span style=\"font-family:Courier New,Courier,monospace;\">$jsonContent<\/span>. Now we\u2019ve got our first log to search, all we have to do is run through the list of hash values to search for! This takes us to the point of the script where we reach the inner-loop. The foreach inner-loop is similar to the outer loop with the exception of how it processes data. The statement, <span style=\"font-family:Courier New,Courier,monospace;\">foreach ($hash in $hashes)<\/span> takes each hash value found in the <span style=\"font-family:Courier New,Courier,monospace;\">$hashes<\/span> array and puts a copy of it into $hash before executing the code block it contains.\u00a0<\/p>\n<p>When the inner-loop runs it does three things. First, <span style=\"font-family:Courier New,Courier,monospace;\">$searchResults = $null<\/span> empties the value of the $searchResults variable. This is also called &#8220;initializing&#8221; the variable, and it\u2019s a good practice whenever you&#8217;re working with loops that re-use the same variable names. Second, with the variable clear and ready to accept new values, the next line accomplishes a few things.<\/p>\n<p>\u00a0 \u00a0 \u00a0 \u00a0<span style=\"font-family:Lucida Sans Unicode,Lucida Grande,sans-serif;\"> $searchResults = $jsonContent | Select-String $hash<\/span><\/p>\n<p>Starting to the right of the equals sign, we\u2019re passing the JSON log text<span style=\"font-family:Courier New,Courier,monospace;\"> $jsonContent<\/span> into the command &#8220;<span style=\"font-family:Courier New,Courier,monospace;\">Select-String<\/span>&#8221; while also passing <span style=\"font-family:Courier New,Courier,monospace;\">Select-String<\/span> a single argument, <span style=\"font-family:Courier New,Courier,monospace;\">$hash<\/span>.\u00a0 Remember earlier when the lowercase foreach loop started, it takes each value found in the $hashes array and (one at a time) places their values into $hash before executing the block of code below it. So we\u2019re passing the text in <span style=\"font-family:Courier New,Courier,monospace;\">$jsonContent<\/span> through another pipe to <span style=\"font-family:Courier New,Courier,monospace;\">Select-String<\/span>, which takes that text and searches for the value <span style=\"font-family:Courier New,Courier,monospace;\">$hash<\/span> within the contents of <span style=\"font-family:Courier New,Courier,monospace;\">$jsonContent<\/span>. The results of <span style=\"font-family:Courier New,Courier,monospace;\">Search-String<\/span> are then stored in the variable named <span style=\"font-family:Courier New,Courier,monospace;\">$searchResults<\/span>.<\/p>\n<p>\u00a0 \u00a0 \u00a0 \u00a0<span style=\"font-family:Lucida Sans Unicode,Lucida Grande,sans-serif;\"> if (![string]::IsNullOrEmpty($searchResults)) {\u00a0<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 write-output $searchResults\u00a0<br \/>\n\u00a0 \u00a0 \u00a0 \u00a0 }<\/span><\/p>\n<p>Third and finally, we have an if statement to determine whether the prior <span style=\"font-family:Courier New,Courier,monospace;\">Select-String<\/span> produced any results. If it found the $hash value it was looking for, the <span style=\"font-family:Courier New,Courier,monospace;\">$searchResults<\/span> variable will contain data. If not, it will remain empty (<span style=\"font-family:Courier New,Courier,monospace;\">$null<\/span>). The if statement makes that determination and prints the $searchResults it found. Note the ! at the beginning of the statement which tells it to evaluate as, &#8220;if not empty.&#8221;<\/p>\n<p><img data-recalc-dims=\"1\" decoding=\"async\" alt=\"\" src=\"https:\/\/i0.wp.com\/isc.sans.edu\/diaryimages\/images\/David_Hammond_pic1.png?ssl=1\" style=\"width: 624px; height: 351px;\"><\/p>\n<p>While compact in size, this script introduces the PowerShell newcomer to a variety of useful functions: traversing files and folders, retrieving text, searching text, and nested loops are all sophisticated techniques. If you save this script, you can adapt it in many ways whenever a quick solution is needed. Understanding the tools that are available to us in any environment and having practice adapting those tools to our circumstances makes us all better cybersecurity professionals.<\/p>\n<p>[1] https:\/\/www.sans.edu\/cyber-security-programs\/bachelors-degree\/<\/p>\n<p>&#8212;&#8212;&#8212;&#8211;<br \/>\nGuy Bruneau <a href=\"http:\/\/www.ipss.ca\/\">IPSS Inc.<\/a><br \/>\n<a href=\"https:\/\/github.com\/bruneaug\/\">My GitHub Page<\/a><br \/>\nTwitter: <a href=\"https:\/\/twitter.com\/guybruneau\">GuyBruneau<\/a><br \/>\ngbruneau at isc dot sans dot edu<\/p>\n<p> (c) SANS Internet Storm Center. https:\/\/isc.sans.edu Creative Commons Attribution-Noncommercial 3.0 United States License.<\/p><\/div>\n<p> \t<BR><br \/>\n <BR><\/BR><\/p>\n<p> \t<BR><br \/>\n<BR><\/BR><br \/>\n<a href=\"https:\/\/isc.sans.edu\/diary\/rss\/32454\">Go to isc.sans.edu<\/a><br \/>\n \t<BR><br \/>\n <BR><\/BR><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Binary Breadcrumbs: Correlating Malware Samples with Honeypot Logs Using PowerShell [Guest Diary], (Wed, Nov 5th) [This is a Guest Diary by David Hammond, an ISC intern as part of the SANS.edu BACS program] My last college credit on my way to earning a bachelor&#8217;s degree was an internship opportunity at the Internet Storm Center. A [&hellip;]<\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[56],"tags":[69],"class_list":["post-8255","post","type-post","status-publish","format-standard","hentry","category-isc-sans-edu","tag-isc-sans-edu"],"_links":{"self":[{"href":"https:\/\/serisec.com\/index.php\/wp-json\/wp\/v2\/posts\/8255"}],"collection":[{"href":"https:\/\/serisec.com\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/serisec.com\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/serisec.com\/index.php\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/serisec.com\/index.php\/wp-json\/wp\/v2\/comments?post=8255"}],"version-history":[{"count":0,"href":"https:\/\/serisec.com\/index.php\/wp-json\/wp\/v2\/posts\/8255\/revisions"}],"wp:attachment":[{"href":"https:\/\/serisec.com\/index.php\/wp-json\/wp\/v2\/media?parent=8255"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/serisec.com\/index.php\/wp-json\/wp\/v2\/categories?post=8255"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/serisec.com\/index.php\/wp-json\/wp\/v2\/tags?post=8255"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}