本地网站404错误,wordpress新浪jquery,月夜在线观看直播视频,电子工程师自学视频pwn学习笔记#xff08;5#xff09;–格式化字符串漏洞
前言#xff1a;由于条件有限#xff0c;因此对于该漏洞的学习不算很多#xff0c;
格式化字符串漏洞基础#xff1a;
格式化字符串介绍#xff1a;
格式化字符串函数可以接收可变数量的参数#xff0…pwn学习笔记5–格式化字符串漏洞
前言由于条件有限因此对于该漏洞的学习不算很多
格式化字符串漏洞基础
格式化字符串介绍
格式化字符串函数可以接收可变数量的参数并将第一个参数作为格式化字符串根据其来解析之后的参数格式化字符串的利用一般分为三个部分 格式化字符串函数格式化字符串[后续参数] 格式化字符串函数
常见的格式化字符串有 输入 scanf() 输出 函数基本介绍printf输出到 stdoutfprintf输出到指定 FILE 流vprintf根据参数列表格式化输出到 stdoutvfprintf根据参数列表格式化输出到指定 FILE 流sprintf输出到字符串snprintf输出指定字节数到字符串vsprintf根据参数列表格式化输出到字符串vsnprintf根据参数列表格式化输出指定字节到字符串setproctitle设置 argvsyslog输出日志 格式化字符串的格式
%[parameter][flags][field width][.precision][length]type 其中需要注意的有parameter参数以及type参数 parameter n$获取格式化字符串中的指定参数 type d/i有符号整数u无符号整数x/X16 进制 unsigned int 。x 使用小写字母X 使用大写字母。如果指定了精度则输出的数字不足时在左侧补 0。默认精度为 1。精度为 0 且值为 0则输出为空。o8 进制 unsigned int 。如果指定了精度则输出的数字不足时在左侧补 0。默认精度为 1。精度为 0 且值为 0则输出为空。s如果没有用 l 标志输出 null 结尾字符串直到精度规定的上限如果没有指定精度则输出所有字节。如果用了 l 标志则对应函数参数指向 wchar_t 型的数组输出时把每个宽字符转化为多字节字符相当于调用 wcrtomb 函数。c如果没有用 l 标志把 int 参数转为 unsigned char 型输出如果用了 l 标志把 wint_t 参数转为包含两个元素的 wchart_t 数组其中第一个元素包含要输出的字符第二个元素为 null 宽字符。p void * 型输出对应变量的值。printf(“%p”,a) 用地址的格式打印变量 a 的值printf(“%p”, a) 打印变量 a 所在的地址。n不输出字符但是把已经成功输出的字符个数写入对应的整型指针参数所指的变量。% %字面值不接受任何 flags, width。 下面使用几个案例来说明下几个需要注意的参数 parameter n$ 表示获取后面参数列表中的第几个参数。 比如如下代码
#includestdio.h
int main(){printf(%2$d\n,1,2,3);return 0;
} 通过编译运行之后得到的值却是
rootg01den-virtual-machine:/home/g01den/Temp# ./a
2 好了我们稍微修改下刚刚的代码
#includestdio.h
int main(){printf(%d%d%d\n,1,2,3);return 0;
} 然后编译为32位之后查看下汇编代码
0000119d main:119d: 8d 4c 24 04 lea 0x4(%esp),%ecx11a1: 83 e4 f0 and $0xfffffff0,%esp11a4: ff 71 fc push -0x4(%ecx)11a7: 55 push %ebp11a8: 89 e5 mov %esp,%ebp11aa: 53 push %ebx11ab: 51 push %ecx11ac: e8 2b 00 00 00 call 11dc __x86.get_pc_thunk.ax11b1: 05 27 2e 00 00 add $0x2e27,%eax11b6: 6a 03 push $0x3 -------------------------------11b8: 6a 02 push $0x2 -------------------------------11ba: 6a 01 push $0x1 -------------------------------11bc: 8d 90 30 e0 ff ff lea -0x1fd0(%eax),%edx11c2: 52 push %edx11c3: 89 c3 mov %eax,%ebx11c5: e8 86 fe ff ff call 1050 printfplt11ca: 83 c4 10 add $0x10,%esp11cd: b8 00 00 00 00 mov $0x0,%eax11d2: 8d 65 f8 lea -0x8(%ebp),%esp11d5: 59 pop %ecx11d6: 5b pop %ebx11d7: 5d pop %ebp11d8: 8d 61 fc lea -0x4(%ecx),%esp11db: c3 ret 能够发现在我指的那三行在调用printf函数之前是先将参数从右向左压入栈中之后通过读取栈的参数与格式化字符串一起打印出来。
格式化字符串漏洞
有了前面的那些储备知识这个时候就可以来看看格式化字符串的漏洞了。
初探
首先写一个程序如下内容
#includestdio.h
int main(){printf(%x %x %x %x \n,0x1);return 0;
} 那么直接进行动态调试观看main的栈帧中相关的信息
00:0000│ esp 0xffffd500 —▸ 0x56557008 ◂— %x %x %x %x \n
01:0004│-014 0xffffd504 ◂— 0x1 --------------------
02:0008│-010 0xffffd508 —▸ 0xf7fbeb20 —▸ 0xf7c1acc6 ◂— GLIBC_PRIVATE --------------------
03:000c│-00c 0xffffd50c —▸ 0x565561b1 (main20) ◂— add eax, 0x2e27 --------------------
04:0010│-008 0xffffd510 —▸ 0xffffd530 ◂— 0x1 --------------------
05:0014│-004 0xffffd514 —▸ 0xf7e2a000 (_GLOBAL_OFFSET_TABLE_) ◂— 0x229dac
06:0018│ ebp 0xffffd518 —▸ 0xf7ffd020 (_rtld_global) —▸ 0xf7ffda40 —▸ 0x56555000 ◂— 0x464c457f 然后运行到printf函数之后再看看输出的值是多少
pwndbg n
1 f7fbeb20 565561b1 ffffd530 发现输出的值和箭头指出来的值一样。因此黑客则可以利用此漏洞配合**%n$d**来进行内存的读取了。
一次简单的漏洞的实例
实际上格式化字符串的漏洞很多时候都是由于程序员们偷懒才会导致一些漏洞的产生比如下面的程序如果写成这个样子
#includestdio.h
int main(){char str[100];scanf(%s\n,str);printf(%s\n,str);return 0;
} 如果是这样的话程序运行就不会出现太大的问题但是当程序员偷懒使用下面的写法就会出现问题
#includestdio.h
int main(){char str[100];scanf(%s,str);printf(str);return 0;
} 运行的话就会出现如下的结果
rootg01den-virtual-machine:/home/g01den/Temp# ./a
aaa.%x
aaa.fff53a28 根据gdb调试就会发现它输出了比esp地址高4字节的那一地址的数据。
泄露内存
还是刚才那个程序其实可以在这个时候多输入几个%x来进行父函数栈帧的esp高4字节的地址开始的多个字节的内存或者通过n$来进行任意内存的读取
aaa.fff53a28rootg01den-virtual-machine:/home/g01den/Temp# ./a
%x.%x.%x.%x.%x.%x.%x
ff8631d8.0.565a31d4.0.0.252e7825.78252e78rootg01den-virtual-machine:/home/g01den/Temp# ./a
%3$x
565cf1d4泄露任意地址的内存
上一个方法只是泄露了栈上的数据其实格式化字符串可以对任意内存地址的数据进行泄露。、
攻击者可以使用类似于%s的格式规范做到泄露栈中存放的值对应的地址的字符串的内容这里引用下dalao的对于%s的讲解 程序会将%s指向的地址作为一个ASCII字符串处理直到遇到一个空字符。所以如果攻击者能够操纵这个参数的值那就可以泄露任意地址的内容。 或者说 %s 是把地址指向的内存内容给打印出来可以把 函数的地址给打印出来。 也就是说想要做到任意地址的内存泄露就需要想办法对栈上的某个地址的值进行修改为想要获得的那个字符串的地址。
覆盖栈内存
%n不能够输出字符但是它能把已经成功输出的字符个数写入对应的整形指针参数所致的变量只要变量对应的地址可写就可以利用格式化字符串来改变其对应的值。 一般来说这种方法利用的步骤为 确定覆盖地址确定相对位移进行覆盖 注以下为我个人的见解如有问题请指正。
首先之前说过了%n这个格式化字符串的作用是啥 %n,不输出字符但是把已经成功输出的字符个数写入对应的整型指针参数所指的变量。 之后用一个程序作为示例
#include stdio.h
int a 123, b 456;
int main() {int c 789;char s[100];printf(%p\n, c);scanf(%s, s);printf(s);if (c 16) {puts(modified c.);} else if (a 2) {puts(modified a for a small number.);} else if (b 0x12345678) {puts(modified b for a big number!);}return 0;
} 这个程序的格式化字符串漏洞的函数很容易就能找到那就是printf(s)因此假设对变量c进行修改就需要用到%n对C进行修改所以格式如下 c的地址12个任意的字符对应格式化字符串的参数的偏移 好了实际试试看吧
AAAA,$p,$p,$p,$p,$p,$p,$p,$p,$prootg01den-virtual-machine:/home/g01den/Temp# ./a
0xff8d3ad4
AAA,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p
AAA,0xff8d3ad8,(nil),0x565d51e4,(nil),0x315,0x2c414141,0x252c7025,0x70252c70,0x2c70252c,0x252c7025,0x70252c70 由此可知传入的格式化字符串AAA所对应的参数是第六位因此对应格式化字符串对应的偏移为6也就是说值为%6$n好的之后就是c的地址我在学到这里的时候出现了理解不能的问题最后大致是整明白了就是不知道对不对。
对于C的地址我认为是正是因为构造的格式化字符串的前四个字节是c的地址因此%n这个格式化字符串在写的时候对应的就是第六个参数而第六个参数的前四个字节就是c的地址因此就成功修改了c这个变量的值**那么问题就来了为啥不能直接指定第五个参数进行修改呢**我个人的理解是这样的因为第五个参数存放的并不是c这个变量的地址是789因此%n就试图把16写入789这个地址里去但是这个地址要么不存在要么就是无法访问则会出现segment fault导致程序出现了错误。
覆盖小数字
那么问题又出现了因为如果让变量的地址作为开始的字符就会导致%n写入的最小的值也是4那么如果要让某个变量被覆盖为2又该怎么做呢
注以下对于内容的解释部分均为我自己的理解如有错误还请指正。
照理来说想要某个变量被覆盖为2的话就需要诸如 aa%k$naa地址 这样的字符串所以是为啥呢
这里借用julao们的说法是 aa%k n x x 如果用这样的方式前面 a a nxx如果用这样的方式前面 aa%k 是第六个参数 nxx如果用这样的方式前面aanxx 是第七个参数后面在跟一个 我们想要修改的地址那么这个地址就是第八个参数只需要把 k 改成 8 就可以把这第八个参数改成 2aa%8$nxx 我个人的理解为在栈中每个字符的长度为1字节另外对于格式化字符串的参数传递而言每个参数都占用了四个字节32位所以这里需要前和中都构成四个字节的字符串因此第六位的字符串就是aa%k第七位就是$naa第八位就可以传入变量的地址了。
后续的内容需要花费一些时间短时间内先放放暂时放下不管之后有时间再来补充。