# 6.2.5 re PicoCTF2014 Baleful

* [题目解析](#题目解析)
  * [逆向 VM 求解](#逆向-vm-求解)
  * [使用 Pin 求解](#使用-pin-求解)
* [参考资料](#参考资料)

[下载文件](https://github.com/firmianay/CTF-All-In-One/blob/master/src/writeup/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
```

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

```python
#!/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
```

确实没有，写下脚本：

```python
#!/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
```

简单到想哭。

## 参考资料

* [Pico CTF 2014 : Baleful](https://github.com/ctfs/write-ups-2014/tree/master/pico-ctf-2014/master-challenge/baleful-200)


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://firmianay.gitbook.io/ctf-all-in-one/6_writeup/reverse/6.2.5_re_picoctf2014_baleful.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
