欢迎来到 黑吧安全网 聚焦网络安全前沿资讯,精华内容,交流技术心得!

格式化字符串任意地址写操作学习小计

来源:本站整理 作者:佚名 时间:2019-06-11 TAG: 我要投稿

大家对格式化字符串读操作一定不陌生,但是对写操作的概念或者具体步骤会比较模糊。这里主要总结一下格式化字符串写操作,会以两道例题来进行讲解。
格式化字符串写操作的原理
%c、%x 的用法
%c 在 printf 的使用中,表示的是输出类型为字符型,例如:%200c 表示总共输出 200 个字符,如果不足 200 个则在前面补上空字符。
再例如下面的代码:
#include
#include
int main(){
        int i = 0x6667;
        printf("%200c",((char *)&i)[1]);
        return 0;
}
输出结果:

所以根据上面的结果就很容易知道他的用法,这里的 %c 经常会配合 %n 来进行使用。
如果这里将 printf 的控制字符改成 %x 的话,结果也是大同小异,只是输出的类型变为字符的十六进制数,输出字符相应的数量会多 1 个。

%n 的用法
特殊的格式化控制符%n,和其他控制输出格式和内容的格式化字符不同的是,这个格式化字符会将已输出的字符数写入到对应参数的内存中。
%n          一次性写入 4 个字节
%hn         一次性写入 2 个字节
%hhn        一次性写入 1 个字节
%n 一般会配合 %c 进行使用,%c 负责输出字符,%n 负责统计输出的字符串的数量并转化为 16 进制格式写入到偏移的内存地址里。
所以之后写内存的任务其实就是计数的任务了,在后面的例子中也会详细讲解到。
这个控制字符的详细用法在这篇文章中讲的很清楚了,这里就不赘述了。
使用 pwntools 模块生成 payload
直接利用 pwntools 的 fmtstr_payload 函数即可生成相应的 payload,具体用法可以查看官方文档。
例如举一个最简单的用法,假如我们知道这里 fmt 的偏移是 4,我们要将 0x80405244 地址的值覆盖为 0x12344321,就可以这样调用:
fmtstr_payload(4,{0x80405244:0x12344321})

第三个参数(write_size)代表一次写入内存的字节数,三种 payload 都是一样的效果,具体选择哪个后面会说到。
 
例题
例题 1
这个例题是国赛某赛区线下半决赛的一道 pwn 题目。解题思路是覆盖某个函数的 got 表进行 getshell。
程序代码
主要逻辑只有一个 main 函数,允许输入 64 个字符的字符串(其实这是一个提示最后的 payload 是 64 个字节长度),接着在下面有一处很明显的格式化字符串漏洞,那么这里我们就可以输入 %x、%p 进行内存泄露。
int __cdecl main(int argc, const char **argv, const char **envp)
{
  char format; // [esp+0h] [ebp-48h]
  setvbuf(stdin, 0, 2, 0);
  setvbuf(stdout, 0, 2, 0);
  puts("Welcome to my ctf! What's your name?");
  __isoc99_scanf("%64s", &format);
  printf("Hello ");
  printf(&format);
  return 0;
}
同时在程序中还有一个 system 函数,这里的 command 是一个 0x00 的 4 字节位于 .data 段的值。

确定偏移
按照正常套路来先确定一波偏移。
aaaa%x,%x,%x,%x
aaaa%4$x
很容易知道这里的偏移是 4。

解题思路
来缕清一下思路,我们只有一次格式化字符串的机会,要么是进行格式化字符串的读操作,要么是进行格式化字符串的写操作,所以这里就没办法在读内存之后进行任意地址写了。
那么有没有办法同时读或者写呢?或者让程序多循环几次呢?答案是有的。
具体的做法可以参考某位大佬的文章:https://bbs.ichunqiu.com/thread-43624-1-1.html
引用文章中的一张图:

按照文章中的 “使用格式化字符串漏洞使程序无限循环“ 的操作,大概的操作是:
在将 start 函数或者 main 函数的地址覆写 .fini.array 段中的函数指针,导致程序在进行程序执行结束的收尾操作时,重新执行一次 main 函数,这样我们就可以重新返回 main 函数。在覆写 .fini.array 段的函数指针的同时,将 printf 函数的 got 表覆盖为 system 函数的地址即可。
在 IDA 中查看 .fini.array 中区段的函数,可见就只有一个函数指针:__do_global_dtors_aux_fini_array_entry,所以我们的目的就是把 main 的地址写到这个地址即可。

回到 main 函数后,输入 /bin/shx00 就相当于调用 system(“/bin/sh”) 函数。

[1] [2] [3]  下一页

【声明】:黑吧安全网(http://www.myhack58.com)登载此文出于传递更多信息之目的,并不代表本站赞同其观点和对其真实性负责,仅适于网络安全技术爱好者学习研究使用,学习中请遵循国家相关法律法规。如有问题请联系我们,联系邮箱admin@myhack58.com,我们会在最短的时间内进行处理。
  • 最新更新
    • 相关阅读
      • 本类热门
        • 最近下载