Writeup: Redpwn 2019 - Dedication

Information

  • category: forensics
  • points: 397

Description

Only for the dedicated.

1 file : chall.zip

Writeup

The challenge ZIP file is composed of two files:

  • jjofpbwvgk.png
  • jjofpbwvgk.zip

The PNG file is corrupted and the archive is password-protected.

The PNG file is not a PNG but a simple text file containing a lot of rows similar to this one:

1
(0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (95,95,95) (223,223,223) (255,255,255) (255,255,255) (255,255,255) (255,255,255) (255,255,255) (255,255,255) (255,255,255) (255,255,255) (255,255,255) (255,255,255) (255,255,255) (255,255,255) (159,159,159) (31,31,31) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0) (0,0,0)

The content of the file and the challenge itself suggest that the password of the ZIP file is displayed build an image using the pixels contained in the text file. Each row of the text file is a row of pixels of the final image.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
from PIL import Image
import sys

lang = "eng+spa"

filepixels = sys.argv[1]
outputimage = sys.argv[2]
pixels_list = open(filepixels).read().split("\n")
px = len(pixels_list) - 1
row = eval("[" + pixels_list[0].replace(" ", ",").strip(",") + "]")
py = len(row) - 1
newi = Image.new("RGB", (px, py), "black")
new_pixels = newi.load()


try:
for x in range(0, newi.size[0]):
row = eval("[" + pixels_list[x].replace(" ", ",").strip(",") + "]")
for y in range(0, newi.size[1]):
new_pixels[x, y] = row[y]
except IndexError:
print(x, y)
print(px, py)

newi.save(outputimage)

This script parse the text file to recreate the image using Pillow. Once execute the output is saved in the file fixed.png.

claquers is the password for the ZIP file:

Unfortunately the zip contains another pair of PNG (text) and ZIP file. The process is the same but the got another pair of files… so we automated the process of creation and extraction using an OCR library and a ZSH script.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
from PIL import Image, ImageOps
import sys

# yay -S tesseract tesseract-data-spa tesseract-data-eng
import pytesseract

lang = "eng+spa"


def ocr(filename):
return pytesseract.image_to_string(Image.open(filename), lang=lang)


filepixels = sys.argv[1]
outputimage = sys.argv[2]
pixels_list = open(filepixels).read().split("\n")
px = len(pixels_list) - 1
row = eval("[" + pixels_list[0].replace(" ", ",").strip(",") + "]")
py = len(row) - 1
newi = Image.new("RGB", (px, py), "black")
new_pixels = newi.load()


try:
for x in range(0, newi.size[0]):
row = eval("[" + pixels_list[x].replace(" ", ",").strip(",") + "]")
for y in range(0, newi.size[1]):
new_pixels[x, y] = row[y]
except IndexError:
print(x, y)
print(px, py)


newi.save(outputimage)
print(ocr(outputimage).replace(" ","").lower())

The python script uses Tesseract to read the fixed.png image to get the password for the ZIP.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#!/usr/bin/env zsh
#
# Author: dodo
# redpwnctf-2019: dedication
#

password=$(python prod.py ./*.png ./fixed.png)
unzip -j -P "${password}" *.zip -d dodo0

for n ({0..2000}); do
password=$(python prod.py "./dodo${n}/*.png" "./dodo${n}/fixed.png")
echo ${password}
if unzip -j -P "${password}" "./dodo${n}"/*.zip -d "./dodo$((${n}+1))"; then
continue
else
# Wrong password
xdg-open "./dodo${n}/fixed.png"
tmp=""
vared -p 'Insert the correct password: ' -c tmp
unzip -j -P "${tmp}" "./dodo${n}"/*.zip -d "./dodo$((${n}+1))"
fi
done

The ZSH script should be executed from the folder where the first two file (PNG and ZIP) are located. The script just execute the python script and extract the new pair of file into a folder; if the password is not correct the user is asked to insert it.

After 1000 iterations and a lots of manual insertions we got the final PNG.

Flag

flag{th3s_1s_tru3_d3d1cAt10N!!!}