(置顶)pwncollege部分通关记录

指 https://dojo.pwn.college/challenges

按照网站要求,这里只提供思路和前两题的exploit。

heap

babyheap1.0~2.1——uaf

1
2
3
4
5
def exploit():
malloc(flag_size)
free()
read_flag()
puts()

babyheap3.0~3.1——uaf2.0

注意tcache是LIFO结构

babyheap4.0&4.1——tcache double free

第一次独立写出堆题,虽然是最简单的那种tcache double free

babyheap5.0&5.1——unsorted bin attack

libc 2.23->libc 2.31

通过耗尽所有的tcache来触发small bin和unsorted bin,进而修改chunk。

babyheap6.0&6.1——tcache poisoning

简单的tcache poisoning

https://wargames.ret2.systems/level/how2heap_tcache_poisoning_2.31

babyheap7.0&7.1——tcache poisoning2.0

由于flag不会变化,secret后半部分,只需将sec_addr+=8,重新运行即可。

babyheap8.0&8.1——brute force

通过先前的方法可以泄露出secret的后12位,前4位可以通过暴力枚举实现。

脚本跑了一节课才跑出来。

babyheap9.0——tcache double free

由于允许uaf,第一次free过后将next指针置0,即可double free。

此时如果将next改成0x42b321就可在给出的堆信息中看到key。

babyheap9.1——tcache double free | perthread corruption

接着上一关的步骤,接着malloc两次,虽然题目禁止你读取key附近的指针,但malloc操作还是进行了。

由于tcache的链表特性,addr+8处会被清零。再进行一次这样的操作后,可以输入16个\0通过验证。

通过修改地址到perthread struct读取在链表头的8字节key理论上可行,就没试了。

babyheap10.0&10.1——heap on stack

给出了栈地址与elf地址,因此直接把堆放到栈上,修改retn地址即可。

babyheap11.0&11.1——free_hook+arbitary read

由于不知道elf地址和栈地址,首先我覆盖free_hook(注意写free_hook的时候size位最好是\x7f)成为system_plt拿到了shell,但是并没有权限。

然后发现执行echo函数后会多开辟一个堆空间,里面有elf与stack相关的地址,而且echo函数没有限制偏移大小,因此可以拿到这些地址。

由于我已经可以写free_hook,直接将free_hook覆盖成win即可。

babyheap12.0&12.1——modify size

由于tcache的机制,修改栈上堆的size即可绕过合法性检测。

babyheap13.0——tcache double free

类似于babyheap9.1的做法,分配到sec附近,然后输入一堆\0覆盖key

babyheap13.1——modify size

由于sec与堆的偏移不超过一个字节,修改栈上堆的size之后,使用malloc分配一个稍微大点的堆填充数据即可。

babyheap14.0&14.1——modify size+leak elf&canary

在level13的基础上,用echo泄露elf地址与canary地址,然后在堆上进行栈溢出。

对于14.1,注意\x09不可输入,建议换一个靠后的地址。

这个题有点tricky,我把elf、stack、libc、heap泄露完了才发现uaf已经没了。

任意写的方式除了double free就是unlink了,我懒得去想多么巧妙的堆风水,干脆直接unlink结束战斗。

kernel

babykernel1.0~6.1——basic definition

参照这篇博客:https://www.cnblogs.com/crybaby/p/14431651.html

kernel shellcode着实有点麻人。

注:如何查看有没有kaslr?

进入kernel后,demsg查看第二行最后有没有nokaslr

babykernel7.0~7.1——struct&debug

需要传入一个类似于这样的结构体:

1
2
3
4
5
struct shellcode{
unsigned long length;
char shellcode[0x1000];
unsigned long* shellcode_addr;
}

注意以下几点:

  • shellcode必须包含ret语句(ret2usr)。
  • shellcode_addr可以通过动调得到,位置固定。

babykernel8.0~8.1——shellcode in shellcode

一句话——在shellcode里写shellcode

第一次进内核来一遍commit_creds(prepare_kernel_cred(0))ret2usr后变成root shell。再进入一次run_cmd /bin/chmod 777 /flag即可修改flag权限。(注意在内核态里起shell没有任何作用)

babykernel9.0~9.1——run_cmd

直接把printk指针改为run_cmd,然后rdi是输入处,写入/bin/chmod 777 /flag即可。

babykernel10.0——kaslr leak

注意kaslr只有在kernel重启之后才会重新随机,所以重新运行程序,原有的kernel函数地址不会变。

与userland的aslr类似,kaslr的低5位都是固定的。写入256字节使得printk的地址被泄露,从而找到run_cmd的地址。

babykernel10.1——partial overwrite

由于该驱动不会打印自己的输入,这里采取重写printk的后3字节,也就是得爆破run_cmd的倒数第六位。

babykernel11.0~12.1——memory scanning

程序将flag加载到内存里,然后删掉了flag文件,因此flag只能在内存里面找。

通过discord的spoiler可以得知应该从0xffff888000000000开始扫内存(即宏phys_to_virt(0)的值,也是源码__PAGE_OFFSET_BASE_L4一行的值),然后dmesg找一下flag即可。

注意以下几点:

  • 如果用这个网站将汇编转成字节码,务必把字节码转成汇编验证一下是否漏掉了指令。
  • 为了减少输出,建议将地址的值与pwn.coll(或类似的64位字符串)比较一下再打印到控制台。
  • python很耗费内存,可能起一遍python,flag那部分的内存就被重新分配了。所以建议把shellcode输出到文件再进行文件流输入。