2017-12-05 18:06:40 +07:00
|
|
|
|
# 6.2.5 re PicoCTF2014 Baleful
|
|
|
|
|
|
|
|
|
|
- [题目解析](#题目解析)
|
2017-12-06 16:19:49 +07:00
|
|
|
|
- [逆向 VM 求解](#逆向-vm-求解)
|
|
|
|
|
- [使用 Pin 求解](#使用-pin-求解)
|
2017-12-05 18:06:40 +07:00
|
|
|
|
- [参考资料](#参考资料)
|
|
|
|
|
|
|
|
|
|
[下载文件](../src/writeup/6.2.5_re_picoctf2014_baleful)
|
|
|
|
|
|
|
|
|
|
## 题目解析
|
2018-08-05 16:43:10 +07:00
|
|
|
|
|
|
|
|
|
```text
|
|
|
|
|
$ file baleful
|
2017-12-05 18:06:40 +07:00
|
|
|
|
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!
|
2017-12-06 16:19:49 +07:00
|
|
|
|
$ upx -d baleful -o baleful_unpack
|
2017-12-05 18:06:40 +07:00
|
|
|
|
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
|
|
|
|
|
-------------------- ------ ----------- -----------
|
2017-12-06 16:19:49 +07:00
|
|
|
|
144956 <- 6752 4.66% linux/i386 baleful_unpack
|
2017-12-05 18:06:40 +07:00
|
|
|
|
|
|
|
|
|
Unpacked 1 file.
|
2017-12-06 16:19:49 +07:00
|
|
|
|
$ 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
|
2017-12-05 18:06:40 +07:00
|
|
|
|
```
|
2018-08-05 16:43:10 +07:00
|
|
|
|
|
2017-12-06 16:19:49 +07:00
|
|
|
|
经过简单地检查,我们发现二进制文件被加了壳,使用 upx 脱掉就好了。
|
|
|
|
|
|
|
|
|
|
运行下看看,典型的密码验证题:
|
2018-08-05 16:43:10 +07:00
|
|
|
|
|
|
|
|
|
```text
|
2017-12-06 16:19:49 +07:00
|
|
|
|
$ ./baleful_unpack
|
2017-12-05 18:06:40 +07:00
|
|
|
|
Please enter your password: ABCD
|
|
|
|
|
Sorry, wrong password!
|
|
|
|
|
```
|
|
|
|
|
|
2018-08-05 16:43:10 +07:00
|
|
|
|
### 逆向 VM 求解
|
|
|
|
|
|
2017-12-06 16:19:49 +07:00
|
|
|
|
打开 r2 开干吧!
|
2018-08-05 16:43:10 +07:00
|
|
|
|
|
|
|
|
|
```text
|
2017-12-06 16:19:49 +07:00
|
|
|
|
[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
|
2018-08-05 16:43:10 +07:00
|
|
|
|
| ; 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
|
2017-12-06 16:19:49 +07:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
`fcn.0804898b` 是程序主要的逻辑所在,很容易看出来它其实是实现了一个虚拟机:
|
|
|
|
|
|
2018-08-05 16:43:10 +07:00
|
|
|
|
### 使用 Pin 求解
|
2017-12-06 16:19:49 +07:00
|
|
|
|
|
|
|
|
|
就像上面那样逆向实在是太难了,不如 Pin 的黑科技。
|
|
|
|
|
|
|
|
|
|
编译 32 位 pintool:
|
2018-08-05 16:43:10 +07:00
|
|
|
|
|
|
|
|
|
```text
|
2017-12-06 16:19:49 +07:00
|
|
|
|
[ManualExamples]$ make obj-ia32/inscount0.so TARGET=
|
|
|
|
|
```
|
2018-08-05 16:43:10 +07:00
|
|
|
|
|
2017-12-06 16:19:49 +07:00
|
|
|
|
随便输入几个长度不同的密码试试:
|
2018-08-05 16:43:10 +07:00
|
|
|
|
|
|
|
|
|
```text
|
|
|
|
|
[ManualExamples]$ echo "A" | ../../../pin -t obj-ia32/inscount0.so -o inscount.out -- ~/baleful_unpack ; cat inscount.out
|
2017-12-06 16:19:49 +07:00
|
|
|
|
Please enter your password: Sorry, wrong password!
|
|
|
|
|
Count 437603
|
2018-08-05 16:43:10 +07:00
|
|
|
|
[ManualExamples]$ echo "AA" | ../../../pin -t obj-ia32/inscount0.so -o inscount.out -- ~/baleful_unpack ; cat inscount.out
|
2017-12-06 16:19:49 +07:00
|
|
|
|
Please enter your password: Sorry, wrong password!
|
|
|
|
|
Count 438397
|
2018-08-05 16:43:10 +07:00
|
|
|
|
[ManualExamples]$ echo "AAA" | ../../../pin -t obj-ia32/inscount0.so -o inscount.out -- ~/baleful_unpack ; cat inscount.out
|
2017-12-06 16:19:49 +07:00
|
|
|
|
Please enter your password: Sorry, wrong password!
|
|
|
|
|
Count 439191
|
|
|
|
|
```
|
2018-08-05 16:43:10 +07:00
|
|
|
|
|
|
|
|
|
```text
|
2017-12-06 16:19:49 +07:00
|
|
|
|
$ python -c 'print(439191 - 438397)'
|
|
|
|
|
794
|
|
|
|
|
$ python -c 'print(438397 - 437603)'
|
|
|
|
|
794
|
|
|
|
|
```
|
2018-08-05 16:43:10 +07:00
|
|
|
|
|
2017-12-06 16:19:49 +07:00
|
|
|
|
指令执行的次数呈递增趋势,完美,这样只要递增到这个次数有不同时,就可以得到正确的密码长度:
|
2018-08-05 16:43:10 +07:00
|
|
|
|
|
2017-12-06 16:19:49 +07:00
|
|
|
|
```python
|
2018-08-05 16:43:10 +07:00
|
|
|
|
#!/usr/bin/env python
|
|
|
|
|
|
2017-12-06 16:19:49 +07:00
|
|
|
|
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))
|
|
|
|
|
```
|
2018-08-05 16:43:10 +07:00
|
|
|
|
|
|
|
|
|
```text
|
2017-12-06 16:19:49 +07:00
|
|
|
|
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
|
|
|
|
|
```
|
2018-08-05 16:43:10 +07:00
|
|
|
|
|
2017-12-06 16:19:49 +07:00
|
|
|
|
好,密码长度为 30,接下来是逐字符爆破,首先要确定字符不同对 count 没有影响:
|
2018-08-05 16:43:10 +07:00
|
|
|
|
|
|
|
|
|
```text
|
|
|
|
|
[ManualExamples]$ echo "A" | ../../../pin -t obj-ia32/inscount0.so -o inscount.out -- ~/baleful_unpack ; cat inscount.out
|
2017-12-06 16:19:49 +07:00
|
|
|
|
Please enter your password: Sorry, wrong password!
|
|
|
|
|
Count 437603
|
2018-08-05 16:43:10 +07:00
|
|
|
|
[ManualExamples]$ echo "b" | ../../../pin -t obj-ia32/inscount0.so -o inscount.out -- ~/baleful_unpack ; cat inscount.out
|
2017-12-06 16:19:49 +07:00
|
|
|
|
Please enter your password: Sorry, wrong password!
|
|
|
|
|
Count 437603
|
2018-08-05 16:43:10 +07:00
|
|
|
|
[ManualExamples]$ echo "_" | ../../../pin -t obj-ia32/inscount0.so -o inscount.out -- ~/baleful_unpack ; cat inscount.out
|
2017-12-06 16:19:49 +07:00
|
|
|
|
Please enter your password: Sorry, wrong password!
|
|
|
|
|
Count 437603
|
|
|
|
|
```
|
2018-08-05 16:43:10 +07:00
|
|
|
|
|
2017-12-06 16:19:49 +07:00
|
|
|
|
确实没有,写下脚本:
|
2018-08-05 16:43:10 +07:00
|
|
|
|
|
2017-12-06 16:19:49 +07:00
|
|
|
|
```python
|
2018-08-05 16:43:10 +07:00
|
|
|
|
#!/usr/bin/env python
|
|
|
|
|
|
2017-12-06 16:19:49 +07:00
|
|
|
|
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))
|
|
|
|
|
```
|
2018-08-05 16:43:10 +07:00
|
|
|
|
|
|
|
|
|
```text
|
2017-12-06 16:19:49 +07:00
|
|
|
|
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
|
|
|
|
|
```
|
|
|
|
|
|
2018-08-05 16:43:10 +07:00
|
|
|
|
简单到想哭。
|
2017-12-06 16:19:49 +07:00
|
|
|
|
|
2017-12-05 18:06:40 +07:00
|
|
|
|
## 参考资料
|
2018-08-05 16:43:10 +07:00
|
|
|
|
|
2017-12-06 16:19:49 +07:00
|
|
|
|
- [Pico CTF 2014 : Baleful](https://github.com/ctfs/write-ups-2014/tree/master/pico-ctf-2014/master-challenge/baleful-200)
|