Brute Forcing Simple Archive Passwords

Published: 2023-06-05
Last Updated: 2023-06-05 12:49:26 UTC
by Johannes Ullrich (Version: 1)
0 comment(s)

[Guest dairy submitted by Gebhard]

Occasionally, malicious files are distributed by email in a password-protected attachment to restrain email security gateways from analyzing the included files. The password can normally be found in the email itself and is pretty simple: it should only hinder analysis and not the lazy (but curious) user from opening the attachment.

But what if the email containing the password is lost? For example, the encrypted file attachment could have been saved some time ago to disk (not detected as being malicious at that time) but be detected afterward, e.g., by a full scan or IoCs. So now you have an encrypted archive file, and you're pretty damn sure it's malicious (e.g., VT tells you so - but doesn't provide any details).

Because the bad guys don't want to overburden the user, the password is short (3-4 characters) and only contains characters and numbers. So this is a rare case where it can make sense to just brute-force the password of the archive in a reasonable time. 


I've looked around but haven't found any tool which can be used out of the box. So I used some ideas ([1], [2]) and created a script that should run on Kali 2023 out of the box:


#!/bin/bash

VERSION="v0.2"

# based on: 

# - https://synacl.wordpress.com/2012/08/18/decrypting-a-zip-using-john-the-ripper/
# - https://gist.github.com/bcoles/421cc413d07cd9ba7855
# modified for bruteforcing 

# use for malicious file attachments with short passwords
# tested on kali 2023
# 2023-05-27 v0.1 gebhard
# 2023-05-28 v0.2 gebhard
# Todos:
# - make password method configurable
# - speed up / distribute brute forcing

LINE="#############################################################################"

echo "Archive Password Bruteforce Script ${VERSION}"
echo "---------------------------------------"

# check parameters
if [ $# -lt 1 ]; then
   echo "Usage $0 <archive-file> [<min-length:3> <max-length:6>]"
   exit 1
fi

ZIP=${1}

# crunch configuration
# default values for password length: min=3, max=6
MINLENGTH=${2-3}
MAXLENGTH=${3-6}
# crunch charset config
CHARFILE="/usr/share/crunch/charset.lst"
CHARSET="mixalpha-numeric"

if [ ! -r ${ZIP} ] ; then
   echo "Archive file \"${ZIP}\" not found."
   exit 2
fi
if [ ! -r ${CHARFILE} ] ; then
   echo "Charset file \"${CHARFILE}\" not found."
   exit 2
fi

echo "Parameters"
echo "----------"
echo "File      : ${ZIP}"
echo "Min-Length: ${MINLENGTH}"
echo "Max-Length: ${MAXLENGTH}"
echo "Charfile  : ${CHARFILE}"
echo "Charset   : ${CHARSET}"
echo ${LINE}

# counter for sign of life
COUNT=0
# every xxx guesses: display sign of life
SOL=1000
# counter for total guesses
GUESS=0

echo "Archive content"
echo "---------------"
7z l ${ZIP}

# check if 7z found the file to be OK
if [ ${?} -ne 0 ] ; then
   echo ${LINE}
   echo "7z reported an error. Archive file corrupt?"
   exit 3
fi

echo ${LINE}
echo "Continue: ENTER, Abort: <CTRL+C>"
read lala

echo ${LINE}

echo "Start: `date`"

# note: stdout of crunch is passed to a subshell, so passing variables back is not that easy
crunch ${MINLENGTH} ${MAXLENGTH} -f ${CHARFILE} ${CHARSET} |
   while IFS= read -r PASS
   do
      # count total guesses
      ((GUESS=GUESS+1))
      # every $SOL passwords: display a sign of life
      ((COUNT=COUNT+1))
      if [ ${COUNT} -eq ${SOL} -o ${COUNT} -eq 0 ] ; then
         COUNT=0
         echo -ne "\rCurrent password (guess: ${GUESS}): \"${PASS}\" "
      fi
      # try to extract
      7z t -p${PASS} ${ZIP} >/dev/null 2>&1 

      # check exit code of 7z
      if [ ${?} -eq 0 ]; then
         # 7z returns 0, so password has been found 

         echo ""
         echo ${LINE}
         echo "Script finished (${GUESS} guesses)."
         echo "Archive password is: \"${PASS}\""
         echo "End: `date`"
         # return from subshell with exit status 99 so that main process knows pwd has been found
         exit 99
      fi
   done

# if exit code from subshell is not 99 then pwd has not been found
if [ ${?} -ne 99 ] ; then
   echo ""
   echo ${LINE}
   echo "Script finished. No password found."
   echo "End: `date`"
   exit -1
fi

exit 0

I used a slightly earlier version of the script, and it was able to get the password for this example
https://www.virustotal.com/gui/file/dc374b6eeae0a555796f2a6811997fda6e1a6b293a2c63e1c7254ac61c990c5b
in about 12 hours on a reasonably fast VM using 12,131,410 attempts. 


Here's the output:
???(kali?kali)-[~/analysis/]
??$ ./pw-brute.sh file.zip 

ZIP Password Bruteforce Script
------------------------------
Parameters
----------
File: file.zip
Min-Length: 3
Max-Length: 6
Charfile:   /usr/share/crunch/charset.lst
Charset:    mixalpha-numeric
#############################################################################
Archive content
---------------

7-Zip [64] 16.02 : Copyright (c) 1999-2016 Igor Pavlov : 2016-05-21
p7zip Version 16.02 (locale=en_US.UTF-8,Utf16=on,HugeFiles=on,64 bits,128 CPUs Intel(R) Xeon(R) E-2104G CPU @ 3.20GHz (906EA),ASM,AES-NI)

Scanning the drive for archives:
1 file, 648326 bytes (634 KiB)

Listing archive: file.zip

--
Path = file.zip
Type = zip
Physical Size = 648326

   Date      Time    Attr         Size   Compressed  Name
------------------- ----- ------------ ------------  ------------------------
2022-10-10 08:27:19 ....A      1525760       648148  New_documents#3893.iso
------------------- ----- ------------ ------------  ------------------------
2022-10-10 08:27:19            1525760       648148  1 files
#############################################################################
Continue: ENTER, Abort: <CTRL+C>

#############################################################################
Start: Sat May 27 04:02:00 PM EDT 2023
Crunch will now generate the following amount of data: 403173281072 bytes
384496 MB
375 GB
0 TB
0 PB
Crunch will now generate the following number of lines: 57731383080 

Current password (guess: 12131000): "X3Zr" 

##########################################################################
Script finished (12131410 guesses).
Archive password is: "X353"
End: Sun May 28 04:37:39 AM EDT 2023

The script is pretty basic:
- get the archive file name and, optionally the min and max password length from the user
- do a basic check on the archive (make sure 7z can access the content)
- use "crunch" to loop through a list of mix alpha-numeric passwords which fit between the length borders
- for every password: try to extract the archive (without actually writing the files to disk)
- if found: exit the loop

Handling the password-found vs. password-not-found case has to work despite the actual check running in a subshell. So we're using an exit code to signal the main process if the password was found (99) or not.

If this was helpful, feel free to issue a comment with details. 

---
This guest diary was submitted by Gebhard.


 
Keywords: archives password zip
0 comment(s)
ISC Stormcast For Monday, June 5th, 2023 https://isc.sans.edu/podcastdetail/8524

Comments

What's this all about ..?
password reveal .
<a hreaf="https://technolytical.com/">the social network</a> is described as follows because they respect your privacy and keep your data secure:

<a hreaf="https://technolytical.com/">the social network</a> is described as follows because they respect your privacy and keep your data secure. The social networks are not interested in collecting data about you. They don't care about what you're doing, or what you like. They don't want to know who you talk to, or where you go.

<a hreaf="https://technolytical.com/">the social network</a> is not interested in collecting data about you. They don't care about what you're doing, or what you like. They don't want to know who you talk to, or where you go. The social networks only collect the minimum amount of information required for the service that they provide. Your personal information is kept private, and is never shared with other companies without your permission
https://thehomestore.com.pk/
<a hreaf="https://defineprogramming.com/the-public-bathroom-near-me-find-nearest-public-toilet/"> public bathroom near me</a>
<a hreaf="https://defineprogramming.com/the-public-bathroom-near-me-find-nearest-public-toilet/"> nearest public toilet to me</a>
<a hreaf="https://defineprogramming.com/the-public-bathroom-near-me-find-nearest-public-toilet/"> public bathroom near me</a>
<a hreaf="https://defineprogramming.com/the-public-bathroom-near-me-find-nearest-public-toilet/"> public bathroom near me</a>
<a hreaf="https://defineprogramming.com/the-public-bathroom-near-me-find-nearest-public-toilet/"> nearest public toilet to me</a>
<a hreaf="https://defineprogramming.com/the-public-bathroom-near-me-find-nearest-public-toilet/"> public bathroom near me</a>
https://defineprogramming.com/
https://defineprogramming.com/
Enter comment here... a fake TeamViewer page, and that page led to a different type of malware. This week's infection involved a downloaded JavaScript (.js) file that led to Microsoft Installer packages (.msi files) containing other script that used free or open source programs.
distribute malware. Even if the URL listed on the ad shows a legitimate website, subsequent ad traffic can easily lead to a fake page. Different types of malware are distributed in this manner. I've seen IcedID (Bokbot), Gozi/ISFB, and various information stealers distributed through fake software websites that were provided through Google ad traffic. I submitted malicious files from this example to VirusTotal and found a low rate of detection, with some files not showing as malware at all. Additionally, domains associated with this infection frequently change. That might make it hard to detect.
https://clickercounter.org/
Enter corthrthmment here...

Diary Archives