20199324《Linux内核原理与分析》第十二周作业
格式化字符串漏洞实验
一、 实验描述
格式化字符串漏洞是由像 printf(user_input) 这样的代码引起的,其中 user_input 是用户输入的数据,具有 Set-UID root 权限的这类程序在运行的时候,printf 语句将会变得非常危险,因为它可能会导致下面的结果:
- 使得程序崩溃
- 任意一块内存读取数据
- 修改任意一块内存里的数据(这种结果是非常危险的,因为它允许用户修改 set-UID root 程序内部变量的值,从而改变这些程序的行为)
本实验将会提供一个具有格式化漏洞的程序,我们将制定一个计划来探索这些漏洞。
二、 实验内容
2.1 实验一
户需要输入一段数据,数据保存在 user_input 数组中,程序会使用 printf 函数打印数据内容,并且该程序以root权限运行。这个程序存在一个格式化漏洞。让我们来看看利用这些漏洞可以搞些什么破坏。
在/home/shiyanlou/目录下,新建vul_prog.c文件,输入以下代码内容:
/* vul_prog.c */
#include <stdlib.h>
#include <stdio.h>
#define SECRET1 0x44
#define SECRET2 0x55
int main(int argc, char *argv[])
{
char user_input[100];
int *secret;
long int_input;
int a, b, c, d; /* other variables, not used here.*/
/* The secret value is stored on the heap */
secret = (int *) malloc(2*sizeof(int));
/* getting the secret */
secret[0] = SECRET1; secret[1] = SECRET2;
printf("The variable secret's address is 0x%8x (on stack)\n", &secret);
printf("The variable secret's value is 0x%8x (on heap)\n", secret);
printf("secret[0]'s address is 0x%8x (on heap)\n", &secret[0]);
printf("secret[1]'s address is 0x%8x (on heap)\n", &secret[1]);
printf("Please enter a decimal integer\n");
scanf("%d", &int_input); /* getting an input from user */
printf("Please enter a string\n");
scanf("%s", user_input); /* getting a string from user */
/* Vulnerable place */
printf(user_input);
printf("\n");
/* Verify whether your attack is successful */
printf("The original secrets: 0x%x -- 0x%x\n", SECRET1, SECRET2);
printf("The new secrets: 0x%x -- 0x%x\n", secret[0], secret[1]);
return 0;
}
程序说明:
程序内存中存在两个秘密值,我们想要知道这两个值,但发现无法通过读二进制代码的方式来获取它们(实验中为了简单起见,硬编码这些秘密值为0x44和0x55)。尽管我们不知道它们的值,但要得到它们的内存地址倒不是特别困难,因为对大多数系统而言,每次运行程序,这些内存地址基本上是不变的。实验假设我们已经知道了这些内存地址,为了达到这个目的,程序特意为我们打出了这些地址。
有了这些前提以后我们需要达到以下目标:
- 找出 secret[1] 的值
- 修改 secret[1] 的值
- 修改 secret[1] 为期望值
注意:因为实验环境是 64 位系统,所以需要使用 %016llx 才能读取整个字。但为了简便起见,对程序进行了修改了,使用 %08x 也能完成实验。
编译运行:
$ gcc -z execstack -fno-stack-protector -o vul_prog vul_prog.c
$ sudo chmod u+s vul_prog
注:这里会报出一些警告 warning,可以忽略。
2.1.1 找出 secret[1]的值
1.运行 vul_prog 程序去定位 int_input 的位置,这样就确认了 %s 在格式字符串中的位置。

12和后面的 %016llx都是自己随意输入的, %016llx的个数最好在10个以上。
12的十六进制码就是0x000C,可以看到输出中12的十六进制在第8个位置上,这样我们就能确定格式化字符串的位置了。
2.输入 secret[1] 的地址,记得做进制转换,同时在格式字符串中加入 %s

可以看到 secret[1] 的地址是 0x602014 ,转换成十进制就是 6299668。
第八个位置上替换成 %s 就能打印出 secret[1] 的值了。
U 的 ascii 码就是 55。
2.1.2 修改 secret[1]的值
只要求修改,不要求改成什么。可以看到已经变成 0x77 了。

2.1.3 修改 secret[1]为期望值
要改成自己期望的值,比如改成十进制1000,手动填充1000太累了!这里可以用格式化字符填充。

从图上得知,secret[1] 的地址是 0x602014,换算成十进制就是6299668。
我们在第7位用%.897u这种设置精度的方式填充字符,897是怎么来的呢?
在 %.897u 前有 6 个 64 位地址,每个地址对应 16 个字符,加上逗号一共就是 6*(16+1)=102 ,在 %.897u 后面的地址上还有一个 , 占一个字符。所以一共是 102+1=103 个字符,要改成 1000 的话,我们就还差 1000-103=897 个字符。
0x3e8 的 10 进制就是 1000 。
2.2 实验二
现在修改vul_prog.c程序,把第一个scanf语句去掉,并去掉与int_input变量相关的所有语句,程序如下:
/* vul_prog.c */
#include <stdlib.h>
#include <stdio.h>
#define SECRET1 0x44
#define SECRET2 0x55
int main(int argc, char *argv[])
{
char user_input[100];
int *secret;
int a, b, c, d; /* other variables, not used here.*/
/* The secret value is stored on the heap */
secret = (int *) malloc(2*sizeof(int));
/* getting the secret */
secret[0] = SECRET1; secret[1] = SECRET2;
printf("The variable secret's address is 0x%8x (on stack)\n", &secret);
printf("The variable secret's value is 0x%8x (on heap)\n", secret);
printf("secret[0]'s address is 0x%8x (on heap)\n", &secret[0]);
printf("secret[1]'s address is 0x%8x (on heap)\n", &secret[1]);
printf("Please enter a string\n");
scanf("%s", user_input); /* getting a string from user */
/* Vulnerable place */
printf(user_input);
printf("\n");
/* Verify whether your attack is successful */
printf("The original secrets: 0x%x -- 0x%x\n", SECRET1, SECRET2);
printf("The new secrets: 0x%x -- 0x%x\n", secret[0], secret[1]);
return 0;
}
同时在 xfce 终端使用 linux 命令设置关闭地址随机化选项:
sudo sysctl -w kernel.randomize_va_space=0
关闭地址随机化后,这样每次运行程序得到的 secret 地址就都一样了。
在 /home/shiyanlou 目录新建一个程序write_string.c。以下程序将一个格式化字符串写入了一个叫 mystring 的文件,前4个字节由任意你想放入格式化字符串的数字构成:
/* write_string.c */
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main()
{
char buf[1000];
int fp, size;
unsigned int *address;
/* Putting any number you like at the beginning of the format string */
address = (unsigned int *) buf;
*address = 0x113222580;
/* Getting the rest of the format string */
scanf("%s", buf+4);
size = strlen(buf+4) + 4;
printf("The string length is %d\n", size);
/* Writing buf to "mystring" */
fp = open("mystring", O_RDWR | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
if (fp != -1) {
write(fp, buf, size);
close(fp);
} else {
printf("Open failed!\n");
}
}
编译 vul_prog.c 与 write_string.c:
rm vul_prog
gcc -z execstack -fno-stack-protector -o vul_prog vul_prog.c
gcc -o write_string write_string.c
然后通过 write_string 程序将内容输入进 mystring 文件中,文件内容包括代码中加入的头四个字节和你之后输入的内容。
先运行 vul_prog 程序,输入 4 个 %016llx 。再运行 write_string 程序,输入 8 个 %016llx 和 1 个 %n ,此操作会生成一个 mystring 文件。然后输入如下命令:
./vul_prog < mystring

修改成功(0x8c = 140 = 8*16+8 个逗号+开头 4 个字节)。
20199324《Linux内核原理与分析》第十二周作业的更多相关文章
- 2018-2019-1 20189221 《Linux内核原理与分析》第八周作业
2018-2019-1 20189221 <Linux内核原理与分析>第八周作业 实验七 编译链接过程 gcc –e –o hello.cpp hello.c / gcc -x cpp-o ...
- 2018-2019-1 20189221 《Linux内核原理与分析》第七周作业
2018-2019-1 20189221 <Linux内核原理与分析>第七周作业 实验六 分析Linux内核创建一个新进程的过程 代码分析 task_struct: struct task ...
- 2018-2019-1 20189221 《Linux内核原理与分析》第六周作业
2018-2019-1 20189221 <Linux内核原理与分析>第六周作业 实验五 实验过程 将Fork函数移植到Linux的MenuOS fork()函数通过系统调用创建一个与原来 ...
- 2018-2019-1 20189221《Linux内核原理与分析》第五周作业
2018-2019-1 20189221<Linux内核原理与分析>第五周作业 实验四 实验过程 当用户态进程调用一个系统调用时,cpu切换到内核态并开始执行一个内核函数. 在Linux中 ...
- 2018-2019-1 20189221《Linux内核原理与分析》第三周作业
2018-2019-1 20189221<Linux内核原理与分析>第三周作业 实验二 完成一个简单的时间片轮转多道程序内核代码 实验过程 在实验楼中编译内核 编写mymain.c函数和m ...
- 2019-2020-1 20199329《Linux内核原理与分析》第十三周作业
<Linux内核原理与分析>第十三周作业 一.本周内容概述 通过重现缓冲区溢出攻击来理解漏洞 二.本周学习内容 1.实验简介 注意:实验中命令在 xfce 终端中输入,前面有 $ 的内容为 ...
- 2019-2020-1 20199329《Linux内核原理与分析》第十一周作业
<Linux内核原理与分析>第十一周作业 一.本周内容概述: 学习linux安全防护方面的知识 完成实验楼上的<ShellShock 攻击实验> 二.本周学习内容: 1.学习& ...
- 2019-2020-1 20199329《Linux内核原理与分析》第八周作业
<Linux内核原理与分析>第八周作业 一.本周内容概述: 理解编译链接的过程和ELF可执行文件格式 编程练习动态链接库的两种使用方式 使用gdb跟踪分析一个execve系统调用内核处理函 ...
- 2019-2020-1 20199329《Linux内核原理与分析》第七周作业
<Linux内核原理与分析>第七周作业 一.本周内容概述: 对Linux系统如何创建一个新进程进行追踪 分析Linux内核创建一个新进程的过程 二.本周学习内容: 1.学习进程的描述 操作 ...
- 2019-2020-1 20199329《Linux内核原理与分析》第六周作业
<Linux内核原理与分析>第六周作业 一.本周内容概述: 学习系统调用的相关理论知识,并使用库函数API和C代码中嵌入汇编代码两种方式使用getpid()系统调用 学习系统调用syste ...
随机推荐
- 实战 迁移学习 VGG19、ResNet50、InceptionV3 实践 猫狗大战 问题
实战 迁移学习 VGG19.ResNet50.InceptionV3 实践 猫狗大战 问题 参考博客:::https://blog.csdn.net/pengdali/article/detail ...
- kettle将csv文件导入数据库
具体过程学习了: 1.连接数据库 2.添加新资源库 3.选择Other Repositories 4.选择Database Repository,第二个需要配置额外参数 5.连接数据库相关设置 6.连 ...
- idea新建java项目
盘符下新建一个目录: 打开idea: Open -> 新建的目录: 右击目录 -> new -> module: 填写模块名 -> finish: file -> pro ...
- SQL注入类型
本文转自:https://www.cnblogs.com/cui0x01/p/6322826.html Sql注入_类型 1.sql注入 通过把SQL命令插入到Web表单提交或输入域名或页面请求的查询 ...
- SQL约束攻击
本文转载自https://blog.csdn.net/kkr3584/article/details/69223010 目前值得高兴的是,开发者在建立网站时,已经开始关注安全问题了--几乎每个开发者都 ...
- SQL审核平台-Yearning安装部署实践
相关文档: https://guide.yearning.io/ yearning简介 http://python.yearning.io/install/ yearning安装 Yearning ...
- 统计Shell脚本执行时间
统计Shell脚本执行时间,帮助分析改进脚本执行 用 date 相减 #!/bin/bash startTime=`date +%Y%m%d-%H:%M:%S` startTime_s=`date + ...
- VUE中的MVVM模式
1.传统MVP模式:业务逻辑相关的控制层 M:模型层,ajax请求 V:dom层,视图 P:控制器.js代码之类的 2.MVVM MVVM模式主要操作数据层,代码减少量是MVP的30%甚至70%
- cat <<EOF> file
.多行导入文件(新建文件或者覆盖文件内容) cat << EOF > abcd.txt Hello! This is a test file! Test for cat and ...
- PHP 的变量类型,变量检测
1.PHP的变量类型: 整型 浮点型 字符串 布尔型 数组 对象 null 资源类型 一个变量就是一个盒子,类型可以看做盒子的标签,变量的值就是盒子里的内容 null 是没有类型的空盒子, ...