CTF-All-In-One/doc/6.2.5_re_picoctf2014_baleful.md
2018-08-05 17:43:10 +08:00

223 lines
7.0 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 6.2.5 re PicoCTF2014 Baleful
- [题目解析](#题目解析)
- [逆向 VM 求解](#逆向-vm-求解)
- [使用 Pin 求解](#使用-pin-求解)
- [参考资料](#参考资料)
[下载文件](../src/writeup/6.2.5_re_picoctf2014_baleful)
## 题目解析
```text
$ 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 脱掉就好了。
运行下看看,典型的密码验证题:
```text
$ ./baleful_unpack
Please enter your password: ABCD
Sorry, wrong password!
```
### 逆向 VM 求解
打开 r2 开干吧!
```text
[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
```text
[ManualExamples]$ make obj-ia32/inscount0.so TARGET=
```
随便输入几个长度不同的密码试试:
```text
[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
```
```text
$ 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))
```
```text
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 没有影响:
```text
[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))
```
```text
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)