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 ...
随机推荐
- 51nod 1475:建设国家 优先队列的好题
1475 建设国家 基准时间限制:1 秒 空间限制:131072 KB 分值: 20 难度:3级算法题 收藏 关注 小C现在想建设一个国家.这个国家中有一个首都,然后有若干个中间站,还有若干个城市 ...
- POJ 1944:Fiber Communications
Fiber Communications Time Limit: 1000MS Memory Limit: 30000K Total Submissions: 4236 Accepted: 1 ...
- mnist数据集tensorflow实现
TensorFlow——CNN实现MNIST手写体识别 2019年04月08日 21:46:19 星空Ice_ 阅读数 83 文章目录 TensorFlowCNN实现MNIST 1,数据集 2,回 ...
- js连续的日期判断,判断相差几天
var startTime=Date.parse(new Date('2020-02-28')); var endTime=Date.parse(new Date('2020-02-29')); $. ...
- mysql自增长字段设置
mysql版本:5.7.27 说明:表中只能设置一个自增长字段[主键.索引(其他普通字段不行))] 设置自增长开始值: ALTER TABLE table_name AUTO_INCREMENT=10 ...
- 一天一个设计模式——Prototype 原型模式
一.模式说明 看了比较多的资料,对原型模式写的比较复杂,个人的理解就是模型复制,根据现有的类来直接创建新的类,而不是调用类的构造函数. 那为什么不直接调用new方法来创建类的实例呢,主要一个原因是如果 ...
- Bootstrap-模态框 modal.js
参考网址:http://v3.bootcss.com/(能抄不写) 1.大模态框 图片效果图: 代码:(button的属性data-target对应的是具体模态框的class) <!-- Lar ...
- webview HttpClient 怎么保持会话session统一
cookies session均为key---value的形式展示, 1. session是存储在服务端,并有一块区域控件存储用户信息,主要是为了判断该用户是否登录,在客户端采用httpC ...
- 工程日记之ChildLost(1):URLSession
URLSession 是什么 URL Loading System提供了访问URL资源的系统,提供了访问http/https/自定义URL访问的接口.其中,URLSession实例可以创建多个URLS ...
- SPOJ 247 chocolate (CHOCLO)
题目http://www.spoj.com/problems/CHOCOLA/ 把一整块巧克力分成一个一个单元,掰断每一横行有个代价值,掰断每个纵行也有个代价值,要你求最后的总代价值最小 这个题目放在 ...