6.2.5 re PicoCTF2014 Baleful

下载文件

题目解析

$ file baleful
baleful: ELF 32-bit LSB executable, Intel 80386, version 1 (GNU/Linux), statically linked, stripped
$ strings baleful | grep -i upx
@UPX!
$Info: This file is packed with the UPX executable packer http://upx.sf.net $
$Id: UPX 3.91 Copyright (C) 1996-2013 the UPX Team. All Rights Reserved. $
UPX!u
UPX!
UPX!
$ upx -d baleful -o baleful_unpack
                       Ultimate Packer for eXecutables
                          Copyright (C) 1996 - 2017
UPX 3.94        Markus Oberhumer, Laszlo Molnar & John Reiser   May 12th 2017

        File size         Ratio      Format      Name
   --------------------   ------   -----------   -----------
    144956 <-      6752    4.66%   linux/i386    baleful_unpack

Unpacked 1 file.
$ file baleful_unpack
baleful_unpack: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, for GNU/Linux 2.6.24, BuildID[sha1]=35d1a373cbe6a675ecbbc904722a86f853f20ce3, stripped

经过简单地检查,我们发现二进制文件被加了壳,使用 upx 脱掉就好了。

运行下看看,典型的密码验证题:

$ ./baleful_unpack
Please enter your password: ABCD
Sorry, wrong password!

逆向 VM 求解

打开 r2 开干吧!

[0x08048540]> pdf @ main
/ (fcn) main 96
|   main ();
|           ; var int local_8h @ ebp-0x8
|           ; var int local_10h @ esp+0x10
|           ; var int local_8ch @ esp+0x8c
|           ; DATA XREF from entry0 (0x8048557)
|           0x08049c82      push ebp
|           0x08049c83      mov ebp, esp
|           0x08049c85      push edi
|           0x08049c86      push ebx
|           0x08049c87      and esp, 0xfffffff0
|           0x08049c8a      sub esp, 0x90
|           0x08049c90      mov eax, dword gs:[0x14]                   ; [0x14:4]=-1 ; 20
|           0x08049c96      mov dword [local_8ch], eax
|           0x08049c9d      xor eax, eax
|           0x08049c9f      lea eax, [local_10h]                       ; 0x10 ; 16
|           0x08049ca3      mov ebx, eax
|           0x08049ca5      mov eax, 0
|           0x08049caa      mov edx, 0x1f                              ; 31
|           0x08049caf      mov edi, ebx
|           0x08049cb1      mov ecx, edx
|           0x08049cb3      rep stosd dword es:[edi], eax
|           0x08049cb5      lea eax, [local_10h]                       ; 0x10 ; 16
|           0x08049cb9      mov dword [esp], eax
|           0x08049cbc      call fcn.0804898b
|           0x08049cc1      mov eax, 0
|           0x08049cc6      mov edx, dword [local_8ch]                 ; [0x8c:4]=-1 ; 140
|           0x08049ccd      xor edx, dword gs:[0x14]
|       ,=< 0x08049cd4      je 0x8049cdb
|       |   0x08049cd6      call sym.imp.__stack_chk_fail              ; void __stack_chk_fail(void)
|       |   ; CODE XREF from main (0x8049cd4)
|       `-> 0x08049cdb      lea esp, [local_8h]
|           0x08049cde      pop ebx
|           0x08049cdf      pop edi
|           0x08049ce0      pop ebp
\           0x08049ce1      ret

fcn.0804898b 是程序主要的逻辑所在,很容易看出来它其实是实现了一个虚拟机:

使用 Pin 求解

就像上面那样逆向实在是太难了,不如 Pin 的黑科技。

编译 32 位 pintool:

[ManualExamples]$ make obj-ia32/inscount0.so TARGET=

随便输入几个长度不同的密码试试:

[ManualExamples]$ echo "A" | ../../../pin -t obj-ia32/inscount0.so -o inscount.out -- ~/baleful_unpack ; cat inscount.out
Please enter your password: Sorry, wrong password!
Count 437603
[ManualExamples]$ echo "AA" | ../../../pin -t obj-ia32/inscount0.so -o inscount.out -- ~/baleful_unpack ; cat inscount.out
Please enter your password: Sorry, wrong password!
Count 438397
[ManualExamples]$ echo "AAA" | ../../../pin -t obj-ia32/inscount0.so -o inscount.out -- ~/baleful_unpack ; cat inscount.out
Please enter your password: Sorry, wrong password!
Count 439191
$ python -c 'print(439191 - 438397)'
794
$ python -c 'print(438397 - 437603)'
794

指令执行的次数呈递增趋势,完美,这样只要递增到这个次数有不同时,就可以得到正确的密码长度:

#!/usr/bin/env python

import os

def get_count(flag):
    cmd = "echo " + "\"" + flag + "\"" + " | ../../../pin -t obj-ia32/inscount0.so -o inscount.out -- ~/baleful_unpack"
    os.system(cmd)
    with open("inscount.out") as f:
        count = int(f.read().split(" ")[1])
    return count

flag = "A"
p_count = get_count(flag)
for i in range(50):
    flag += "A"
    count = get_count(flag)
    print("count: ", count)
    diff = count - p_count
    print("diff: ", diff)
    if diff != 794:
        break
    p_count = count
print("length of password: ", len(flag))
Please enter your password: Sorry, wrong password!
count:  459041
diff:  794
Please enter your password: Sorry, wrong password!
count:  459835
diff:  794
Please enter your password: Sorry, wrong password!
count:  508273
diff:  48438
length of password:  30

好,密码长度为 30,接下来是逐字符爆破,首先要确定字符不同对 count 没有影响:

[ManualExamples]$ echo "A" | ../../../pin -t obj-ia32/inscount0.so -o inscount.out -- ~/baleful_unpack ; cat inscount.out
Please enter your password: Sorry, wrong password!
Count 437603
[ManualExamples]$ echo "b" | ../../../pin -t obj-ia32/inscount0.so -o inscount.out -- ~/baleful_unpack ; cat inscount.out
Please enter your password: Sorry, wrong password!
Count 437603
[ManualExamples]$ echo "_" | ../../../pin -t obj-ia32/inscount0.so -o inscount.out -- ~/baleful_unpack ; cat inscount.out
Please enter your password: Sorry, wrong password!
Count 437603

确实没有,写下脚本:

#!/usr/bin/env python

import os

def get_count(flag):
    cmd = "echo " + "\"" + flag + "\"" + " | ../../../pin -t obj-ia32/inscount0.so -o inscount.out -- ~/baleful_unpack"
    os.system(cmd)
    with open("inscount.out") as f:
        count = int(f.read().split(" ")[1])
    return count

charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-+*'"

flag = list("A" * 30)
p_count = get_count("".join(flag))
for i in range(30):
    for c in charset:
        flag[i] = c
        print("".join(flag))
        count = get_count("".join(flag))
        print("count: ", count)
        if count != p_count:
            break
    p_count = count
print("password: ", "".join(flag))
packers_and_vms_and_xors_oh_mx
Please enter your password: Sorry, wrong password!
count:  507925
packers_and_vms_and_xors_oh_my
Please enter your password: Congratulations!
count:  505068
password:  packers_and_vms_and_xors_oh_my

简单到想哭。

参考资料

Last updated