乱七八糟

来自:djh

这题出的比较随意, 也非常简单, 大概给大家讲一讲解题思路吧. 也没啥好深究的.

首先, 拿到一个不知道是啥的文件, 先 binwalk 一下:

⚡ root@DDB ~/Desktop/total_mess > binwalk output.bin

DECIMAL       HEXADECIMAL     DESCRIPTION
--------------------------------------------------------------------------------
0             0x0             Zlib compressed data, best compression

乍一看, 觉得就是一个zlib文件了.

⚡ root@DDB ~/Desktop/total_mess > printf "\x1f\x8b\x08\x00\x00\x00\x00\x00" |cat - output.bin |gzip -dc

# Hint: First you need to determine the programming language we use in this script.
eval unpack u=>q{_=7-E($U)344Z.D)A<V4V-#L*=7-E($-R>7!T.CI-;V1E.CI#0D,["G5S92!$:6=E<W0Z.E-(03L*=7-E($-O_;7!R97-S.CI:<W1D.PIU<V4@0V]M<')E<W,Z.EIL:6(["G5S92!)3SHZ1FEL93L*=7-E($9I;&4Z.E-L=7)P_.PH*<W5B(&)A<V4V-&X@>PH);7D@*"1F;&%G+"`D=&EM97,I(#T@0%\["@EF;W(H*#$N+B1T:6UE<RDI>PH)_"21F;&%G(#T@96YC;V1E7V)A<V4V-"@D9FQA9RP@)R<I.PH)?0H)<F5T=7)N("1F;&%G.PI]"@IM>2`D9FQA_9R`](')E861?9FEL92@G9FQA9RYT>'0G*3L*;7D@)'1H:7,@/2!R96%D7V9I;&4H)#`I.PHD9FQA9R`](&)A_<V4V-&XH8F%S938T;BAB87-E-C1N*"1F;&%G+"`X*2P@."DL(#@I.PHD=&AI<R`]($-O;7!R97-S.CI:;&EB_.CIC;VUP<F5S<R@D=&AI<RP@."D["@IM>2`D>G-T9"`@("`@(#T@<W5B<W1R*$-O;7!R97-S.CI:<W1D.CIC_;VUP<F5S<RAB87-E-C1N*"1F;&%G+"`X*2P@."DL(#@L("TX*3L*;7D@)'IL:6(@("`@("`]('-U8G-T<BA#_;VUP<F5S<SHZ6FQI8CHZ8V]M<')E<W,H8F%S938T;B@D9FQA9RP@."DL(#@I+"`X+"`M."D["FUY("1K97D@_("`@("`@/2!S=6)S='(H1&EG97-T.CI32$$Z.G-H83(U-B@D>G-T9"`]?B!Y+R\O8RDL(#@L("TX*3L*;7D@_)&EV("`@("`@("`]('-U8G-T<BA$:6=E<W0Z.E-(03HZ<VAA,C4V*"1Z;&EB(#U^('DO8R\O*2P@."P@+3@I_.PIM>2`D8VEP:&5R("`@(#T@0W)Y<'0Z.DUO9&4Z.D-"0RT^;F5W*"=!15,G*3L*;7D@)&5N8W)Y<'1E9"`]_("1C:7!H97(M/F5N8W)Y<'0H)&9L86<L("1K97DL("1I=BD["@IM>2`D;W5T<'5T(#T@24\Z.D9I;&4M/FYE_=R@G;W5T<'5T+F)I;B<L("<^)RD["B1O=71P=70M/F)I;FUO9&4["B1O=71P=70M/G!R:6YT*"1T:&ES*3L*_)&]U='!U="T^<')I;G0H)'IS=&0I.PHD;W5T<'5T+3YP<FEN="@D96YC<GEP=&5D*3L*)&]U='!U="T^<')I*;G0H)'IL:6(I.P}

gzip: stdin: invalid compressed data--crc error
gzip: stdin: invalid compressed data--length error

emmmm 解压出来的东西很小, 远够不上 275KB 的大小. 看来后面还有东西.

先看看解压出来的东西. 题目提醒我们这可能不是最常见的语言... 如果没有经验, 可以把不熟悉的语言都拿来试一试. 有经验的话很容易看出来. 或者, 如果能看出来后面的编码是 uuencode 的话, 直接 Google 一下 "unpack uuencode" 就能发现这是 perl 语言.

eval 改成 print, 可以得到混淆前的代码:

use MIME::Base64;
use Crypt::Mode::CBC;
use Digest::SHA;
use Compress::Zstd;
use Compress::Zlib;
use IO::File;
use File::Slurp;

sub base64n {
    my ($flag, $times) = @_;
    for((1..$times)){
        $flag = encode_base64($flag, '');
    }
    return $flag;
}

my $flag = read_file('flag.txt');
my $this = read_file($0);
$flag = base64n(base64n(base64n($flag, 8), 8), 8);
$this = Compress::Zlib::compress($this, 8);

my $zstd      = substr(Compress::Zstd::compress(base64n($flag, 8), 8), 8, -8);
my $zlib      = substr(Compress::Zlib::compress(base64n($flag, 8), 8), 8, -8);
my $key       = substr(Digest::SHA::sha256($zstd =~ y///c), 8, -8);
my $iv        = substr(Digest::SHA::sha256($zlib =~ y/c//), 8, -8);
my $cipher    = Crypt::Mode::CBC->new('AES');
my $encrypted = $cipher->encrypt($flag, $key, $iv);

my $output = IO::File->new('output.bin', '>');
$output->binmode;
$output->print($this);
$output->print($zstd);
$output->print($encrypted);
$output->print($zlib);

代码非常清晰, 并没有再做什么处理. 没有学过 Perl 的同学也可以基本猜出来代码的意思. 除了两行 $zstd =~ y///c$zlib =~ y/c// 以外. 这两行代码的含义可以通过 Google 和实验的方法解决, 一个是$zstd 的长度, 另一个是$zlibc 的个数.

整理一下, 大概就是这个 output.bin 中存放着四个东西:

  • 代码本身的 zlib
  • flag 经 Base64 编码 32 次再 zstd 压缩, 再去掉首尾的数据
  • flag 经 Base64 编码 24 次再用 $zstd 的长度和 $zlibc 的个数来加密的结果
  • flag 经 Base64 编码 32 次再 zlib 压缩, 再去掉首尾的数据

两种考虑:

  • 如果能还原压缩数据的格式, 直接解压即可
  • 如果不能, 把压缩数据当作纯未知量, 我们要找出 $flag 加密后的位置

这里我们走第二条路. 因为它比较简单且显然.

首先估计 flag 长度, 很好办, 自己新建 flag.txt, 内容随便填, 然后运行这个脚本, 看生成 output.bin 文件的大小, 并和原文件比较. 这样可以把 flag 长度限定到一个小范围. 应该注意到这个范围内的 flag 对应的 $flag 的长度, 是精确相等的. 这样我们就拿到了 $flag 的长度.

然后估计 $zstd 的长度. 如果还按照上述方法的话, 误差会比较大. 穷举时间可能会比较久. 我们这个时候选择计算 output.bin 文件的熵. 因为考虑到压缩文件往往有自己的结构, 而密码学安全的加密算法得到的熵绝对是非常高的.

使用 binvis 打开, 将 curve 选择为 scan, 可以看到熵图中有很清楚的分界线. 部分是因为 zstd 压缩格式的内部结构. 然后我们切换到 byteclass 模式, 可以注意到 zstd 部分的数据里面 0x00 的数目非常多. 根据这个特点, 我们能够将 $flag 的起始位置精确估计一下, 比如 0x1d930 - 0x1d970.

根据 $flag 的起始位置, 我们能够得到 $zstd 的长度和 $zlibc 的个数. 并且注意到, 任何一个字符串, 在经过很多次 Base64 编码之后, 其开头都会收敛到常量 "Vm0wd2QyUXlVWGxWV0d4V1YwZDRWMVl3WkRSV01WbDNXa1JTVjAxV2JETlhhMUpU...", 利用这个特性, 我们可以计算我们估计的起始位置是否正确.

穷举的空间很小, 耗时也不多. 写好脚本一跑就出来了. 没做出来的同学可以再试试.

results matching ""

    No results matching ""