This is the writeup for ievel 1 challenge in Pan Labyrenth CTF - Unix track.

Download the original challenge file

The given file is an obfuscated perl script. There are chunks of base64 strings which are decoded and appended to $a and then at last a huge chunk 0f base64 string is decoded and then eval-ed. Decoding the string to be evaluated, we can find more eval statements in it.

The first step is to remove the first layer of eval obfuscation. So, keep replacing eval statements with the original code until there are no more eval statements left in the source code. Did this by the following script

squash_eval.py

#!/usr/bin/python3
from subprocess import check_output
from pprint import pprint

PERL_DECODER = """
use MIME::Base64();
print {};
"""

lines = ''

def get_eval_line(lines, start, end):
    for i in range(start, end + 1):
        try:
            if lines[i].strip().startswith('eval'):
                print(i)
                return i
        except IndexError:
            print(lines)
            exit()
    raise KeyError("Not fucking found!")

def deobfuscate_code(lines, line_n):
    line = lines[line_n].strip()
    assert line.startswith('eval')
    code = check_output(['perl'], input=str.encode(PERL_DECODER.format(line[5:])))
    #code = check_output(["ls", '-l'])
    code = list(map(bytes.decode, code.split(b"\n")))
    lines = lines[:line_n] + code +lines[line_n+1:]


    return lines, [line_n, line_n + len(code)]

def main():
    count = 0

    with open('bowie.pl', 'r') as fp:
        lines = fp.read().split("\n")

    line_n = get_eval_line(lines, 0, len(lines) - 1)
    while True:
        try:
            print("Deobfuscation round {}".format(count))
            count += 1

            lines, pos = deobfuscate_code(lines, line_n)
            line_n = get_eval_line(lines, pos[0], pos[1])
        except KeyError:
            break

    with open('output.pl', 'w') as fp:
        fp.write('\n'.join(lines))

    print("Done")

if __name__ == '__main__':
    main()

Running this script, we get

189
Deobfuscation round 0
194
Deobfuscation round 1
199
Deobfuscation round 2
204
Deobfuscation round 3
209
Deobfuscation round 4
214
Deobfuscation round 5
219
Deobfuscation round 6
224
Deobfuscation round 7
229
Deobfuscation round 8
234
Deobfuscation round 9
239
Deobfuscation round 10
244
Deobfuscation round 11
249
Deobfuscation round 12
254
Deobfuscation round 13
259
Deobfuscation round 14
264
Deobfuscation round 15
269
Deobfuscation round 16
274
Deobfuscation round 17
279
Deobfuscation round 18
284
Deobfuscation round 19
Done

We have output.pl script which consists of no eval statements. We can see that the script is writing all the contents of $a to a file at the end.

open(my $fh, ">", "entrevue.gif");
print $fh $a;
close $fh;

Now, we just need to remove all those unnecessary if statements and just append everything to $a. Stripping off these checks can be done by a simple script.

cat > reduced.pl << EOF
use MIME::Base64();
my \$a = "";
EOF
grep -o "\$a = \$a . MIME::Base64::decode.*" output.pl >> reduced.pl
cat >> reduced.pl << EOF
open(my \$fh, ">", "entrevue.gif");
print \$fh \$a;
close \$fh;
EOF

reduced.pl is the patched script, without any checks and will just write to entrevue.gif base64 decoding all the chunks.

Opening the gif file, we get our flag as

PAN{L3ts_533_h0W_U_deal_w_th1s_little_511CE}

flag-image