第一次 Hack

这学期开始学汇编,除了嵌入式之外,还在信息安全方面有大用,本次实验通过 radare2 (没钱买ida-pro)进行汇编代码查看,调试工具为gdb

样例

样例在此下载
运行第一个样例,可以看到要求输入密码:

1
2
3
$ ./crackme0x00
IOLI Crackme Level 0x00
Password:

当然是hack掉了

调用堆栈模型

在汇编中 call 一个函数时发生了什么呢

  1. 用约定好的方式,如将参数放入指定寄存器,如eaxebx,或者通过 push 指令压入栈
  2. 将call下一条指令的地址(eip)压入栈作为返回地址压栈
  3. 保存调用者的栈帧(ebp)压栈
  4. 向下修改esp指针为当前函数留出放置临时变量的空间

返回时就反着做一边

StackOverflow

众所周知堆栈是向下生长的,而输入是往高地址输入的,看起来没问题,但是一旦输入过头就会覆盖上面的数据,比如保存的栈帧甚至是返回的地址,这就给了我们hack的机会,让程序跑飞到我们希望的地方

样例反汇编程序


可以看到在 0x8048456 处调用了scanf,可以尝试构造一定长度的字符串输入,直到覆盖栈里的返回地址。

尝试构造字符串

这里使用这个博主的方法:radare2逆向笔记,使用shell脚本试探出字符串的长度

1
2
3
4
5
6
7
8
#!/bin/bash
for((i=1;i<=50;i=i+1)); do
input=$(cat /dev/urandom | tr -dc '0-9a-z' | head -c $i)
echo $input | ./crackme0x00
if [ $? -ne 0 ]; then
echo $i cracked the program with $input
fi
done

运行结果如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
IOLI Crackme Level 0x00
Password: Invalid Password!

......

Password: Invalid Password!
IOLI Crackme Level 0x00
Password: Invalid Password!
IOLI Crackme Level 0x00
Password: Invalid Password!
IOLI Crackme Level 0x00
Password: Invalid Password!
./test.sh:行 8: 29128 已完成 echo $input
29129 段错误 (核心已转储)| ./crackme0x00
28 cracked the program with 9fp95h7p17dv0jey1wy5hl89hstd
IOLI Crackme Level 0x00
Password: Invalid Password!
./test.sh:行 8: 29141 已完成 echo $input
29142 段错误 (核心已转储)| ./crackme0x00
29 cracked the program with to55sai6xa1gvg3ularlj6pbl9die
IOLI Crackme Level 0x00
Password: Invalid Password!
./test.sh:行 8: 29155 已完成 echo $input
29156 段错误 (核心已转储)| ./crackme0x00
30 cracked the program with zjbz0n0sx5oqpoyzqyewxe2uah69z6
IOLI Crackme Level 0x00
Password: Invalid Password!
./test.sh:行 8: 29169 已完成 echo $input
29170 段错误 (核心已转储)| ./crackme0x00
31 cracked the program with r43qq2zbjt2b50jm8fngir0qnfve6gs

......

可以看到从长度为28的字符串开始造成了缓冲区溢出
python产生一个长度为28的字符串,并输入程序,这里直接在gdb中操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
$ python -c "print('A'*28)" > test.in
$ gdb ./crackme0x00
GNU gdb (GDB) 10.1
Copyright (C) 2020 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "x86_64-pc-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<https://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.

For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from ./crackme0x00...
(No debugging symbols found in ./crackme0x00)
(gdb) run < test.in

运行run < test.in之后gdb有了如下输出

1
2
3
4
5
6
7
(gdb) run < test.in
Starting program: /run/media/chipen/data/Pro_TEST/Pro_TEST_HACK/bin-linux/crackme0x00 < test.in
IOLI Crackme Level 0x00
Password: Invalid Password!

Program received signal SIGSEGV, Segmentation fault.
0xf7db9a02 in __libc_start_main () from /usr/lib32/libc.so.6

可以看到程序在段错误之后暂停了,用 i 指令查看寄存器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
(gdb) i registers
eax 0x2400 9216
ecx 0x0 0
edx 0x0 0
ebx 0x0 0
esp 0xffffcd40 0xffffcd40
ebp 0x41414141 0x41414141
esi 0x1 1
edi 0x8048360 134513504
eip 0xf7db9a02 0xf7db9a02 <__libc_start_main+226>
eflags 0x10286 [ PF SF IF RF ]
cs 0x23 35
ss 0x2b 43
ds 0x2b 43
es 0x2b 43
fs 0x0 0
gs 0x63 99

可以看到程序返回时,ebp被改为了0x41414141,而 0x41 正是A 的十六进制值
按照之前的内容,在往上加字符串就会覆盖掉eip,这里使用长为32的字符串:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
(gdb) i registers
eax 0x0 0
ecx 0x0 0
edx 0x0 0
ebx 0x0 0
esp 0xffffcd40 0xffffcd40
ebp 0x41414141 0x41414141
esi 0x1 1
edi 0x8048360 134513504
eip 0x41414141 0x41414141
eflags 0x10286 [ PF SF IF RF ]
cs 0x23 35
ss 0x2b 43
ds 0x2b 43
es 0x2b 43
fs 0x0 0
gs 0x63 99

只要将多出来的4个字符修改为想要的地址就行了,通过汇编可以看到,我们的目标地址是0x08048480
C语言构造字符串:
1
2
3
4
5
6
7
8
#include <stdio.h>

int main()
{
char a[] = "AAAAAAAAAAAAAAAAAAAAAAAAAAAA\x080\x84\x04\x08\0";
printf("%s",a);
return 0;
}

运行

1
2
3
4
5
6
7
8
$ gcc test.c
$ ./a.out > test.out
$ cat test.out | ./crackme0x00
IOLI Crackme Level 0x00
Password: Invalid Password!
Password OK :)
[1] 31352 done cat test.out |
31353 segmentation fault (core dumped) ./crackme0x00