章节大纲

  • 注意:对于 Apple Silicon 机器,任务 1-4  已经使用了 64 位服务器程序,所以这个任务与任务 4 相同,不需要重复。但是,你可以在这个部分找到有关 64 位机器的有用信息。

    在前面的任务中,我们的目标服务器是 32 位程序。在这个任务中,我们攻击一个 64 位服务器程序。我们的新目标是 10.9.0.6,它运行 format 程序的 64 位版本。让我们先给这个服务器发送一个 hello 消息。我们将看到目标容器打印出的以下消息。
     
    $ echo hello | nc 10.9.0.6 9090
    Press Ctrl+C
    
    // 容器控制台上的打印输出
    server-10.9.0.6 | Got a connection from 10.9.0.1
    server-10.9.0.6 | Starting format
    server-10.9.0.6 | Input buffer (address):        0x00007fffffffe200
    server-10.9.0.6 | The secret message's address:  0x0000555555556008
    server-10.9.0.6 | The target variable's address: 0x0000555555558010
    server-10.9.0.6 | Input size: 6
    server-10.9.0.6 | Frame Pointer (inside myprintf):      0x00007fffffffe140
    server-10.9.0.6 | The target variable's value (before): 0x1122334455667788
    server-10.9.0.6 | hello
    server-10.9.0.6 | (^_^)(^_^)  Returned from printf()  (^_^)(^_^)
    server-10.9.0.6 | The target variable's value (after):  0x1122334455667788
     
    你可以看到框架指针和缓冲区的地址的值变为 8 字节长(而不是 32 位程序中的 4 字节)。你的工作是构建你的有效载荷来利用服务器的格式化字符串漏洞。你的最终目标是获得目标服务器上的root shell。你需要使用 64 位版本的 shellcode。
      • x64 架构引起的挑战是地址中的零。尽管 x64 架构支持 64 位地址空间,但只允许从 0x00 到 0x00007FFFFFFFFFFFF 的地址。这意味着对于每个地址(8字节),最高的两个字节总是零。这导致了一个问题。
         
        在攻击中,我们需要在格式字符串中放置地址。对于 32 位程序,我们可以将地址放在任何地方,因为里面没有零,但对于 64 位程序我们不能再这样做。如果你把一个地址放在格式字符串的中间,当 printf() 使用这个格式字符串时,当它看到一个零时,就会停止。所以格式字符串中的第一个零之后的任何东西都不会被认为是格式字符串的一部分。
         
        零引起的问题与在缓冲区溢出攻击中不同,在缓冲区溢出攻击中,如果使用了 strcpy(),出现零将终止内存复制。在这里,我们没有程序中的内存复制,所以我们的输入中可以有零,但是放在哪里是关键的。有很多方法可以解决这个问题,在实验报告中,你应该解释他们是如何解决这个问题的。
      • 在格式字符串中,我们可以使用 %x 移动参数指针 va_list 到下一个可选参数。我们也可以直接将指针移动到第 k 个可选参数。这是使用格式字符串的参数字段完成的(形式为 k$)。以下代码示例使用 "%3$.20x" 打印出第三个可选参数的值(数字3),然后使用 "%6$n" 写入第六个可选参数(变量 var,其值将变为 20)。最后,使用 "%2$.10x",它将指针移回到第 2 个可选参数(数字 2),并打印出来。你可以看到,使用这种方法,我们可以自由地前后移动指针。这种技术非常有用,可以简化任务中格式字符串的构建。
         
        #include <stdio.h>
        int main()
        {
            int var = 1000;
            printf("%3$.20x%6$n%2$.10x\n", 1, 2, 3, 4, 5, &var);
            printf("The value in var: %d\n",var);
            return 0;
        }
        ----- Output ------
        seed@ubuntu:$ a.out
        000000000000000000030000000002
        The value in var: 20