diff --git a/SUMMARY.md b/SUMMARY.md index 667bedc..af7d1c5 100644 --- a/SUMMARY.md +++ b/SUMMARY.md @@ -76,6 +76,7 @@ GitHub 地址:https://github.com/firmianay/CTF-All-In-One * [3.1.11 Linux 内核漏洞利用](doc/3.1.11_linux_kernel_exploit.md) * [3.1.12 Windows 内核漏洞利用](doc/3.1.12_windows_kernel_exploit.md) * [3.1.13 竞争条件](doc/3.1.13_race_condition.md) + * [3.1.14 虚拟机逃逸](doc/3.1.14_vm_escape.md) * Reverse * [3.2.1 patch 二进制文件](doc/3.2.1_patch_binary.md) * [3.2.2 脱壳技术(PE)](doc/3.2.2_pe_unpack.md) @@ -165,6 +166,8 @@ GitHub 地址:https://github.com/firmianay/CTF-All-In-One * [6.1.29 pwn Insomni'hack_teaserCTF2017 The_Great_Escape_part-3](doc/6.1.29_pwn_insomnictf2017_the_great_escape3.md) * [6.1.30 pwn HITCONCTF2017 Ghost_in_the_heap](doc/6.1.30_pwn_hitconctf2017_ghost_in_the_heap.md) * [6.1.31 pwn HITBCTF2018 mutepig](doc/6.1.31_pwn_hitbctf2018_mutepig.md) + * [6.1.32 pwn SECCONCTF2017 vm_no_fun](doc/6.1.32_pwn_secconctf2017_vm_no_fun.md) + * [6.1.33 pwn 34C3CTF2017 LFA](doc/6.1.33_pwn_34c3ctf2017_lfa.md) * Reverse * [6.2.1 re XHPCTF2017 dont_panic](doc/6.2.1_re_xhpctf2017_dont_panic.md) * [6.2.2 re ECTF2016 tayy](doc/6.2.2_re_ectf2016_tayy.md) @@ -221,6 +224,7 @@ GitHub 地址:https://github.com/firmianay/CTF-All-In-One * [8.27 Firmalice - Automatic Detection of Authentication Bypass Vulnerabilities in Binary Firmware](doc/8.27_firmalice_bypass.md) * [8.28 Cross-Architecture Bug Search in Binary Executables](doc/8.28_cross_arch_bug_search.md) * [8.29 Dynamic Hooks: Hiding Control Flow Changes within Non-Control Data](doc/8.29_dynamic_hooks.md) + * [8.30 Preventing brute force attacks against stack canary protection on networking servers](doc/8.30_prevent_brute_force_canary.md) * [九、附录](doc/9_appendix.md) * [9.1 更多 Linux 工具](doc/9.1_Linuxtools.md) * [9.2 更多 Windows 工具](doc/9.2_wintools.md) diff --git a/doc/1.3_linux_basic.md b/doc/1.3_linux_basic.md index 4cfd102..74f248f 100644 --- a/doc/1.3_linux_basic.md +++ b/doc/1.3_linux_basic.md @@ -12,7 +12,8 @@ - [核心转储](#核心转储) - [调用约定](#调用约定) - [环境变量](#环境变量) -- [/proc/[pid]](#procpid) +- [procfs](#procfs) +- [参考资料](#参考资料) ## 常用基础命令 @@ -399,6 +400,8 @@ Cannot access memory at address 0x4141413d ## 环境变量 +环境变量字符串都是 `name=value` 这样的形式。大多数 name 由大写字母加下画线组成,一般把 name 部分叫做环境变量名,value 部分则是环境变量的值,而且 value 需要以 "/0" 结尾,环境变量定义了该进程的运行环境。 + #### 分类 - 按照生命周期划分 - 永久环境变量:修改相关配置文件,永久生效。 @@ -465,12 +468,11 @@ $ LD_PRELOAD=~/libc.so.6 ldd /bin/true /lib64/ld-linux-x86-64.so.2 (0x000055f33bc50000) ``` -注意,在加载动态链接库时需要使用`ld.so`进行重定位,通常被符号链接到`/lib64/ld-linux-x86-64.so`中。动态链接库在编译时隐式指定`ld.so`的搜索路径,并写入ELF Header的INTERP字段中。从其他发行版直接拷贝已编译的`.so`文件可能会引发`ld.so`搜索路径不正确的问题。相似的,在版本依赖高度耦合的发行版中(如ArchLinux),版本相差过大也会引发`ld.so`的运行失败。 +注意,在加载动态链接库时需要使用 `ld.so` 进行重定位,通常被符号链接到 `/lib64/ld-linux-x86-64.so` 中。动态链接库在编译时隐式指定 `ld.so` 的搜索路径,并写入 ELF Header 的 INTERP 字段中。从其他发行版直接拷贝已编译的 `.so` 文件可能会引发 `ld.so` 搜索路径不正确的问题。相似的,在版本依赖高度耦合的发行版中(如 ArchLinux),版本相差过大也会引发 `ld.so` 的运行失败。 -本地同版本编译后通常不会出现问题。如果有直接拷贝已编译版本的需要,可以对比`interpreter`确定是否符合要求,但是不保证不会失败。 +本地同版本编译后通常不会出现问题。如果有直接拷贝已编译版本的需要,可以对比 `interpreter` 确定是否符合要求,但是不保证不会失败。 上面的例子中两个 libc 是这样的: - ``` $ file /lib/x86_64-linux-gnu/libc-2.23.so /lib/x86_64-linux-gnu/libc-2.23.so: ELF 64-bit LSB shared object, x86-64, version 1 (GNU/Linux), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=088a6e00a1814622219f346b41e775b8dd46c518, for GNU/Linux 2.6.32, stripped @@ -496,112 +498,304 @@ $ file ~/libc.so.6 ``` 一个在 `interpreter /usr/lib/ld-linux-x86-64.so.2`,而另一个在 `interpreter /lib64/ld-linux-x86-64.so.2`。 +#### environ +libc 中定义的全局变量 `environ` 指向环境变量表。而环境变量表存在于栈上,所以通过 `environ` 指针的值就可以泄露出栈地址。 +``` +gdb-peda$ vmmap libc +Start End Perm Name +0x00007ffff7a1c000 0x00007ffff7bcf000 r-xp /usr/lib/libc-2.27.so +0x00007ffff7bcf000 0x00007ffff7dce000 ---p /usr/lib/libc-2.27.so +0x00007ffff7dce000 0x00007ffff7dd2000 r--p /usr/lib/libc-2.27.so +0x00007ffff7dd2000 0x00007ffff7dd4000 rw-p /usr/lib/libc-2.27.so +gdb-peda$ vmmap stack +Start End Perm Name +0x00007ffffffde000 0x00007ffffffff000 rw-p [stack] +gdb-peda$ shell nm -D /usr/lib/libc-2.27.so | grep environ +00000000003b8ee0 V environ +00000000003b8ee0 V _environ +00000000003b8ee0 B __environ +gdb-peda$ x/gx 0x00007ffff7a1c000 + 0x00000000003b8ee0 +0x7ffff7dd4ee0 : 0x00007fffffffde48 +gdb-peda$ x/5gx 0x00007fffffffde48 +0x7fffffffde48: 0x00007fffffffe1da 0x00007fffffffe1e9 +0x7fffffffde58: 0x00007fffffffe1fd 0x00007fffffffe233 +0x7fffffffde68: 0x00007fffffffe25f +gdb-peda$ x/5s 0x00007fffffffe1da +0x7fffffffe1da: "COLORFGBG=15;0" +0x7fffffffe1e9: "COLORTERM=truecolor" +0x7fffffffe1fd: "DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/1000/bus" +0x7fffffffe233: "DESKTOP_SESSION=/usr/share/xsessions/plasma" +0x7fffffffe25f: "DISPLAY=:0" +``` -## /proc/[pid] -proc 文件系统是 Linux 内核提供的,为访问系统内核数据的操作提供接口。在该文件系统下,有一些以数字命名的目录,这些数字是进程的 PID 号,而这些目录是进程目录。 -目录下的所有文件如下,然后会介绍几个比较重要的: +## procfs +procfs 文件系统是 Linux 内核提供的虚拟文件系统,为访问系统内核数据的操作提供接口。之所以说是虚拟文件系统,是因为它不占用存储空间,而只是占用了内存。用户可以通过 procfs 查看有关系统硬件及当前正在运行进程的信息,甚至可以通过修改其中的某些内容来改变内核的运行状态。 + +#### /proc/cmdline +在启动时传递给内核的相关参数信息,通常由 lilo 或 grub 等启动管理工具提供: +``` +cat /proc/cmdline +BOOT_IMAGE=/boot/vmlinuz-4.14-x86_64 root=UUID=8e79a67d-af1b-4203-8c1c-3b670f0ec052 rw quiet resume=UUID=a220ecb1-7fde-4032-87bf-413057e9c06f +``` + +#### /proc/cpuinfo +记录 CPU 相关的信息: +``` +$ cat /proc/cpuinfo +processor : 0 +vendor_id : GenuineIntel +cpu family : 6 +model : 60 +model name : Intel(R) Core(TM) i5-4210H CPU @ 2.90GHz +stepping : 3 +microcode : 0x24 +cpu MHz : 1511.087 +cache size : 3072 KB +physical id : 0 +siblings : 4 +core id : 0 +cpu cores : 2 +apicid : 0 +initial apicid : 0 +fpu : yes +fpu_exception : yes +cpuid level : 13 +wp : yes +flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx pdpe1gb rdtscp lm constant_tsc arch_perfmon pebs bts rep_good nopl xtopology nonstop_tsc cpuid aperfmperf pni pclmulqdq dtes64 monitor ds_cpl vmx est tm2 ssse3 sdbg fma cx16 xtpr pdcm pcid sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand lahf_lm abm cpuid_fault epb invpcid_single pti ibrs ibpb stibp tpr_shadow vnmi flexpriority ept vpid fsgsbase tsc_adjust bmi1 avx2 smep bmi2 erms invpcid xsaveopt dtherm ida arat pln pts +bugs : cpu_meltdown spectre_v1 spectre_v2 spec_store_bypass +bogomips : 5788.66 +clflush size : 64 +cache_alignment : 64 +address sizes : 39 bits physical, 48 bits virtual +power management: +... +``` + +#### /proc/crypto +已安装的内核所使用的密码算法及算法的详细信息: +``` +$ cat /proc/crypto +name : ccm(aes) +driver : ccm_base(ctr(aes-aesni),cbcmac(aes-aesni)) +module : ccm +priority : 300 +refcnt : 2 +selftest : passed +internal : no +type : aead +async : no +blocksize : 1 +ivsize : 16 +maxauthsize : 16 +geniv : +... +``` + +#### /proc/devices +已加载的所有块设备和字符设备的信息,包含主设备号和设备组(与主设备号对应的设备类型)名: +``` +$ cat /proc/devices +Character devices: + 1 mem + 4 /dev/vc/0 + 4 tty + 4 ttyS + 5 /dev/tty + 5 /dev/console +... +``` + +#### /proc/interrupts +X86/X86_64 系统上每个 IRQ 相关的中断号列表,多路处理器平台上每个 CPU 对于每个 I/O 设备均有自己的中断号: +``` +$ cat /proc/interrupts + CPU0 CPU1 CPU2 CPU3 + 0: 15 0 0 0 IR-IO-APIC 2-edge timer + 1: 46235 1277 325 156 IR-IO-APIC 1-edge i8042 + 8: 0 1 0 0 IR-IO-APIC 8-edge rtc0 +... +NMI: 0 0 0 0 Non-maskable interrupts +LOC: 7363806 5569019 6138317 5442200 Local timer interrupts +SPU: 0 0 0 0 Spurious interrupts +... +``` + +#### /proc/kcore +系统使用的物理内存,以 ELF 核心文件(core file)格式存储: +``` +$ sudo file /proc/kcore +/proc/kcore: ELF 64-bit LSB core file x86-64, version 1 (SYSV), SVR4-style, from 'BOOT_IMAGE=/boot/vmlinuz-4.14-x86_64 root=UUID=8e79a67d-af1b-4203-8c1c-3b670f0e' +``` + +#### /proc/meminfo +系统中关于当前内存的利用状况等的信息: +``` +$ cat /proc/meminfo +MemTotal: 12226252 kB +MemFree: 4909444 kB +MemAvailable: 8776048 kB +Buffers: 288236 kB +Cached: 3953616 kB +... +``` + +#### /proc/mounts +每个进程自身挂载名称空间中的所有挂载点列表文件的符号链接: +``` +$ cat /proc/mounts +proc /proc proc rw,nosuid,nodev,noexec,relatime 0 0 +sys /sys sysfs rw,nosuid,nodev,noexec,relatime 0 0 +dev /dev devtmpfs rw,nosuid,relatime,size=6106264k,nr_inodes=1526566,mode=755 0 0 +... +``` + +#### /proc/modules +当前装入内核的所有模块名称列表,可以由 lsmod 命令使用。其中第一列表示模块名,第二列表示此模块占用内存空间大小,第三列表示此模块有多少实例被装入,第四列表示此模块依赖于其它哪些模块,第五列表示此模块的装载状态:Live(已经装入)、Loading(正在装入)和 Unloading(正在卸载),第六列表示此模块在内核内存(kernel memory)中的偏移量: +``` +$ cat /proc/modules +fuse 118784 3 - Live 0xffffffffc0d9b000 +ccm 20480 3 - Live 0xffffffffc0d95000 +rfcomm 86016 4 - Live 0xffffffffc0d7f000 +bnep 24576 2 - Live 0xffffffffc0d78000 +... +``` + +#### /proc/slabinfo +保存着监视系统中所有活动的 slab 缓存的信息: +``` +$ sudo cat /proc/slabinfo +slabinfo - version: 2.1 +# name : tunables : slabdata +fuse_request 0 20 400 20 2 : tunables 0 0 0 : slabdata 1 1 0 +fuse_inode 1 39 832 39 8 : tunables 0 0 0 : slabdata 1 1 0 +drm_i915_gem_request 765 1036 576 28 4 : tunables 0 0 0 : slabdata 37 37 0 +... +``` + +#### /proc/[pid] +在 /proc 文件系统下,还有一些以数字命名的目录,这些数字是进程的 PID 号,而这些目录是进程目录。目录下的所有文件如下,然后会介绍几个比较重要的: ``` $ cat - & -[1] 2865 -$ ls /proc/2865/ -attr cpuset limits ns root statm -autogroup cwd map_files numa_maps sched status -auxv environ maps oom_adj schedstat syscall -cgroup exe mem oom_score setgroups task -clear_refs fd mountinfo oom_score_adj smaps timers -cmdline fdinfo mounts pagemap smaps_rollup timerslack_ns -comm gid_map mountstats personality stack uid_map -coredump_filter io net projid_map stat wchan - -[1]+ Stopped cat - +[1] 1060 +$ ls /proc/1060/ +attr comm fd maps ns personality smaps syscall +autogroup coredump_filter fdinfo mem numa_maps projid_map smaps_rollup task +auxv cpuset gid_map mountinfo oom_adj root stack timers +cgroup cwd io mounts oom_score sched stat timerslack_ns +clear_refs environ limits mountstats oom_score_adj schedstat statm uid_map +cmdline exe map_files net pagemap setgroups status wchan ``` +#### /proc/[pid]/cmdline +启动当前进程的完整命令: +``` +$ cat /proc/1060/cmdline +cat- +``` + +#### /proc/[pid]/exe +指向启动当前进程的可执行文件的符号链接: +``` +$ file /proc/1060/exe +/proc/1060/exe: symbolic link to /usr/bin/cat +``` + +#### /proc/[pid]/root +当前进程运行根目录的符号链接: +``` +$ file /proc/1060/root +/proc/1060/root: symbolic link to / +``` + +#### /proc/[pid]/mem +当前进程所占用的内存空间,由open、read和lseek等系统调用使用,不能被用户读取。但可通过下面的 /proc/[pid]/maps 查看。 + #### /proc/[pid]/maps 这个文件大概是最常用的,用于显示进程的内存区域映射信息: ``` -$ cat /proc/2865/maps -5580631c6000-5580631ce000 r-xp 00000000 08:01 4981196 /usr/bin/cat -5580633cd000-5580633ce000 r--p 00007000 08:01 4981196 /usr/bin/cat -5580633ce000-5580633cf000 rw-p 00008000 08:01 4981196 /usr/bin/cat -558063c7d000-558063c9e000 rw-p 00000000 00:00 0 [heap] -7f6301cd7000-7f6302027000 r--p 00000000 08:01 4993768 /usr/lib/locale/locale-archive -7f6302027000-7f63021d5000 r-xp 00000000 08:01 4982395 /usr/lib/libc-2.26.so -7f63021d5000-7f63023d5000 ---p 001ae000 08:01 4982395 /usr/lib/libc-2.26.so -7f63023d5000-7f63023d9000 r--p 001ae000 08:01 4982395 /usr/lib/libc-2.26.so -7f63023d9000-7f63023db000 rw-p 001b2000 08:01 4982395 /usr/lib/libc-2.26.so -7f63023db000-7f63023df000 rw-p 00000000 00:00 0 -7f63023df000-7f6302404000 r-xp 00000000 08:01 4982398 /usr/lib/ld-2.26.so -7f63025c1000-7f63025c3000 rw-p 00000000 00:00 0 -7f63025e1000-7f6302603000 rw-p 00000000 00:00 0 -7f6302603000-7f6302604000 r--p 00024000 08:01 4982398 /usr/lib/ld-2.26.so -7f6302604000-7f6302605000 rw-p 00025000 08:01 4982398 /usr/lib/ld-2.26.so -7f6302605000-7f6302606000 rw-p 00000000 00:00 0 -7fff2ab81000-7fff2aba2000 rw-p 00000000 00:00 0 [stack] -7fff2abef000-7fff2abf2000 r--p 00000000 00:00 0 [vvar] -7fff2abf2000-7fff2abf4000 r-xp 00000000 00:00 0 [vdso] +$ cat /proc/1060/maps +56271b3a5000-56271b3ad000 r-xp 00000000 08:01 24904069 /usr/bin/cat +56271b5ac000-56271b5ad000 r--p 00007000 08:01 24904069 /usr/bin/cat +56271b5ad000-56271b5ae000 rw-p 00008000 08:01 24904069 /usr/bin/cat +56271b864000-56271b885000 rw-p 00000000 00:00 0 [heap] +7fefb66cd000-7fefb6a1e000 r--p 00000000 08:01 24912207 /usr/lib/locale/locale-archive +7fefb6a1e000-7fefb6bd1000 r-xp 00000000 08:01 24905238 /usr/lib/libc-2.27.so +7fefb6bd1000-7fefb6dd0000 ---p 001b3000 08:01 24905238 /usr/lib/libc-2.27.so +7fefb6dd0000-7fefb6dd4000 r--p 001b2000 08:01 24905238 /usr/lib/libc-2.27.so +7fefb6dd4000-7fefb6dd6000 rw-p 001b6000 08:01 24905238 /usr/lib/libc-2.27.so +7fefb6dd6000-7fefb6dda000 rw-p 00000000 00:00 0 +7fefb6dda000-7fefb6dff000 r-xp 00000000 08:01 24905239 /usr/lib/ld-2.27.so +7fefb6fbd000-7fefb6fbf000 rw-p 00000000 00:00 0 +7fefb6fdc000-7fefb6ffe000 rw-p 00000000 00:00 0 +7fefb6ffe000-7fefb6fff000 r--p 00024000 08:01 24905239 /usr/lib/ld-2.27.so +7fefb6fff000-7fefb7000000 rw-p 00025000 08:01 24905239 /usr/lib/ld-2.27.so +7fefb7000000-7fefb7001000 rw-p 00000000 00:00 0 +7ffde5659000-7ffde567a000 rw-p 00000000 00:00 0 [stack] +7ffde5748000-7ffde574b000 r--p 00000000 00:00 0 [vvar] +7ffde574b000-7ffde574d000 r-xp 00000000 00:00 0 [vdso] ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0 [vsyscall] ``` #### /proc/[pid]/stack -这个文件表示当前进程的内核调用栈信息: +这个文件表示当前进程的内核调用栈信息,只有在内核编译启用 `CONFIG_STACKTRACE` 选项,才会生成该文件: ``` -$ sudo cat /proc/2865/stack -[] do_signal_stop+0xae/0x1f0 -[] get_signal+0x18c/0x5a0 -[] do_signal+0x36/0x610 -[] exit_to_usermode_loop+0x69/0xa0 -[] syscall_return_slowpath+0x9b/0xb0 -[] entry_SYSCALL_64_fastpath+0x7b/0x7d +$ sudo cat /proc/1060/stack +[] do_signal_stop+0xae/0x1f0 +[] get_signal+0x191/0x580 +[] do_signal+0x36/0x610 +[] exit_to_usermode_loop+0x69/0xa0 +[] do_syscall_64+0xf1/0x100 +[] entry_SYSCALL_64_after_hwframe+0x3d/0xa2 [] 0xffffffffffffffff ``` #### /proc/[pid]/auxv 该文件包含了传递给进程的解释器信息,即 auxv(AUXiliary Vector),每一项都是由一个 unsigned long 长度的 ID 加上一个 unsigned long 长度的值构成: ``` -$ xxd -e -g8 /proc/2865/auxv -00000000: 0000000000000021 00007fff2abf2000 !........ .*.... +$ xxd -e -g8 /proc/1060/auxv +00000000: 0000000000000021 00007ffde574b000 !.........t..... 00000010: 0000000000000010 00000000bfebfbff ................ 00000020: 0000000000000006 0000000000001000 ................ 00000030: 0000000000000011 0000000000000064 ........d....... -00000040: 0000000000000003 00005580631c6040 ........@`.c.U.. +00000040: 0000000000000003 000056271b3a5040 ........@P:.'V.. 00000050: 0000000000000004 0000000000000038 ........8....... 00000060: 0000000000000005 0000000000000009 ................ -00000070: 0000000000000007 00007f63023df000 ..........=.c... +00000070: 0000000000000007 00007fefb6dda000 ................ 00000080: 0000000000000008 0000000000000000 ................ -00000090: 0000000000000009 00005580631c8290 ...........c.U.. +00000090: 0000000000000009 000056271b3a7260 ........`r:.'V.. 000000a0: 000000000000000b 00000000000003e8 ................ 000000b0: 000000000000000c 00000000000003e8 ................ 000000c0: 000000000000000d 00000000000003e8 ................ 000000d0: 000000000000000e 00000000000003e8 ................ 000000e0: 0000000000000017 0000000000000000 ................ -000000f0: 0000000000000019 00007fff2ab9ff39 ........9..*.... +000000f0: 0000000000000019 00007ffde5678349 ........I.g..... 00000100: 000000000000001a 0000000000000000 ................ -00000110: 000000000000001f 00007fff2aba1feb ...........*.... -00000120: 000000000000000f 00007fff2ab9ff49 ........I..*.... +00000110: 000000000000001f 00007ffde5679fef ..........g..... +00000120: 000000000000000f 00007ffde5678359 ........Y.g..... 00000130: 0000000000000000 0000000000000000 ................ ``` 每个值具体是做什么的,可以用下面的办法显示出来,对比看一看,更详细的可以查看 `/usr/include/elf.h` 和 `man ld.so`: ``` $ LD_SHOW_AUXV=1 cat - -AT_SYSINFO_EHDR: 0x7fff6afb3000 +AT_SYSINFO_EHDR: 0x7ffd16be5000 AT_HWCAP: bfebfbff AT_PAGESZ: 4096 AT_CLKTCK: 100 -AT_PHDR: 0x557b68217040 +AT_PHDR: 0x55eb4c59a040 AT_PHENT: 56 AT_PHNUM: 9 -AT_BASE: 0x7f41e5689000 +AT_BASE: 0x7f61506e8000 AT_FLAGS: 0x0 -AT_ENTRY: 0x557b68219290 +AT_ENTRY: 0x55eb4c59c260 AT_UID: 1000 AT_EUID: 1000 AT_GID: 1000 AT_EGID: 1000 AT_SECURE: 0 -AT_RANDOM: 0x7fff6aedc0a9 +AT_RANDOM: 0x7ffd16bd0ce9 AT_HWCAP2: 0x0 -AT_EXECFN: /usr/bin/cat +AT_EXECFN: /bin/cat AT_PLATFORM: x86_64 ``` 值得一提的是,`AT_SYSINFO_EHDR` 所对应的值是一个叫做的 VDSO(Virtual Dynamic Shared Object) 的地址。在 ret2vdso 漏洞利用方法中会用到(参考章节6.1.6)。 @@ -609,84 +803,66 @@ AT_PLATFORM: x86_64 #### /proc/[pid]/environ 该文件包含了进程的环境变量: ``` -$ strings /proc/2865/environ +$ strings /proc/1060/environ +GS_LIB=/home/firmy/.fonts +KDE_FULL_SESSION=true +VIRTUALENVWRAPPER_WORKON_CD=1 +VIRTUALENVWRAPPER_HOOK_DIR=/home/firmy/.virtualenvs +LANG=zh_CN.UTF-8 +... ``` #### /proc/[pid]/fd 该文件包含了进程打开文件的情况: ``` -$ ls -al /proc/2865/fd +$ ls -al /proc/1060/fd total 0 -dr-x------ 2 firmy firmy 0 12月 30 11:13 . -dr-xr-xr-x 9 firmy firmy 0 12月 30 11:13 .. -lrwx------ 1 firmy firmy 64 12月 30 12:31 0 -> /dev/pts/2 -lrwx------ 1 firmy firmy 64 12月 30 12:31 1 -> /dev/pts/2 -lrwx------ 1 firmy firmy 64 12月 30 12:31 2 -> /dev/pts/2 +dr-x------ 2 firmy firmy 0 6月 7 23:37 . +dr-xr-xr-x 9 firmy firmy 0 6月 7 23:37 .. +lrwx------ 1 firmy firmy 64 6月 7 23:44 0 -> /dev/pts/3 +lrwx------ 1 firmy firmy 64 6月 7 23:44 1 -> /dev/pts/3 +lrwx------ 1 firmy firmy 64 6月 7 23:44 2 -> /dev/pts/3 ``` #### /proc/[pid]/status 该文件包含了进程的状态信息: ``` -$ cat /proc/2865/status +$ cat /proc/1060/status Name: cat Umask: 0022 State: T (stopped) -Tgid: 2865 +Tgid: 1060 Ngid: 0 -Pid: 2865 -PPid: 2059 +Pid: 1060 +PPid: 1035 TracerPid: 0 Uid: 1000 1000 1000 1000 Gid: 1000 1000 1000 1000 FDSize: 256 Groups: 3 7 10 56 90 91 93 95 96 98 1000 -NStgid: 2865 -NSpid: 2865 -NSpgid: 2865 -NSsid: 2059 -VmPeak: 7828 kB -VmSize: 7828 kB -VmLck: 0 kB -VmPin: 0 kB -VmHWM: 788 kB -VmRSS: 788 kB -RssAnon: 64 kB -RssFile: 724 kB -RssShmem: 0 kB -VmData: 312 kB -VmStk: 132 kB -VmExe: 32 kB -VmLib: 1876 kB -VmPTE: 40 kB -VmPMD: 12 kB -VmSwap: 0 kB -HugetlbPages: 0 kB -Threads: 1 -SigQ: 2/47723 -SigPnd: 0000000000000000 -ShdPnd: 0000000000000000 -SigBlk: 0000000000000000 -SigIgn: 0000000000000000 -SigCgt: 0000000000000000 -CapInh: 0000000000000000 -CapPrm: 0000000000000000 -CapEff: 0000000000000000 -CapBnd: 0000003fffffffff -CapAmb: 0000000000000000 -NoNewPrivs: 0 -Seccomp: 0 -Cpus_allowed: ff -Cpus_allowed_list: 0-7 -Mems_allowed: 00000001 -Mems_allowed_list: 0 -voluntary_ctxt_switches: 1 -nonvoluntary_ctxt_switches: 0 +... +``` + +#### /proc/[pid]/task +一个目录,包含当前进程的每一个线程的相关信息,每个线程的信息分别放在一个由线程号(tid)命名的目录中: +``` +$ ls /proc/1060/task/ +1060 +$ ls /proc/1060/task/1060/ +attr clear_refs cwd fdinfo maps net oom_score projid_map setgroups stat uid_map +auxv cmdline environ gid_map mem ns oom_score_adj root smaps statm wchan +cgroup comm exe io mountinfo numa_maps pagemap sched smaps_rollup status +children cpuset fd limits mounts oom_adj personality schedstat stack syscall ``` #### /proc/[pid]/syscall 该文件包含了进程正在执行的系统调用: ``` -$ sudo cat /proc/2865/syscall -0 0x0 0x7f63025e2000 0x20000 0x22 0xffffffffffffffff 0x0 0x7fff2ab9f958 0x7f630210ea11 +$ sudo cat /proc/1060/syscall +0 0x0 0x7fefb6fdd000 0x20000 0x22 0xffffffff 0x0 0x7ffde5677d48 0x7fefb6b07901 ``` 第一个值是系统调用号,后面跟着是六个参数,最后两个值分别是堆栈指针和指令计数器的值。 + + +## 参考资料 +- [Linux Filesystem Hierarchy](http://tldp.org/LDP/Linux-Filesystem-Hierarchy/html/proc.html) diff --git a/doc/1.5.8_glibc_malloc.md b/doc/1.5.8_glibc_malloc.md index ec16d19..6506743 100644 --- a/doc/1.5.8_glibc_malloc.md +++ b/doc/1.5.8_glibc_malloc.md @@ -66,6 +66,11 @@ $ ldd a.out /usr/local/glibc-2.23/lib/ld-2.23.so => /usr/lib64/ld-linux-x86-64.so.2 (0x00007f6abdb1c000) ``` +然后如果希望在调试时指定 libc 的源文件,可以使用 gdb 命令 `directory`,但是这种方法的缺点是不能解析子目录,所以推荐使用下面的命令在启动时加载: +``` +$ gdb `find ~/path/to/glibc/source -type d -printf '-d %p '` ./a.out +``` + ## malloc.c 下面我们先分析 glibc 2.23 版本的源码,它是 Ubuntu16.04 的默认版本,在 pwn 中也最常见。然后,我们再探讨新版本的 glibc 中所加入的漏洞缓解机制。 diff --git a/doc/2.3.1_gdb.md b/doc/2.3.1_gdb.md index 977fdc2..4c0ed61 100644 --- a/doc/2.3.1_gdb.md +++ b/doc/2.3.1_gdb.md @@ -242,6 +242,11 @@ run `python2 -c 'print "A"*100'` #### generate-core-file 将调试中的进程生成内核转储文件。 +#### directory -- dir +设置查找源文件的路径。 + +或者使用 gdb 的 `-d` 参数,例如:`gdb a.out -d /search/code/` + ## gdb-peda 当 gdb 启动时,它会在当前用户的主目录中寻找一个名为 `.gdbinit` 的文件;如果该文件存在,则 gdb 就执行该文件中的所有命令。通常,该文件用于简单的配置命令。但是 `.gdbinit` 的配置十分繁琐,因此对 gdb 的扩展通常用插件的方式来实现,通过 python 的脚本可以很方便的实现需要的功能。 diff --git a/doc/3.1.14_vm_escape.md b/doc/3.1.14_vm_escape.md new file mode 100644 index 0000000..f3dd155 --- /dev/null +++ b/doc/3.1.14_vm_escape.md @@ -0,0 +1 @@ +# 3.1.14 虚拟机逃逸 diff --git a/doc/3_topics.md b/doc/3_topics.md index 26768fc..1e55ecf 100644 --- a/doc/3_topics.md +++ b/doc/3_topics.md @@ -14,6 +14,7 @@ * [3.1.11 Linux 内核漏洞利用](3.1.11_linux_kernel_exploit.md) * [3.1.12 Windows 内核漏洞利用](3.1.12_windows_kernel_exploit.md) * [3.1.13 竞争条件](3.1.13_race_condition.md) + * [3.1.14 虚拟机逃逸](3.1.14_vm_escape.md) * Reverse * [3.2.1 patch 二进制文件](3.2.1_patch_binary.md) * [3.2.2 脱壳技术(PE)](3.2.2_pe_unpack.md) diff --git a/doc/4.12_stack_chk_fail.md b/doc/4.12_stack_chk_fail.md index e655d11..c22c025 100644 --- a/doc/4.12_stack_chk_fail.md +++ b/doc/4.12_stack_chk_fail.md @@ -4,6 +4,7 @@ - [libc 2.23](#libc-2.23) - [CTF 实例](#ctf-实例) - [libc 2.25](#libc-2.25) +- [参考资料](#参考资料) ## 回顾 canary @@ -197,3 +198,7 @@ argv[0]: ./a.out *** stack smashing detected ***: terminated Aborted (core dumped) ``` + + +## 参考资料 +- [Adventure with Stack Smashing Protector (SSP)](http://site.pi3.com.pl/papers/ASSP.pdf) diff --git a/doc/4.14_glibc_tcache.md b/doc/4.14_glibc_tcache.md index 3d3c4ed..ccb9c6d 100644 --- a/doc/4.14_glibc_tcache.md +++ b/doc/4.14_glibc_tcache.md @@ -3,6 +3,7 @@ - [tcache](#tcache) - [安全性分析](#安全性分析) - [CTF 实例](#ctf-实例) +- [CVE-2017-17426](#cve-2017-17426) - [参考资料](#参考资料) @@ -674,6 +675,71 @@ gdb-peda$ x/12gx 0x0000555555756000+0x10 在最近的 CTF 中,已经开始尝试使用 libc-2.26,比如章节 6.1.15、6.1.19 中的例子。 +## CVE-2017-17426 +libc-2.26 中的 tcache 机制被发现了安全漏洞,由于 `__libc_malloc()` 使用 `request2size()` 来将所请求的分配大小转换为计算块大小,该函数不会进行整数溢出检查。所以如果请求一个非常大的堆块(接近 `SIZE_MAX`),将会导致整数溢出,从而导致 malloc 错误地返回了 tcache bin 里的堆块。 + +一个例子: +```c +#include +#include + +int main() { + void *x = malloc(10); + printf("malloc(10): %p\n", x); + free(x); + + void *y = malloc(((size_t)~0) - 2); // overflow allocation (size_t.max-2) + printf("malloc(((size_t)~0) - 2): %p\n", y); +} +``` +``` +$ gcc cve201717426.c +$ /usr/local/glibc-2.26/lib/ld-2.26.so ./a.out +malloc(10): 0x7f3f945ed260 +malloc(((size_t)~0) - 2): 0x7f3f945ed260 +$ /usr/local/glibc-2.27/lib/ld-2.27.so ./a.out +malloc(10): 0x7f399c69e260 +malloc(((size_t)~0) - 2): (nil) +``` +可以看到在使用 libc-2.26 时,第二次 malloc 返回了第一次 free 的堆块。而在使用 libc-2.27 时返回 NULL,说明该问题已被修复。 + +#### patch +该漏洞在 libc-2.27 的这次 [commit](https://sourceware.org/git/gitweb.cgi?p=glibc.git;h=34697694e8a93b325b18f25f7dcded55d6baeaf6) 中被修复。方法是用更安全的 `checked_request2size()` 替换 `request2size()`,以实现对整数溢出的检查: +```diff +$ git show 34697694e8a93b325b18f25f7dcded55d6baeaf6 malloc/malloc.c | cat +commit 34697694e8a93b325b18f25f7dcded55d6baeaf6 +Author: Arjun Shankar +Date: Thu Nov 30 13:31:45 2017 +0100 + + Fix integer overflow in malloc when tcache is enabled [BZ #22375] + + When the per-thread cache is enabled, __libc_malloc uses request2size (which + does not perform an overflow check) to calculate the chunk size from the + requested allocation size. This leads to an integer overflow causing malloc + to incorrectly return the last successfully allocated block when called with + a very large size argument (close to SIZE_MAX). + + This commit uses checked_request2size instead, removing the overflow. + +diff --git a/malloc/malloc.c b/malloc/malloc.c +index 79f0e9eac7..0c9e0748b4 100644 +--- a/malloc/malloc.c ++++ b/malloc/malloc.c +@@ -3031,7 +3031,8 @@ __libc_malloc (size_t bytes) + return (*hook)(bytes, RETURN_ADDRESS (0)); + #if USE_TCACHE + /* int_free also calls request2size, be careful to not pad twice. */ +- size_t tbytes = request2size (bytes); ++ size_t tbytes; ++ checked_request2size (bytes, tbytes); + size_t tc_idx = csize2tidx (tbytes); + + MAYBE_INIT_TCACHE (); +``` + + ## 参考资料 - [thread local caching in glibc malloc](http://tukan.farm/2017/07/08/tcache/) - [MallocInternals](https://sourceware.org/glibc/wiki/MallocInternals) +- [CVE-2017-17426](https://sourceware.org/bugzilla/show_bug.cgi?id=22375) +- [CVE-2017-17426 Detail](https://nvd.nist.gov/vuln/detail/CVE-2017-17426) diff --git a/doc/4.1_linux_kernel_debug.md b/doc/4.1_linux_kernel_debug.md index dc2c4f5..f09d5a5 100644 --- a/doc/4.1_linux_kernel_debug.md +++ b/doc/4.1_linux_kernel_debug.md @@ -173,7 +173,7 @@ $ wget -c http://busybox.net/downloads/busybox-1.28.3.tar.bz2 $ tar -xvjf busybox-1.28.3.tar.bz2 $ cd busybox-1.28.3/ ``` -生成默认配置文件并修改 `CONFIG_STATIC=y` 让它生成的是一个静态链接的 BusyBox: +生成默认配置文件并修改 `CONFIG_STATIC=y` 让它生成的是一个静态链接的 BusyBox,这是因为 qemu 中没有动态链接库: ``` $ make defconfig $ cat .config | grep "CONFIG_STATIC" diff --git a/doc/4.4_gcc_sec.md b/doc/4.4_gcc_sec.md index c5794fa..de34f93 100644 --- a/doc/4.4_gcc_sec.md +++ b/doc/4.4_gcc_sec.md @@ -10,7 +10,7 @@ Linux 中有各种各样的安全防护,其中 ASLR 是由内核直接提供的,通过系统配置文件控制。NX,Canary,PIE,RELRO 等需要在编译时根据各项参数开启或关闭。未指定参数时,使用默认设置。 #### CANARY -启用 CANARY 后,函数开始执行的时候会先往栈里插入 canary 信息,当函数返回时验证插入的 canary 是否被修改,如果是,就停止运行。 +启用 CANARY 后,函数开始执行的时候会先往栈里插入 canary 信息,当函数返回时验证插入的 canary 是否被修改,如果是,则说明发生了栈溢出,程序停止运行。 下面是一个例子: ```c @@ -150,7 +150,7 @@ No-eXecute,表示不可执行,其原理是将数据所在的内存页标识 但这种保护并不能阻止攻击者通过代码重用来进行攻击(ret2libc)。 #### PIE -PIE(Position Independent Executable)需要配合 ASLR 来使用,以达到可执行文件的加载时地址随机化。简单来说,PIE 是编译时随机化,由编译器完成;ASLR 是加载时随机化,由操作系统完成。ASLR 将程序运行时的堆栈以及共享库的加载地址随机化,而 PIE 在编译时将程序编译为位置无关、、即程序运行时各个段加载的虚拟地址在装载时确定。开启 PIE 时,编译生成的是动态库文件(Shared object)文件,而关闭 PIE 后生成可执行文件(Executable)。 +PIE(Position Independent Executable)需要配合 ASLR 来使用,以达到可执行文件的加载时地址随机化。简单来说,PIE 是编译时随机化,由编译器完成;ASLR 是加载时随机化,由操作系统完成。ASLR 将程序运行时的堆栈以及共享库的加载地址随机化,而 PIE 在编译时将程序编译为位置无关、即程序运行时各个段加载的虚拟地址在装载时确定。开启 PIE 时,编译生成的是动态库文件(Shared object)文件,而关闭 PIE 后生成可执行文件(Executable)。 我们通过实际例子来探索一下 PIE 和 ASLR: ```c diff --git a/doc/6.1.32_pwn_secconctf2017_vm_no_fun.md b/doc/6.1.32_pwn_secconctf2017_vm_no_fun.md new file mode 100644 index 0000000..14d9336 --- /dev/null +++ b/doc/6.1.32_pwn_secconctf2017_vm_no_fun.md @@ -0,0 +1,30 @@ +# 6.1.32 pwn SECCONCTF2017 vm_no_fun + +- [题目复现](#题目复现) +- [题目解析](#题目解析) +- [漏洞利用](#漏洞利用) +- [参考资料](#参考资料) + + +[下载文件](../src/writeup/6.1.32_pwn_secconctf2017_vm_no_fun) + +## 题目复现 +``` +$ file inception +inception: ELF 64-bit LSB pie executable x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=c36d0c2ef8cae7c5166fa8e3cc30a229f97968c3, stripped +$ checksec -f inception +RELRO STACK CANARY NX PIE RPATH RUNPATH FORTIFY Fortified Fortifiable FILE +Partial RELRO Canary found NX enabled PIE enabled No RPATH No RUNPATH Yes 0 3 inception +$ strings libc-2.23.so | grep "GNU C" +GNU C Library (Ubuntu GLIBC 2.23-0ubuntu9) stable release version 2.23, by Roland McGrath et al. +Compiled by GNU CC version 5.4.0 20160609. +``` +64 位程序,开启了 canary、NX 和 PIE,默认开启 ASLR。 + + +## 题目解析 + +## 漏洞利用 + +## 参考资料 +- https://github.com/SECCON/SECCON2017_online_CTF/tree/master/pwn/500_vm_no_fun diff --git a/doc/6.1.33_pwn_34c3ctf2017_lfa.md b/doc/6.1.33_pwn_34c3ctf2017_lfa.md new file mode 100644 index 0000000..4d0d6f2 --- /dev/null +++ b/doc/6.1.33_pwn_34c3ctf2017_lfa.md @@ -0,0 +1,19 @@ +# 6.1.33 pwn 34C3CTF2017 LFA + +- [题目复现](#题目复现) +- [题目解析](#题目解析) +- [漏洞利用](#漏洞利用) +- [参考资料](#参考资料) + + +[下载文件](../src/writeup/6.1.33_pwn_34c3ctf2017_lfa) + +## 题目复现 + +## 题目解析 + +## 漏洞利用 + +## 参考资料 +- https://ctftime.org/task/5167 +- https://github.com/bkth/34c3ctf/tree/master/LFA diff --git a/doc/6_writeup.md b/doc/6_writeup.md index bc517de..00c3242 100644 --- a/doc/6_writeup.md +++ b/doc/6_writeup.md @@ -32,6 +32,8 @@ * [6.1.29 pwn Insomni'hack_teaserCTF2017 The_Great_Escape_part-3](6.1.29_pwn_insomnictf2017_the_great_escape3.md) * [6.1.30 pwn HITCONCTF2017 Ghost_in_the_heap](6.1.30_pwn_hitconctf2017_ghost_in_the_heap.md) * [6.1.31 pwn HITBCTF2018 mutepig](6.1.31_pwn_hitbctf2018_mutepig.md) + * [6.1.32 pwn SECCONCTF2017 vm_no_fun](6.1.32_pwn_secconctf2017_vm_no_fun.md) + * [6.1.33 pwn 34C3CTF2017 LFA](6.1.33_pwn_34c3ctf2017_lfa.md) * Reverse * [6.2.1 re XHPCTF2017 dont_panic](6.2.1_re_xhpctf2017_dont_panic.md) * [6.2.2 re ECTF2016 tayy](6.2.2_re_ectf2016_tayy.md) diff --git a/doc/7.1.8_adobe_reader_2010-2883.md b/doc/7.1.8_adobe_reader_2010-2883.md index 1684422..8ee991d 100644 --- a/doc/7.1.8_adobe_reader_2010-2883.md +++ b/doc/7.1.8_adobe_reader_2010-2883.md @@ -28,6 +28,7 @@ msf > search cve-2010-2883 exploit/windows/fileformat/adobe_cooltype_sing 2010-09-07 great Adobe CoolType SING Table "uniqueName" Stack Buffer Overflow msf > use exploit/windows/fileformat/adobe_cooltype_sing +msf exploit(windows/fileformat/adobe_cooltype_sing) > show info msf exploit(windows/fileformat/adobe_cooltype_sing) > set payload windows/exec payload => windows/exec diff --git a/doc/7.1.9_ms_word_2010-3333.md b/doc/7.1.9_ms_word_2010-3333.md index f1a3956..1a8f63f 100644 --- a/doc/7.1.9_ms_word_2010-3333.md +++ b/doc/7.1.9_ms_word_2010-3333.md @@ -20,7 +20,26 @@ cve-2010-3333 漏洞是一个栈溢出漏洞,该漏洞是由于 Microsoft Offi | 反汇编器 | IDA Pro | 版本号:7.0 | | 漏洞软件 | MS Office | 版本号:2003 SP3 | -我们利用 Metasploit 来生成攻击样本 +我们利用 Metasploit 来生成拒绝服务攻击样本: +``` +msf > search cve-2010-3333 + Name Disclosure Date Rank Description + ---- --------------- ---- ----------- + exploit/windows/fileformat/ms10_087_rtf_pfragments_bof 2010-11-09 great MS10-087 Microsoft Word RTF pFragments Stack Buffer Overflow (File Format) + +msf > use exploit/windows/fileformat/ms10_087_rtf_pfragments_bof +msf exploit(windows/fileformat/ms10_087_rtf_pfragments_bof) > show info + +msf exploit(windows/fileformat/ms10_087_rtf_pfragments_bof) > set target 6 +target => 6 + +msf exploit(windows/fileformat/ms10_087_rtf_pfragments_bof) > set filename cve20103333.rtf +filename => cve20103333.rtf + +msf exploit(windows/fileformat/ms10_087_rtf_pfragments_bof) > exploit +[*] Creating 'cve20103333.rtf' file ... +[+] cve20103333.rtf stored at /home/firmy/.msf4/local/cve20103333.rtf +``` ## 漏洞分析 diff --git a/doc/8.30_prevent_brute_force_canary.md b/doc/8.30_prevent_brute_force_canary.md new file mode 100644 index 0000000..5af97a0 --- /dev/null +++ b/doc/8.30_prevent_brute_force_canary.md @@ -0,0 +1 @@ +# 8.30 Preventing brute force attacks against stack canary protection on networking servers diff --git a/doc/8_academic.md b/doc/8_academic.md index b4001a2..901365a 100644 --- a/doc/8_academic.md +++ b/doc/8_academic.md @@ -32,3 +32,4 @@ * [8.27 Firmalice - Automatic Detection of Authentication Bypass Vulnerabilities in Binary Firmware](8.27_firmalice_bypass.md) * [8.28 Cross-Architecture Bug Search in Binary Executables](8.28_cross_arch_bug_search.md) * [8.29 Dynamic Hooks: Hiding Control Flow Changes within Non-Control Data](8.29_dynamic_hooks.md) +* [8.30 Preventing brute force attacks against stack canary protection on networking servers](8.30_prevent_brute_force_canary.md) diff --git a/src/others/4.14_glibc_tcache/cve201717426.c b/src/others/4.14_glibc_tcache/cve201717426.c new file mode 100644 index 0000000..0cd1d1e --- /dev/null +++ b/src/others/4.14_glibc_tcache/cve201717426.c @@ -0,0 +1,11 @@ +#include +#include + +int main() { + void *x = malloc(10); + printf("malloc(10): %p\n", x); + free(x); + + void *y = malloc(((size_t)~0) - 2); // overflow allocation (size_t.max-2) + printf("malloc(((size_t)~0) - 2): %p\n", y); +} diff --git a/src/writeup/6.1.32_pwn_secconctf2017_vm_no_fun/inception b/src/writeup/6.1.32_pwn_secconctf2017_vm_no_fun/inception new file mode 100755 index 0000000..7474cf3 Binary files /dev/null and b/src/writeup/6.1.32_pwn_secconctf2017_vm_no_fun/inception differ diff --git a/src/writeup/6.1.32_pwn_secconctf2017_vm_no_fun/libc-2.23.so b/src/writeup/6.1.32_pwn_secconctf2017_vm_no_fun/libc-2.23.so new file mode 100755 index 0000000..0101d5c Binary files /dev/null and b/src/writeup/6.1.32_pwn_secconctf2017_vm_no_fun/libc-2.23.so differ diff --git a/src/writeup/6.1.33_pwn_34c3ctf2017_lfa/LFA.so b/src/writeup/6.1.33_pwn_34c3ctf2017_lfa/LFA.so new file mode 100755 index 0000000..50bb7d5 Binary files /dev/null and b/src/writeup/6.1.33_pwn_34c3ctf2017_lfa/LFA.so differ diff --git a/src/writeup/6.1.33_pwn_34c3ctf2017_lfa/README.txt b/src/writeup/6.1.33_pwn_34c3ctf2017_lfa/README.txt new file mode 100644 index 0000000..54505c4 --- /dev/null +++ b/src/writeup/6.1.33_pwn_34c3ctf2017_lfa/README.txt @@ -0,0 +1,14 @@ +The server runs on ubuntu/latest + +to build the same version of ruby do the following steps: + +git clone https://github.com/ruby/ruby.git +cd ruby +git checkout a5ec07c73fb667378ed617da6031381ee2d832b0 +git apply ../sandbox_patch +autoconf +./configure +make install +mv LFA.so /usr/local/lib/ruby/site_ruby/2.4.0/x86_64-linux/LFA.so + +then check that ruby 'sample.rb' runs properly (if you have ruby pre-installed on the machine check that you are running the right version of ruby) diff --git a/src/writeup/6.1.33_pwn_34c3ctf2017_lfa/libc-2.26.so b/src/writeup/6.1.33_pwn_34c3ctf2017_lfa/libc-2.26.so new file mode 100755 index 0000000..3dc5ae5 Binary files /dev/null and b/src/writeup/6.1.33_pwn_34c3ctf2017_lfa/libc-2.26.so differ diff --git a/src/writeup/6.1.33_pwn_34c3ctf2017_lfa/sample.rb b/src/writeup/6.1.33_pwn_34c3ctf2017_lfa/sample.rb new file mode 100644 index 0000000..0a9f9c8 --- /dev/null +++ b/src/writeup/6.1.33_pwn_34c3ctf2017_lfa/sample.rb @@ -0,0 +1,9 @@ +require 'LFA' + +$arr = LFA.new +$arr[1] = 11 +$arr[5] = 11 +$arr[15000] = 11 +puts $arr.sum + + diff --git a/src/writeup/6.1.33_pwn_34c3ctf2017_lfa/sandbox_patch b/src/writeup/6.1.33_pwn_34c3ctf2017_lfa/sandbox_patch new file mode 100644 index 0000000..016c9fc --- /dev/null +++ b/src/writeup/6.1.33_pwn_34c3ctf2017_lfa/sandbox_patch @@ -0,0 +1,433 @@ +diff --git a/io.c b/io.c +index ee3ea3e68a..f53b4190cc 100644 +--- a/io.c ++++ b/io.c +@@ -9388,6 +9388,419 @@ rb_io_fcntl(int argc, VALUE *argv, VALUE io) + #define rb_io_fcntl rb_f_notimplement + #endif + ++ ++/* ------------SECCOMP--------------- */ ++ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++ ++ ++#include /* for __BITS_PER_LONG */ ++#include ++#include ++#include ++#include /* for seccomp_data */ ++#include ++#include ++#include ++ ++#define BPF_LABELS_MAX 256 ++struct bpf_labels { ++ int count; ++ struct __bpf_label { ++ const char *label; ++ __u32 location; ++ } labels[BPF_LABELS_MAX]; ++}; ++ ++ ++#define JUMP_JT 0xff ++#define JUMP_JF 0xff ++#define LABEL_JT 0xfe ++#define LABEL_JF 0xfe ++ ++#if defined(__i386__) ++# define REG_SYSCALL REG_EAX ++# define ARCH_NR AUDIT_ARCH_I386 ++#elif defined(__x86_64__) ++# define REG_SYSCALL REG_RAX ++# define ARCH_NR AUDIT_ARCH_X86_64 ++#else ++# warning "Platform does not support seccomp filter yet" ++# define REG_SYSCALL 0 ++# define ARCH_NR 0 ++#endif ++ ++#define arch_nr (offsetof(struct seccomp_data, arch)) ++ ++#define VALIDATE_ARCHITECTURE \ ++ BPF_STMT(BPF_LD+BPF_W+BPF_ABS, arch_nr), \ ++ BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, ARCH_NR, 1, 0), \ ++ BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_KILL) ++ ++ ++#define ALLOW \ ++ BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_ALLOW) ++#define DENY \ ++ BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_KILL) ++#define JUMP(labels, label) \ ++ BPF_JUMP(BPF_JMP+BPF_JA, FIND_LABEL((labels), (label)), \ ++ JUMP_JT, JUMP_JF) ++#define LABEL(labels, label) \ ++ BPF_JUMP(BPF_JMP+BPF_JA, FIND_LABEL((labels), (label)), \ ++ LABEL_JT, LABEL_JF) ++#define SECCOMP_SYSCALL(nr, jt) \ ++ BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, (nr), 0, 1), \ ++ jt ++ ++/* Lame, but just an example */ ++#define FIND_LABEL(labels, label) seccomp_bpf_label((labels), #label) ++ ++#define EXPAND(...) __VA_ARGS__ ++ ++/* Ensure that we load the logically correct offset. */ ++#if __BYTE_ORDER == __LITTLE_ENDIAN ++#define LO_ARG(idx) offsetof(struct seccomp_data, args[(idx)]) ++#elif __BYTE_ORDER == __BIG_ENDIAN ++#define LO_ARG(idx) offsetof(struct seccomp_data, args[(idx)]) + sizeof(__u32) ++#else ++#error "Unknown endianness" ++#endif ++ ++/* Map all width-sensitive operations */ ++#if __BITS_PER_LONG == 32 ++ ++#define JEQ(x, jt) JEQ32(x, EXPAND(jt)) ++#define JNE(x, jt) JNE32(x, EXPAND(jt)) ++#define JGT(x, jt) JGT32(x, EXPAND(jt)) ++#define JLT(x, jt) JLT32(x, EXPAND(jt)) ++#define JGE(x, jt) JGE32(x, EXPAND(jt)) ++#define JLE(x, jt) JLE32(x, EXPAND(jt)) ++#define JA(x, jt) JA32(x, EXPAND(jt)) ++#define ARG(i) ARG_32(i) ++ ++#elif __BITS_PER_LONG == 64 ++ ++/* Ensure that we load the logically correct offset. */ ++#if __BYTE_ORDER == __LITTLE_ENDIAN ++#define ENDIAN(_lo, _hi) _lo, _hi ++#define HI_ARG(idx) offsetof(struct seccomp_data, args[(idx)]) + sizeof(__u32) ++#elif __BYTE_ORDER == __BIG_ENDIAN ++#define ENDIAN(_lo, _hi) _hi, _lo ++#define HI_ARG(idx) offsetof(struct seccomp_data, args[(idx)]) ++#endif ++ ++union arg64 { ++ struct { ++ __u32 ENDIAN(lo32, hi32); ++ }; ++ __u64 u64; ++}; ++ ++#define JEQ(x, jt) \ ++ JEQ64(((union arg64){.u64 = (x)}).lo32, \ ++ ((union arg64){.u64 = (x)}).hi32, \ ++ EXPAND(jt)) ++#define JGT(x, jt) \ ++ JGT64(((union arg64){.u64 = (x)}).lo32, \ ++ ((union arg64){.u64 = (x)}).hi32, \ ++ EXPAND(jt)) ++#define JGE(x, jt) \ ++ JGE64(((union arg64){.u64 = (x)}).lo32, \ ++ ((union arg64){.u64 = (x)}).hi32, \ ++ EXPAND(jt)) ++#define JNE(x, jt) \ ++ JNE64(((union arg64){.u64 = (x)}).lo32, \ ++ ((union arg64){.u64 = (x)}).hi32, \ ++ EXPAND(jt)) ++#define JLT(x, jt) \ ++ JLT64(((union arg64){.u64 = (x)}).lo32, \ ++ ((union arg64){.u64 = (x)}).hi32, \ ++ EXPAND(jt)) ++#define JLE(x, jt) \ ++ JLE64(((union arg64){.u64 = (x)}).lo32, \ ++ ((union arg64){.u64 = (x)}).hi32, \ ++ EXPAND(jt)) ++ ++#define JA(x, jt) \ ++ JA64(((union arg64){.u64 = (x)}).lo32, \ ++ ((union arg64){.u64 = (x)}).hi32, \ ++ EXPAND(jt)) ++#define ARG(i) ARG_64(i) ++ ++#else ++#error __BITS_PER_LONG value unusable. ++#endif ++ ++/* Loads the arg into A */ ++#define ARG_32(idx) \ ++ BPF_STMT(BPF_LD+BPF_W+BPF_ABS, LO_ARG(idx)) ++ ++/* Loads lo into M[0] and hi into M[1] and A */ ++#define ARG_64(idx) \ ++ BPF_STMT(BPF_LD+BPF_W+BPF_ABS, LO_ARG(idx)), \ ++ BPF_STMT(BPF_ST, 0), /* lo -> M[0] */ \ ++ BPF_STMT(BPF_LD+BPF_W+BPF_ABS, HI_ARG(idx)), \ ++ BPF_STMT(BPF_ST, 1) /* hi -> M[1] */ ++ ++#define JEQ32(value, jt) \ ++ BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, (value), 0, 1), \ ++ jt ++ ++#define JNE32(value, jt) \ ++ BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, (value), 1, 0), \ ++ jt ++ ++#define JA32(value, jt) \ ++ BPF_JUMP(BPF_JMP+BPF_JSET+BPF_K, (value), 0, 1), \ ++ jt ++ ++#define JGE32(value, jt) \ ++ BPF_JUMP(BPF_JMP+BPF_JGE+BPF_K, (value), 0, 1), \ ++ jt ++ ++#define JGT32(value, jt) \ ++ BPF_JUMP(BPF_JMP+BPF_JGT+BPF_K, (value), 0, 1), \ ++ jt ++ ++#define JLE32(value, jt) \ ++ BPF_JUMP(BPF_JMP+BPF_JGT+BPF_K, (value), 1, 0), \ ++ jt ++ ++#define JLT32(value, jt) \ ++ BPF_JUMP(BPF_JMP+BPF_JGE+BPF_K, (value), 1, 0), \ ++ jt ++ ++/* ++ * All the JXX64 checks assume lo is saved in M[0] and hi is saved in both ++ * A and M[1]. This invariant is kept by restoring A if necessary. ++ */ ++#define JEQ64(lo, hi, jt) \ ++ /* if (hi != arg.hi) goto NOMATCH; */ \ ++ BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, (hi), 0, 5), \ ++ BPF_STMT(BPF_LD+BPF_MEM, 0), /* swap in lo */ \ ++ /* if (lo != arg.lo) goto NOMATCH; */ \ ++ BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, (lo), 0, 2), \ ++ BPF_STMT(BPF_LD+BPF_MEM, 1), \ ++ jt, \ ++ BPF_STMT(BPF_LD+BPF_MEM, 1) ++ ++#define JNE64(lo, hi, jt) \ ++ /* if (hi != arg.hi) goto MATCH; */ \ ++ BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, (hi), 0, 3), \ ++ BPF_STMT(BPF_LD+BPF_MEM, 0), \ ++ /* if (lo != arg.lo) goto MATCH; */ \ ++ BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, (lo), 2, 0), \ ++ BPF_STMT(BPF_LD+BPF_MEM, 1), \ ++ jt, \ ++ BPF_STMT(BPF_LD+BPF_MEM, 1) ++ ++#define JA64(lo, hi, jt) \ ++ /* if (hi & arg.hi) goto MATCH; */ \ ++ BPF_JUMP(BPF_JMP+BPF_JSET+BPF_K, (hi), 3, 0), \ ++ BPF_STMT(BPF_LD+BPF_MEM, 0), \ ++ /* if (lo & arg.lo) goto MATCH; */ \ ++ BPF_JUMP(BPF_JMP+BPF_JSET+BPF_K, (lo), 0, 2), \ ++ BPF_STMT(BPF_LD+BPF_MEM, 1), \ ++ jt, \ ++ BPF_STMT(BPF_LD+BPF_MEM, 1) ++ ++#define JGE64(lo, hi, jt) \ ++ /* if (hi > arg.hi) goto MATCH; */ \ ++ BPF_JUMP(BPF_JMP+BPF_JGT+BPF_K, (hi), 4, 0), \ ++ /* if (hi != arg.hi) goto NOMATCH; */ \ ++ BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, (hi), 0, 5), \ ++ BPF_STMT(BPF_LD+BPF_MEM, 0), \ ++ /* if (lo >= arg.lo) goto MATCH; */ \ ++ BPF_JUMP(BPF_JMP+BPF_JGE+BPF_K, (lo), 0, 2), \ ++ BPF_STMT(BPF_LD+BPF_MEM, 1), \ ++ jt, \ ++ BPF_STMT(BPF_LD+BPF_MEM, 1) ++ ++#define JGT64(lo, hi, jt) \ ++ /* if (hi > arg.hi) goto MATCH; */ \ ++ BPF_JUMP(BPF_JMP+BPF_JGT+BPF_K, (hi), 4, 0), \ ++ /* if (hi != arg.hi) goto NOMATCH; */ \ ++ BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, (hi), 0, 5), \ ++ BPF_STMT(BPF_LD+BPF_MEM, 0), \ ++ /* if (lo > arg.lo) goto MATCH; */ \ ++ BPF_JUMP(BPF_JMP+BPF_JGT+BPF_K, (lo), 0, 2), \ ++ BPF_STMT(BPF_LD+BPF_MEM, 1), \ ++ jt, \ ++ BPF_STMT(BPF_LD+BPF_MEM, 1) ++ ++#define JLE64(lo, hi, jt) \ ++ /* if (hi < arg.hi) goto MATCH; */ \ ++ BPF_JUMP(BPF_JMP+BPF_JGE+BPF_K, (hi), 0, 4), \ ++ /* if (hi != arg.hi) goto NOMATCH; */ \ ++ BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, (hi), 0, 5), \ ++ BPF_STMT(BPF_LD+BPF_MEM, 0), \ ++ /* if (lo <= arg.lo) goto MATCH; */ \ ++ BPF_JUMP(BPF_JMP+BPF_JGT+BPF_K, (lo), 2, 0), \ ++ BPF_STMT(BPF_LD+BPF_MEM, 1), \ ++ jt, \ ++ BPF_STMT(BPF_LD+BPF_MEM, 1) ++ ++#define JLT64(lo, hi, jt) \ ++ /* if (hi < arg.hi) goto MATCH; */ \ ++ BPF_JUMP(BPF_JMP+BPF_JGE+BPF_K, (hi), 0, 4), \ ++ /* if (hi != arg.hi) goto NOMATCH; */ \ ++ BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, (hi), 0, 5), \ ++ BPF_STMT(BPF_LD+BPF_MEM, 0), \ ++ /* if (lo < arg.lo) goto MATCH; */ \ ++ BPF_JUMP(BPF_JMP+BPF_JGE+BPF_K, (lo), 2, 0), \ ++ BPF_STMT(BPF_LD+BPF_MEM, 1), \ ++ jt, \ ++ BPF_STMT(BPF_LD+BPF_MEM, 1) ++ ++#define LOAD_SYSCALL_NR \ ++ BPF_STMT(BPF_LD+BPF_W+BPF_ABS, \ ++ offsetof(struct seccomp_data, nr)) ++ ++ ++int bpf_resolve_jumps(struct bpf_labels *labels, ++ struct sock_filter *filter, size_t count) ++{ ++ size_t i; ++ ++ if (count < 1 || count > BPF_MAXINSNS) ++ return -1; ++ /* ++ * Walk it once, backwards, to build the label table and do fixups. ++ * Since backward jumps are disallowed by BPF, this is easy. ++ */ ++ for (i = 0; i < count; ++i) { ++ size_t offset = count - i - 1; ++ struct sock_filter *instr = &filter[offset]; ++ if (instr->code != (BPF_JMP+BPF_JA)) ++ continue; ++ switch ((instr->jt<<8)|instr->jf) { ++ case (JUMP_JT<<8)|JUMP_JF: ++ if (labels->labels[instr->k].location == 0xffffffff) { ++ fprintf(stderr, "Unresolved label: '%s'\n", ++ labels->labels[instr->k].label); ++ return 1; ++ } ++ instr->k = labels->labels[instr->k].location - ++ (offset + 1); ++ instr->jt = 0; ++ instr->jf = 0; ++ continue; ++ case (LABEL_JT<<8)|LABEL_JF: ++ if (labels->labels[instr->k].location != 0xffffffff) { ++ fprintf(stderr, "Duplicate label use: '%s'\n", ++ labels->labels[instr->k].label); ++ return 1; ++ } ++ labels->labels[instr->k].location = offset; ++ instr->k = 0; /* fall through */ ++ instr->jt = 0; ++ instr->jf = 0; ++ continue; ++ } ++ } ++ return 0; ++} ++ ++/* Simple lookup table for labels. */ ++__u32 seccomp_bpf_label(struct bpf_labels *labels, const char *label) ++{ ++ struct __bpf_label *begin = labels->labels, *end; ++ int id; ++ ++ if (labels->count == BPF_LABELS_MAX) { ++ fprintf(stderr, "Too many labels\n"); ++ exit(1); ++ } ++ if (labels->count == 0) { ++ begin->label = label; ++ begin->location = 0xffffffff; ++ labels->count++; ++ return 0; ++ } ++ end = begin + labels->count; ++ for (id = 0; begin < end; ++begin, ++id) { ++ if (!strcmp(label, begin->label)) ++ return id; ++ } ++ begin->label = label; ++ begin->location = 0xffffffff; ++ labels->count++; ++ return id; ++} ++ ++void seccomp_bpf_print(struct sock_filter *filter, size_t count) ++{ ++ struct sock_filter *end = filter + count; ++ for ( ; filter < end; ++filter) ++ printf("{ code=%u,jt=%u,jf=%u,k=%u },\n", ++ filter->code, filter->jt, filter->jf, filter->k); ++} ++ ++void ++init_seccomp() { ++ struct bpf_labels l = { ++ .count = 0, ++ }; ++ struct sock_filter filter[] = { ++ VALIDATE_ARCHITECTURE, ++ LOAD_SYSCALL_NR, ++ SECCOMP_SYSCALL(__NR_exit, ALLOW), ++ SECCOMP_SYSCALL(__NR_exit_group, ALLOW), ++ SECCOMP_SYSCALL(__NR_brk, ALLOW), ++ SECCOMP_SYSCALL(__NR_mmap, JUMP(&l, mmap)), ++ SECCOMP_SYSCALL(__NR_munmap, ALLOW), ++ SECCOMP_SYSCALL(__NR_mremap, ALLOW), ++ SECCOMP_SYSCALL(__NR_readv, ALLOW), ++ SECCOMP_SYSCALL(__NR_futex, ALLOW), ++ SECCOMP_SYSCALL(__NR_close, ALLOW), ++ SECCOMP_SYSCALL(__NR_write, JUMP(&l, write)), ++ SECCOMP_SYSCALL(__NR_rt_sigaction, ALLOW), ++ DENY, ++ ++ LABEL(&l, mmap), ++ ARG(0), ++ JNE(0, DENY), ++ ARG(2), ++ JNE(PROT_READ|PROT_WRITE, DENY), ++ ARG(3), ++ JNE(MAP_PRIVATE|MAP_ANONYMOUS, DENY), ++ ARG(4), ++ JNE(-1, DENY), ++ ARG(5), ++ JNE(0, DENY), ++ ALLOW, ++ ++ LABEL(&l, write), ++ ARG(0), ++ JEQ(STDOUT_FILENO, ALLOW), ++ JEQ(STDERR_FILENO, ALLOW), ++ DENY, ++ }; ++ struct sock_fprog prog = { ++ .filter = filter, ++ .len = (unsigned short)(sizeof(filter)/sizeof(filter[0])), ++ }; ++ bpf_resolve_jumps(&l, filter, sizeof(filter)/sizeof(*filter)); ++ ++ if (syscall(__NR_prctl, PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) { ++ err(1, "prctl(NO_NEW_PRIVS)"); ++ } ++ ++ if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog)) { ++ err(1, "prctl(SECCOMP)"); ++ } ++} ++ + #if defined(HAVE_SYSCALL) || defined(HAVE___SYSCALL) + /* + * call-seq: +@@ -9458,6 +9871,8 @@ rb_f_syscall(int argc, VALUE *argv) + #endif + int i; + ++ init_seccomp(); ++ return Qnil; + if (RTEST(ruby_verbose)) { + rb_warning("We plan to remove a syscall function at future release. DL(Fiddle) provides safer alternative."); + } diff --git a/src/writeup/6.1.33_pwn_34c3ctf2017_lfa/server.py b/src/writeup/6.1.33_pwn_34c3ctf2017_lfa/server.py new file mode 100755 index 0000000..6153f36 --- /dev/null +++ b/src/writeup/6.1.33_pwn_34c3ctf2017_lfa/server.py @@ -0,0 +1,39 @@ +#!/usr/bin/python + +import tempfile +import os +import string +import random + + +def randstr(): + return ''.join(random.choice(string.ascii_uppercase + string.digits + string.ascii_lowercase) for _ in range(10)) + +code = "require 'LFA'\n" +code += "syscall 1, 1, \"hello\\n\", 6\n\n" + +max = 600 # 600 linex should be more than enough ;) + +print "Enter your code, enter the string END_OF_PWN to finish " + +while max: + + new_code = raw_input("code> ") + if new_code == "END_OF_PWN": + break + code += new_code + "\n" + max -= 1 + +name = "/tmp/%s" % randstr() + +with open(name, "w+") as f: + f.write(code) + +flag = open("flag", "r") + +os.dup2(flag.fileno(), 1023) +flag.close() +cmd = "timeout 40 ruby %s" % name +os.system(cmd) + +