环境搭建

1.关闭地址空间随机化

2.在serve_code下make,把fromat.c编译成32位和64位版本:

命令:

make

make install

在lab4下

dcbuild

dcup

出现attaching to …后,关闭终端

dockps (之后按照手册)

实验过程

Task 1: Crashing the Program

10.9.0.5 运行32bits的漏洞程序

向10.9.0.5发送“hello”

image-20231105211915751

10.9.0.5打印

image-20231105212208305

image-20231105212247021

使程序崩溃

image-20231105212958700

image-20231105212913422

没有return properly

Task 2: Printing Out the Server Program’s Memory

task 2a:打印出input[0:4]

把buffer的前四个字节设为0xaaaaaaaa

image-20231105222146187

尝试使用70个格式化字符,试探va_list到buffer的距离

image-20231105222236261

看到了”aaaaaaaa“是通过第64个%x输出的。所以可以认为va_list到buffer的距离为64*4=256bytes。

image-20231105222406478

task 2b

Task 2.B: Heap Data There is a secret message (a string) stored in the heap area, and you can find the

address of this string from the server printout. Your job is to print out this secret message. To achieve

this goal, you need to place the address (in the binary form) of the secret message in the format string

将buffer中前4个字节改成secret message的地址

image-20231105222815710

通过63个”%x“使va_list移动到buffer前面,用一个”%s“对buffer的前四个字节进行寻址,即找到secret string的地址,并将secret stirng打印出来

image-20231105223043435

Task 3: Modifying the Server Program’s Memory

3a Change the value to a different value

在buffer的前四个字节中写入target的地址

image-20231105224351115

通过63个”%.9x|“将va_list移动到buffer前面,通过”%n“将输出字符的个数写入target地址,即改写了target的值。由于前面输出了8个字符,加上63个”%.9x|“,target的值为638.

image-20231105224210661

输出target的值为0x27e,即十进制的638

image-20231105224809068

3b Change the value to 0x5000

任务:target原为0x11223344,改为0x00005000

在遇到”%n“之前,打印出0x5000个字符即可。十进制为20480个字符,可以通过控制字符的长度来打印。

通过63个”%x“来控制va_list的移动,移动到buffer前面。在buffer[0]-[7]处打印出8个字符,在va_list移动62次时打印出330*62=20460个字符,最后一次移动时,打印出12个字符,共20480个字符,即0x5000.

image-20231105231354144

输出:target=0x5000

image-20231105231454644

3c Change the value to 0xAABBCCDD

由于是小端法存储,所以0x080e5068的位置上是0x44,0x…69的位置上是0x33,0x…6a的位置上是0x22,0x…6b的位置上是0x11.

任务3c即为把0x080e5068的位置上改为0xdd,0x…69的位置上改为0xcc,0x…6a的位置上改为0xbb,0x…6b的位置上改为0xaa.

输出字符个数为0xaabb (43707)时,写入0x080e5070,

输出字符个数为0xccdd (52445)时,写入0x080e5068.

image-20231105234445774

在[0:4]写入aabb的地址,在[8:12]写入ccdd的地址,中间[4:8]用作缓冲。当va_list写完43707个字符后,需要再写8738个字符,写入这8738个字符时,va_list还会再移动一次,从content[4]移动到content[8],这时才去寻址并改写地址0x080e5068所存的值。

下面计算字符串

content[0:12]输出12个字符,12+704*62+47=43707

43707+8738=52445

image-20231105233911715

输出target = aabbccdd

image-20231105234533856

Task 4: Inject Malicious Code into the Server Program

6.1Understanding the Stack Layout

• Question 1: What are the memory addresses at the locations marked by ➋ and ➌?

• Question 2: How many %x format specifiers do we need to move the format string argument pointer to ➌? Remember, the argument pointer starts from the location above ➊

image-20231105234926097

A1:由输出可知:2处的地址0xffffd3bc,3处的地址为0xffffd490

image-20231106083724106

A2:在task2中尝试过,需要63个”%x“

6.3思路

buffer最开始的几个字节用于存放格式化字符串fmt,将shellcode存放在buffer尾部,修改2处的返回地址为shellcode的地址,当从myprintf函数返回时,它将跳转到shellcode并执行。

2处(return address)的地址为0xffffd3bc,3处(buffer)的地址为0xffffd490,shellcode在buffer中的偏移为1364(0x554),所以返回地址应该修改为0xffffd9e4.

过程:

将shellcode 放在尾部

image-20231106081607766

构造badfile,将myprintf的返回地址修改为0xffffd9e4。

先打印出12个字符,再使va_list移动62次,打印出8*62个字符,再移动一次到buffer[0],打印出55272个字符,共打印了55780个字符,即0xd9e4,写入返回地址的低四位。再移动va_list,并打印9755个字符,共打印65535个字符,即0xffff,写入返回地址的高四位。

image-20231106095524047

攻击成功,服务器端显示:

image-20231106095137150

在攻击端监听端口,构造reverse shell

修改shellcode中的指令(注意此处不能改变这一行字符串的长度,因为在shellcode中通过硬编码规定了长度。)

image-20231106091554622

在10.9.0.1上监听9090端口,再次发送cat badfile | nc 10.9.0.5 9090命令,可以看到执行shellcode后,建立了反向shell。通过id查看到此shell有root权限。

image-20231106193154030

Task 5: Attacking the 64-bit Server Program

特别感谢:

Format String Attack Lab - Yuhan’s blog (yuhan2001.github.io)

Task 5 是参考以上博客所作。

先查看内存的结构:

image-20231106195652514

myprintf的返回地址为0x00007fffffffdf08,需要改写这个返回地址使其执行shellcode。

printf函数遇到”00”会认为是字符串的结束标记,从而停止打印字符。在input中出现一次“00”后,printf停止打印。虽然printf函数面对00会停止解析,也就是va_list指针停止移动,但是我们还可以用k$去手动移动va_list指针,依次地址。

在格式字符串中,我们可以使用%x将参数指针va_list移动到下一个可选参数。我们也可以直接将指针移动到第k个可选参数。

这是使用格式字符串的参数字段(以k$的形式)完成的。

下面的代码示例使用%3$.20x打印第三个可选参数的值3(前面填19个0),然后使用%6$n将一个值写入第6个可选参数(变量var,其值将变为20)。最后,使用%2$.10x时,它将指针移回第二个可选参数,并将其打印出来(2,前面填9个0)。

先查看shellcode的大小,有165个字节。

image-20231106200720095

image-20231106200743740

将shellcode放在input的尾部,这并不受到地址中“00”的影响。

试探性地在input[0]处写入0xffffffff,在s中构造70个“%x”并打印出来,发现va_list移动34次后打印出ffffffff,所以需要移动34次到input[0]。

image-20231106211626526

将8字节的return address分割为4个2字节分别修改,值为shellcode的起始地址。由于地址的最高位都是0000,所以这两个字节不需要修改。

因为在64位中shellcode的入口地址是包含0x00的。将地址转换为字节时,00对应的Ascii码为\0。当 printf()解析格式化字符串时,在遇到零(\0)后会停止解析,之后的任何内容都不会被视为格式化字符串的一部分。故myprintf函数返回地址的所在地址的高四字节和低四字节应该放在格式化字符串的后面。

故先用%.numberx修改已打印字符数,然后使用%k$n将指针移动到printf()的第k个参数,这个参数应为要修改的值的地址。

故格式化字符串的构成为:”%.number1x”、”%k1hn”、”%.number2x”、”%k2hn””`hn””%.`number3`x”、”%k3`hn”。

首先根据服务器输出,获得各地址

目标是把myprintf的返回地址修改为shellcode的入口地址。

image-20231106222628609

由于最高四位不需要修改,所以只取出后面三段的值:

image-20231106223239057

计算修改地址所需要的字符个数:

image-20231106222856527

构造格式化字符串:

image-20231106222920976

因为格式化字符串不会太大,且需要考虑va_list的移动是八位对齐的,所以考虑把需要改变值的地址放在buffer偏移量64、72、80的位置,对应的是printf的第42、43、44个参数。

image-20231106223413880

修改shellcode中的命令,建立反向shell

image-20231106223448504

获得root shell

image-20231106222505496

Task 6: Fixing the Problem

gcc的警告:

image-20231106223612706

意思是:字符串格式并不是一个常量,而且没有格式化字符串的参数。

修复漏洞:

format.cprintf(msg)更改为printf("%s",msg),并重新编译,发现没有警告。

image-20231106223814710

image-20231106223838591

重新尝试攻击

重新建立并开启docker,对32位服务器的target值进行攻击。发现target的值并没有改变,故攻击失败。

加油加油!!