解决方案1:C(Mac OS X x86_64),109字节
golf_sol1.c的来源
main[]={142510920,2336753547,3505849471,284148040,2370322315,2314740852,1351437506,1208291319,914962059,195};
上面的程序需要使用__DATA段上的执行访问权限进行编译。
clang golf_sol1.c -o golf_sol1 -Xlinker -segprot -Xlinker __DATA -Xlinker rwx -Xlinker rwx
然后,要执行该程序,请运行以下命令:
./golf_sol1 $(ruby -e 'puts "\xf5\xff\xff\xfe\xff\xff\x44\x82\x57\x7d\xff\x7f"')
结果:
不幸的是,Valgrind不会监视从系统调用分配的内存,因此我无法显示出很好的检测到的泄漏。
但是,我们可以查看vmmap来查看分配的内存(MALLOC元数据)的大块。
VIRTUAL REGION
REGION TYPE SIZE COUNT (non-coalesced)
=========== ======= =======
Kernel Alloc Once 4K 2
MALLOC guard page 16K 4
MALLOC metadata 16.2M 7
MALLOC_SMALL 8192K 2 see MALLOC ZONE table below
MALLOC_TINY 1024K 2 see MALLOC ZONE table below
STACK GUARD 56.0M 2
Stack 8192K 3
VM_ALLOCATE (reserved) 520K 3 reserved VM address space (unallocated)
__DATA 684K 42
__LINKEDIT 70.8M 4
__TEXT 5960K 44
shared memory 8K 3
=========== ======= =======
TOTAL 167.0M 106
TOTAL, minus reserved VM space 166.5M 106
说明
因此,我认为在介绍改进的解决方案之前,我需要描述一下这里实际发生的情况。
这个主要功能是滥用C的缺失类型声明(因此它默认为int,而无需我们浪费字符编写它),以及符号的工作方式。链接器仅关心是否可以找到main
要调用的符号。因此,在这里我们将main做成一个int数组,并使用将要执行的shellcode对其进行初始化。因此,不会将main添加到__TEXT段,而是添加到__DATA段,原因是我们需要使用可执行的__DATA段来编译程序。
在main中找到的shellcode如下:
movq 8(%rsi), %rdi
movl (%rdi), %eax
movq 4(%rdi), %rdi
notl %eax
shrq $16, %rdi
movl (%rdi), %edi
leaq -0x8(%rsp), %rsi
movl %eax, %edx
leaq -9(%rax), %r10
syscall
movq (%rsi), %rsi
movl %esi, (%rsi)
ret
这是在调用syscall函数来分配内存页面(syscall mach_vm_allocate在内部使用)。RAX应该等于0x100000a(告诉系统调用我们想要的功能),而RDI持有分配目标(在我们的情况下,我们希望它是mach_task_self()),RSI应该持有地址以将指针写入到新创建的内存中(因此,我们只是将其指向堆栈的某个部分),RDX保留分配的大小(我们只是传递RAX或0x100000a只是为了节省字节数),R10保留标志(我们表明它可以分配到任何地方)。
现在还不清楚RAX和RDI从何处获取价值。我们知道RAX必须为0x100000a,RDI必须为mach_task_self()返回的值。幸运的是,mach_task_self()实际上是变量(mach_task_self_)的宏,该变量每次都位于相同的内存地址(但是应在重新启动时更改)。在我的特定情况下,mach_task_self_恰好位于0x00007fff7d578244。因此,为了减少指令,我们将改为从argv传递此数据。这就是为什么我们使用此表达式运行程序$(ruby -e 'puts "\xf5\xff\xff\xfe\xff\xff\x44\x82\x57\x7d\xff\x7f"')
第一个论点。字符串是两个值的组合,其中RAX值(0x100000a)只有32位,并且对其应用了一个补码(因此没有空字节;我们没有获取原始值的值),下一个值是RDI(0x00007fff7d578244)已移至左侧,并在末尾添加了2个额外的垃圾字节(再次排除空字节,我们只是将其移回右侧以将其恢复为原始字节)。
进行系统调用后,我们正在写入新分配的内存。原因是因为使用mach_vm_allocate(或此syscall)分配的内存实际上是VM页面,并且不会自动分页到内存中。而是保留它们,直到将数据写入它们,然后将这些页面映射到内存中。如果只保留它,不确定是否满足要求。
对于下一个解决方案,我们将利用我们的shellcode没有空字节这一事实,因此可以将其移出程序代码之外以减小大小。
解决方案2:C(Mac OS X x86_64),44字节
golf_sol2.c的来源
main[]={141986632,10937,1032669184,2,42227};
上面的程序需要使用__DATA段上的执行访问权限进行编译。
clang golf_sol2.c -o golf_sol2 -Xlinker -segprot -Xlinker __DATA -Xlinker rwx -Xlinker rwx
然后,要执行该程序,请运行以下命令:
./golf_sol2 $(ruby -e 'puts "\xb8\xf5\xff\xff\xfe\xf7\xd0\x48\xbf\xff\xff\x44\x82\x57\x7d\xff\x7f\x48\xc1\xef\x10\x8b\x3f\x48\x8d\x74\x24\xf8\x89\xc2\x4c\x8d\x50\xf7\x0f\x05\x48\x8b\x36\x89\x36\xc3"')
结果应该与之前相同,因为我们正在分配相同的大小。
说明
遵循与解决方案1大致相同的概念,不同之处在于,我们已将泄漏的代码块移至程序外部。
现在在main中找到的shellcode如下:
movq 8(%rsi), %rsi
movl $42, %ecx
leaq 2(%rip), %rdi
rep movsb (%rsi), (%rdi)
这基本上是将我们传入argv的shellcode复制到此代码之后(因此,在复制完之后,它将运行插入的shellcode)。对我们有利的是__DATA段至少应为页面大小,因此即使我们的代码不是那么大,我们仍然可以“安全”地编写更多代码。缺点是这里的理想解决方案,甚至不需要复制,而是直接在argv中调用并执行shellcode。但不幸的是,该内存没有执行权限。我们可以更改此内存的权限,但是与复制内存相比,它需要更多的代码。另一种策略是从外部程序更改权利(但稍后会进行更多说明)。
我们传递给argv的shellcode如下:
movl $0xfefffff5, %eax
notl %eax
movq $0x7fff7d578244ffff, %rdi
shrq $16, %rdi
movl (%rdi), %edi
leaq -0x8(%rsp), %rsi
movl %eax, %edx
leaq -9(%rax), %r10
syscall
movq (%rsi), %rsi
movl %esi, (%rsi)
ret
这与我们之前的代码非常相似,唯一的区别是我们直接包含了EAX和RDI的值。
可能的解决方案1:C(Mac OS X x86_64),11字节
从外部修改程序的想法为我们提供了将泄漏程序移至外部程序的可能解决方案。我们的实际程序(提交)只是一个伪程序,而泄漏程序将在目标程序中分配一些内存。现在,我不确定这是否会属于此挑战的规则,但还是要分享它。
因此,如果我们在目标设置为挑战程序的外部程序中使用mach_vm_allocate,那可能意味着我们的挑战程序只需要遵循以下内容:
main=65259;
该shellcode只是对自身的短暂跳转(无限跳转/循环),因此程序保持打开状态,我们可以从外部程序中引用它。
可能的解决方案2:C(Mac OS X x86_64),8字节
有趣的是,当我查看valgrind输出时,至少看到valgrind认为dyld会泄漏内存。因此,每个程序实际上都在泄漏一些内存。在这种情况下,我们实际上可以制作一个不执行任何操作(仅退出)的程序,而该程序实际上会泄漏内存。
资源:
main(){}
==55263== LEAK SUMMARY:
==55263== definitely lost: 696 bytes in 17 blocks
==55263== indirectly lost: 17,722 bytes in 128 blocks
==55263== possibly lost: 0 bytes in 0 blocks
==55263== still reachable: 0 bytes in 0 blocks
==55263== suppressed: 16,316 bytes in 272 blocks