题目附件https://github.com/nyyyddddn/ctf/tree/main/xyctf_pwn
前段时间好忙,最近这几天才抽出时间整理下东西,xyctf是今年四月份开的,当时是第一次出题,想给入门pwn一段时间的师傅分享些简单又可以开阔一下思路的pwn题
hello_world(签到)
在glibc 2.31以后 csu fini csu init都变成了动态链接,大多数好用的修改寄存器的gadget(如pop rdi ret,pop rsi ret)都是从csu init中错位字节获取的,在能泄露libc的情况下,可以转换一下思路,从libc中拿gadget
程序逻辑
1 | int __cdecl main(int argc, const char **argv, const char **envp) |
程序有两次输入的机会,第一次输入通过%s去泄露一个libc相关的地址,然后计算出libc_base,然后用libc中的gadget打rop就行了
exp
1 | from pwn import * |
Intermittent
程序的逻辑
1 | int __cdecl main(int argc, const char **argv, const char **envp) |
有三次输入的机会,输入完后会执行输入的shellcode,但是三次输入的地址都不连续,中间有大量的00字段阻碍shellcode的执行,同时长度也不够getshell,所以需要找到连接三段shellcode的方法 以及syscall read一次
这时候有两种思路,第一种是利用相对偏移跳转/相对偏移call去连接三段shellcode,第二种是,阻碍shellcode的原因是因为解引用错误导致程序崩溃,\x00\x00 对应 add byte ptr [rax], al,只需要将rax修改成一个正确的地址,就可以绕过中间这段00数据
第一种思路的exp
1 | from pwn import * |
第二种思路的exp
1 | from pwn import * |
invisible_flag
程序的逻辑
1 | int __cdecl main(int argc, const char **argv, const char **envp) |
seccomp的规则
1 | line CODE JT JF K |
open read write被禁用了,需要找能替代这三个系统调用的方法,最简单的做法是用openat去替代open,然后用sendfile去代替read和write将flag读出来,或者是其他系统调用,或者是用侧信道 + 二分法把flag爆破出来
exp
1 | from pwn import * |
static_link
静态链接的程序,没有system函数 没有binsh字符,可以用ret2syscall,syscall read把binsh写到一个已知地址的内存段里,然后再调用execve拿shell
1 | __int64 vuln() |
exp
1 | from pwn import * |
fmt
1 | int __cdecl main(int argc, const char **argv, const char **envp) |
scanf和printf不同的地方是,scanf并不能通过 %n$p 这个表达式实现任意地址的泄露,但是任意地址写是可以实现的,glibc 2.31 在有libc地址的情况下可以打exit_hook为backdoor 或者one gadget
exp
1 | from pwn import * |
simple_srop
1 | int __cdecl main(int argc, const char **argv, const char **envp) |
Srop + orw, 首先先调用一次signal return布置新栈,栈迁移到一个已知的地址,然后打signal return三次打orw就好了,或者是mprotect变成shellcode版本的orw,这样操作会少一些
exp
1 | from pwn import * |
one_byte
1 | int __cdecl __noreturn main(int argc, const char **argv, const char **envp) |
glibc 2.31,泄露libc地址的方式有很多,read_data中存在一个单字节的溢出,可以覆盖相邻堆块的size位一个字节,off by one漏洞,可以通过切分unsortedbin的方式,利用残留地址去泄露libc,或者是large bin残留的指针,也可以构造堆叠的方式去泄露,然后任意地址申请的原语可以通过double free去申请,也可以通过堆叠去实现 exp
1 | from pwn import * |
fastfastfast
1 | int __cdecl __noreturn main(int argc, const char **argv, const char **envp) |
glibc 2.31,通过fastbin double free去写free hook为system然后 free一个binsh的堆就可以getshell
exp
1 | from pwn import * |
ptmalloc2 it’s myheap
1 | int __cdecl __noreturn main(int argc, const char **argv, const char **envp) |
可以通过glibc堆分配机制伪造一个chunk->inuse去泄露libc,meta_chunk中的指针可以用来泄露堆基地址,有了堆基地址就可以绕过safe link实现任意地址分配,可以通过写gift函数指针去劫持控制流
exp
1 | from pwn import * |
ptmalloc2 it’s myheap pro
在ptmalloc2 its myheap的基础上把gift函数移除掉了
可以通过environ去泄露栈地址,打栈迁移
或者是用exit_hook的做法,有注册函数可以通过异或拿到fs 0x28的值,然后把cxa类型dl_fini写成system的地址 再传binsh的参数
用environ的方式会简单一些
exit_hook详细的原理可以参考这篇文章 https://m101.github.io/binholic/2017/05/20/notes-on-abusing-exit-handlers.html
exp
1 | from pwn import * |
ptmalloc2 it’s myheap plus
在pro的基础上加了orw
在有任意地址读写的原语后,实现orw三个函数调用,比较简单的做法是通过environ泄露栈地址,然后栈迁移到堆上打rop
1 | from pwn import * |