返回介绍

6.2.5 re PicoCTF2014 Baleful

发布于 2022-02-28 21:36:25 字数 9159 浏览 903 评论 0 收藏 0

  • 参考资料
  • 下载文件

    题目解析

    $ 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
    

    简单到想哭。

    参考资料

    如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

    扫码二维码加入Web技术交流群

    发布评论

    需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
    列表为空,暂无数据
      我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
      原文