00:00
大家好,今天我们看一下格式化字符串漏洞。首先我们看一下格式化字符串函数,它的作用是将计算机内存中表示的数据转化成我们比较容易读的这样的字符串的格式。然后它是有很多函数的,但我们只看print f,那下面有一个例子,它的第一个参数,也就是这一串叫做格式化字符串。当F去输出的时候,如果是正常的这样的字符,它就正常的输出了,但如果是百分号S这样的,它就会去对应的位置去找到它,并且用字符串的形式给输出来,如果是百分号D呢,它就会去用时间制整数的形式给输出来,就是它输出的时候的一个规则吧,算是然后。主要看一下这个百分N啊,这个应该不太常见,但是我们经常要用到,如果做题的话用它来去,因为它不是去读出的,它是去写入的,它写入的是到目前为止我们已经接收的这样的一个字符的数,然后的话会在后面去覆盖一些地址什么的。
01:14
可以用到,然后我们来看具体的一些利用。首先是最简单的一种方法就是输入一串百分号S,然后对于每一个百分号S来说,Print f会从站上取一个数字,然后它会把这个数字视为地址,去打印这个地址指向的内存内容,但因为我们不可能获取的每一个数字都是一个正常的地址啊,或者说那个地址它是受保护的,那这样的话它就会让程序给崩溃掉,因为在Linux里面存取如下的指针会引起进程受到这个什么信号,然后它程序就非正常终止,产生这种什么核心转储了,嗯,然后我们来看他泄露内存啊。
02:02
这是这个例子的源码编译的命令,然后我们直接来运行一下。首先我们在F上给它下个断点,然后我们运行起来,输入的内容是这一串,此时站上的这个布局我们可以对应着源码来看一下。首先是格式化字符串,也就是乘的F的第一个参数,然后是零叉一,也就是A,然后是这里我们这样看一下,零叉二,二二,也就是B,然后下面这个负一,零叉FFF,再下面的话就是我们输入的这个S了。
03:01
零打开号零八叉,嗯,此时我们contain让它继续运行一下,它输出的内容是比较正常的,对吧,然后它在第二个PF上又给断下来了,这时候我们的格式化字符串。就是我们之前输入的这个S了。那这样的话,因为它是没后面没有对应参数的,它同样会去解析后面这三个位置的内容,然后给输出来,我们来看一下。C70对应的这一个,然后零叉C2对应这一个,然后这里对应这里,那这样的话,其实我们去就可以去泄露站上的那个信息了,对吧?当然这种方法还是比较麻烦的,因为我们想先入第三个,我不没有必要去把前面两个也写出来,挨个去写,我们还有一种方法。
04:04
百分号,然后这这个后面,百分号后面的数字就是你想泄露的第几个参数,第第几个位置就是对应格式化字符串它的第几个位置。因为我们前面说这是第一个参数,这是第二个参数,这是第三个,这是第四个,这是相对于print f这个函数来说的,但如果相对于格式化字符串。很明显它的第一个参数,也就是百分号零八叉输出的第一个内容实际上是在这个地方的,对吧,那我们想要泄露第三个。对应格式化字符串的这个地方就是三。然后到了差。那此时再运行一下,可以看到第三个地方的内容就给输出来了。对吧,这样的话我们其实就可以泄露站上的,想要泄露哪一个地方都可以了,对吧。
05:06
但有个问题是,我们这样只能就没有办法泄露任意的地址。比如说,我们想泄露put函数的。高表象的地址,那我们这样是没有没有办法去泄露的,我们来看一下怎么去利用。好,我们来这里看一下,如果说我们把这第三个位置给它改成某一个函数的个表象的话,那我们这里如果是百分号三到了S的话,呃,这个地方应该如果是百分号三到S的话,它就会把这个地址所指向的那个。字符串给我们输出来,也就是说他就会把它的表象的内容给输出来,那这样的话,我们就拿到了它的真实的一个函数的地址,然后我们就可以去利用了什么样的,对吧?但我们没有,我们不能去直接输入,就是说如果我们想要输入一个。
06:13
呃。这样的,如果我们想要输入0804A014的话,我们不能这样输,因为它SCF会把它识别成杠,然后叉幺四,这样的,我们还是需要去借助count的这样的一个函数P64 p32这样去输。那我们看一下,首先我们需要去找一下他这个。具体对应的位置就是说啊,我直接复制一下。这样我们来看一下,首先上面这一串是它第一个平的F正常输出的,我们不用管了,然后我们来看下面对应的我我们想要知道我们输入的内容在。
07:09
格式化字符串的第几个参数上给输出来了,那我们数一下。前面因为我们输入的是AA嘛,那它对应的就是这个地方,然后这是第一个第二个。第三个,第四个,也就是说我们的输入被在有格式化字符串会在第四个位置上给输出来,这样的话,如果我们前面我们发的票的如果是一个函数的。表上的地址。然后后面再跟一个百分号四到S的话,那它就会把这个东西给我们打出来,我们看一下,先把调试给注释掉,没保存。
08:17
那可以看到他就。给我们把。看F这个高表象的内容给我们打出来了,我们拿到这一个函数的地址以后啊,我们当然可以用level算去找一些level什么的。但我们其实有一个更。更有用的是把这个。地址给改掉。那接下来我们看一下怎么去覆盖一个内存地址。如果我们想要覆盖内存的话,就要用到百分号N了,那它的作用就是把已经成功输入的字符的个数给写入到对应的整形指针参数所指的变量,然后我们的用的步骤分为这三步,首先我们要确定我们覆盖的那个地址,然后我们要确定我们输入的内容在相对于格式化字符串,它是第几个参数,然后就可以进行覆盖了。那拿下面这个例子来说,我们想要覆盖C,把它改成16的话。
09:25
首先地址问题,他已经给我们输出来了,在前面我们就不需要管了,然后的话确定偏移,我们可以用这种方法来测,去数一下我们前面输入的AA,可以看到对,对应后面的就是这里了,那我们数一下它是第几个,首先这是第一个。这是第二个,这是第三个,这是第四个,这是第五个,我们输入的是在第六个,所以只要百分号六到了二就可以了。
10:07
那既然我们想要改它的话,我们需要前面有16个字节才能把它改成16,所以这一个百分号012D是用来填12的,因为P32C的地址是一个四字解嘛,所以这样的话,因为一开始我们输的这一个C的地址就是在第六个参数上,所以我们只需要这样就可以改好了,那这是改C,然后我们看一下,改一下其他两个,其他两个比较特殊。首先我们看第一种情况是覆盖一个小数字,因为前面我们的A,我们想要把它改成二的话,如果我们按照这种方法,P3,二,它已经是四个字节了,我们不可能把它改成一个更小的数了,对吧?所以我们需要把A的地址放在后面,这样它就不会受到影响了。然后。
11:01
我们如果这样说的话,我们看一下。首先它接收到两个A对吧,然后它会在第八个位置上去写入一个二,因为它接收到的是两个嘛,那第八个位置对应的就是A的地址,我们来看一下,首先这是四个,然后这是四个,那这再是四个,所以这是第六个。第七个第八个对吧?嗯,那A的地址,因为前面我们看到是在这上面这样的定义的,所以我们只用IDA是可以看到的。嗯。就是这里我们直接可以看到,然后B地址也在这里,对吧,所以这是覆盖一个小数字,我们来看覆盖一个大数字,如果我们想要去写一个特别大的数,比如说上面的这个B啊,这个B。
12:05
呃,我们来看一下,他的话是我们要发这么多的字符,对吧,显然不太不太现实,然后的话,我们的想法是一个字节一个字节的去写,比如说我们在B的地址上去写一个零叉七八,然后在B的地址加一的这个地方就写一个零叉五六这样去。这样的话就比较比较现实了,嗯,然后我们看一下这个P的。哦,对,前面先说一下HHN写入的是单字节,然后HN写入的是一个双字节,这里我们用的就是HHN,那看一下配料的,首先前面的这这一串是B的地址,对吧,这是四个四也是16。16,然后再加上一个,这里的是104,可以看到得到的就是一个七八,所以我们在B的地址这一个。
13:07
第一个字节上先写一个七八,然后再往后是B的地址加一,我们去写的是一个前面的再加上222,拿到的就是一个五六,对吧。因为我们写入的是一个字节嘛,所以说只有后面这个五六,前面这个一是就写不上的,所以。再往后写,因为当然前面整个这一串,我们第一个是第六个位置的参数,然后第七个位置,第八个位置,第九个位置,就是对应着下面这个6789这样,然后我们看。再加222得到的是零差三四,那再加222得到的是零差一二对吧,就最后一个字节嘛,然后我们写进去以后,就可以把B的地址给改成0X145678了,对,因为它是小端存储的,就可以对比一下下面这个例子,就可以看出是什么什么什么原因了,好吧,然后我们来看一下例题。
14:18
首先是一个64位的题,然后他首先会把flag读到站上,跟你跟你输入的去做一下对比,那我们GDP调试看一下。首先在print上下个断点,我们运行一下。这里随便输几个,那它断下来以后,我们看一下站上的情况,Flag是在这里,也就是1234第四个参数上,同时我们这是一个64位的程序,它的前六个参数是在寄存器上,所以而同时第一个寄存器是rdi的,第一个参数存在rdi上,然后第一个参数是格式化字符串嘛,那我们这个flag它的位置相对于格式化字符串来说,它是在。
15:15
五加四第九个参数上,所以如果我们输的是百分号九到S的话,他就会把我们的flag给输出来。啊。这就是一叉P,就是这么写的,然后我们看下一个。这种利用方式就是要去把它的高特表象给改掉,就是说当我们修改了一个高特表象的时候,比如说我们把print f的高特表项改成了system函数的地址,那执行print f的时候,实际上就是去执行了system函数。啊,我们看一个例题,这个题它实现了一个类似FTP的功能,那需要你去输入一个用户名去登录,但是它输入以后并不是直接去跟他那个检验的,对这个字符串做对比,它是经过了一个变化的,就是对每一个字符都加了个一,也就是说我们不能直接输这个SYSBDMN,我们要输它往前移一个的那个,也就是这个。
16:23
这样就可以登录成功了,然后登录成功以后是有三个功能的,一个get,一个put,一个DR get是去往外读的,同时它存在一个格式化字符串漏洞在这里。然后我们的大体的思路就是先通过get功能,利用这个格式化字符串漏洞把LA给泄露出来,然后去计算那个system的地址,然后我们把system的地址写到post函数的高表象上,因为当程序执行D功能的时候,它是调用了一个po的。然后。
17:01
我们只要传一个杠B-SH的参数,就可以去执行这个system-B-SH了,好,我们来具体的操作一下。我来看一下。首先先下个断点吧,其实也没有必要,然后运行输入用户名,用户名就是用这个。那这样我们就登录成功了,然后我们可以用那三个功能,首先我们先put一下,先写一个东西对吧,然后一个AAA找一个。
18:02
然后再去用get去看一下这个东西,它就会触发一个格式化字符串流动那。啊,也没有必要我们直接输出吧,看一下这个NR。它实际上对应的是这个零差零啊。然后我们输入的内容是在这儿。我们看一下是第几个,首先第一个,第二个。第三个第四个第五个第六个第七个上面对吧,那我们如果想要去把铺子输出来的话,我们可以用这种方法。就前面是这是第七,然后这是第八了嘛,我们把第八个给输出来。呃,当然也也可以去把铺子放在前面,然后输那个第七个。
19:06
但是这样的话,你接收的时候。是需要。取后面的。就是它首先会把这个东西给给百分号P给打出来。所以你需要在。在额外就是说这个地方要改成四冒号八这样的啊,那我们直接看P的,首先这前面是登进去的这种操作就不说了,那通过我们写的这一个。他去把文件的内容吧,给改成了这样,然后执行那个get的时候。我们去读它成功泄露了put地址,然后根据LA search去找一下system的地址。然后我们想要把put的那个地址给改成system的那个地址。
20:01
对吧,就直接这里就直接用那个point特里面带的那个函数了,直接一个函数就给改好了,当然这还没改,我们的话要发票的,然后我们再写一个此时的函数名,不是函数名,此时的文件名要用这一个,因为我们去执行system的时候是这样的,所以这个要加快这个,这个叫什么分号啊,然后我们写的内容就是我们要去执行的这个配料的,去把put的get表象改成system的地址,然后去执行一下get,去触发这个漏洞,再去执行DR就可以拿到shell了。就是这一个改过的,然后我们看一下这个。嗯,这一个是想用格式化字符串漏洞来把那个返回地址给改掉,那我们这一个我们直接看一下。嗯。
21:05
首先他让你去登录,那我们输AAA,然后让你输密码,我们输百分号P了。这样它有三个功能,一个是看你的账户信息,一个是编辑你的账户信息,然后就是退出了,那我们可以去利用编辑去进行写入,然后通过看。利用这个格式化字符串漏洞来触发,呃。嗯。
22:04
我们直接shift加F12是可以找到这个这个后门的,也就是说如果我们把返回地址改成这里的话,我们就可以去直接去拿到这个shell了,呃,好,我们来看一下。用GDB来看一下啊。首先下个断点是在。这里。嗯。然后运行我们说A,哎一样,然后把分后,哎哟。这样此时我们用一这个看的功能,我们来看一下。
23:11
那我们现在还没有去调用这个Co上面的这一个,实际上是包含着这个函数的一个。EBP不是,呃,RBP对吧,然后下面这一个是这个函数的。返回地址。就是在这个地方,我们想要做的就是把这个返回地址给改掉,改成我们找到的那一个。这个地方。那我们直接来看这个图啊。这个标好了,因为它是一个64位的嘛,然后第一个参数是2D里面的格式化字符串,然后就下面的话就是6789是这样排的。
24:01
我们想要改这一个的话,首先要知道它的地址啊,对吧,也就是存的这一个,那他我们是没有办法去直接去找到的,但是相对于第六个参数的这个RBP的内容来说的话,它两个的偏移是固定的,所以我们可以通过格式化字符串漏洞把第六个的内容给泄露出来,然后加上这个偏移就得到这个地址了。那我们就可以去对这个地址进行更改了,对吧?那通过计算我们计算得到的是零叉三八,所以我们通过百分号六到S去把啊百号六到P,我们直接需要直接把这个这个东西给显露出来就可以了,然后加上一个零乘三八,得到的这个就是我们想要去改的,把这个地址上的内容给改成我们的返回地址,就是system那个函数的地址啊。
25:01
呃,然后。就是。我们的返回地址拿到以后,把这个2214,也就是8A6嘛,就是上面找到的这个。8A6这样的一个地址去给他写进去就可以了。嗯,就这些啊,然后后面的话那个堆上的就以后再说吧,然后格式化自串盲打这个东西我后面没有跟他的不太一样,所以就先。先不录视频了,等以后再补上好吧。
我来说两句